แคชโค้ดบล็อกค่าภายใน

บล็อกค่าสอดคล้องกับนิพจน์ เมื่อใช้บล็อกค่าเป็นบล็อกภายใน คุณอาจต้องใช้นิพจน์ที่บล็อกสร้างขึ้นมากกว่า 1 ครั้งในโค้ดของบล็อก เช่น บล็อกที่รับองค์ประกอบสุดท้ายของลิสต์ใช้นิพจน์ที่สร้างลิสต์ 2 ครั้ง

// Incorrect block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
  // Get the expression that creates the list.
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);

  // listCode is used twice.
  const code = `${listCode}[${listCode}.length - 1]`;

  return [code, Order.MEMBER];
}

ซึ่งจะทำให้เกิดปัญหาหากโค้ดของบล็อกด้านในสร้างค่าที่แตกต่างกันทุกครั้งที่เรียกใช้ หรือหากโค้ดมีผลข้างเคียง เช่น หากโค้ดของบล็อกด้านในคือการเรียกใช้ฟังก์ชัน โค้ดนี้อาจทำให้เกิดเงื่อนไข "อยู่นอกช่วง"

randomList()[randomList().length - 1]

เพื่อป้องกันปัญหานี้ โค้ดของคุณควรเรียกใช้โค้ดของบล็อกด้านในเพียงครั้งเดียว ทำได้ 2 วิธีดังนี้

  • ตัวแปรชั่วคราว: แคชผลลัพธ์ของการประเมินโค้ดของบล็อกด้านในไว้ในตัวแปรชั่วคราว และใช้ตัวแปรชั่วคราวแทน คุณจะใช้วิธีการนี้ได้ก็ต่อเมื่อบล็อกของคุณเป็นบล็อกคำสั่ง

  • ฟังก์ชันยูทิลิตี: สร้างฟังก์ชันที่จะทํางานที่คุณต้องทำและส่งผลลัพธ์ของการประเมินโค้ดของบล็อกด้านในเป็นอาร์กิวเมนต์ไปยังฟังก์ชันนี้ คุณใช้เมธอดนี้ได้กับทั้งบล็อกค่าและบล็อกคำสั่ง

ตัวแปรชั่วคราว

ตัวแปรชั่วคราวจะจัดเก็บค่าของสตริงโค้ดของบล็อกภายในเพื่อให้ระบบประเมินโค้ดเพียงครั้งเดียว จากนั้นจึงอ้างอิงค่าได้หลายครั้ง

คุณใช้ตัวแปรชั่วคราวในบล็อกค่าไม่ได้ เนื่องจากบล็อกค่าต้องแสดงผลโค้ดเพียงบรรทัดเดียว ให้ใช้ฟังก์ชันยูทิลิตีแทน

import {javascriptGenerator, Order} from 'blockly/javascript';

// Correct block-code generator for a statement block that prints the last element of a list.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
  // Get the expression that creates the list.
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
  // Get the name of a temporary variable.
  const listVar = generator.nameDB_.getDistinctName(
      'temp_list', Blockly.names.NameType.VARIABLE);

  // Evaluate listCode once and assign the result to the temporary variable.
  const code = `var ${listVar} = ${listCode};\n`;
  // Print the last element of the list.
  code += `print(${listVar}[${listVar}.length - 1]);\n`;
  return code;
}

เช่น หากโค้ดของบล็อกด้านในคือการเรียกใช้ฟังก์ชัน randomList() โค้ดที่สร้างขึ้นจะเป็นดังนี้

var temp_list = randomList();
print(temp_list[temp_list.length - 1]);

การเรียกใช้ getDistinctName จะรับชื่อตัวแปรที่ต้องการและแสดงผลชื่อที่ไม่ทับซ้อนกับตัวแปรที่ผู้ใช้กําหนด

ลดโค้ดที่ซ้ำซ้อน

ข้อเสียของตัวแปรชั่วคราวคือหากโค้ดของบล็อกด้านในเป็นค่า ไม่ใช่ฟังก์ชันหรือนิพจน์ คุณจะได้รับโค้ดที่ซ้ำซ้อน

// Assigning to temp_list is unnecessary.
var temp_list = foo;
print(temp_list[temp_list.length - 1]);

หากต้องการให้โค้ดสะอาดขึ้น คุณสามารถตรวจสอบว่าโค้ดของบล็อกด้านในเป็นค่าหรือไม่ และรวมเฉพาะตัวแปรชั่วคราวหากไม่ใช่

if (listCode.match(/^\w+$/)) {
  const code = `print(${listCode}[${listCode}.length - 1]);\n`;
} else {
  const listVar = generator.nameDB_.getDistinctName(
      'temp_list', Blockly.names.NameType.VARIABLE);
  const code = `var ${listVar} = ${listCode};\n`;
  code += `print(${listVar}[${listVar}.length - 1]);\n`;
}

ฟังก์ชันยูทิลิตี

ฟังก์ชันยูทิลิตีคือฟังก์ชันที่นักพัฒนาแอปกำหนดซึ่งรวมอยู่ในสตริงโค้ดที่สร้างขึ้น คุณสามารถใช้เพื่อตรวจสอบว่าโค้ดบล็อกภายในได้รับการประเมินเพียงครั้งเดียว จากนั้นจึงอ้างอิงค่าได้หลายครั้ง

ฟังก์ชันยูทิลิตีใช้ได้ในบล็อกค่าและบล็อกคำสั่ง อย่างไรก็ตาม โดยทั่วไปแล้วบล็อกคำสั่งควรใช้ตัวแปรชั่วคราว ซึ่งมักจะอ่านได้ง่ายกว่า

import {javascriptGenerator, Order} from 'blockly/javascript';

// Correct block-code generator for a value block that gets the last element of a list.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
  // Get the expression that creates the list.
  const listCode = generator.valueToCode(block, 'LIST', Order.NONE);
  // Create a function that accepts a list and returns its last element. The
  // language generator adds this function to your code.
  const functionName = generator.provideFunction_(
      'list_lastElement',
      [
        `function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) {`,
        `  return list[list.length - 1];`,
        `}`
      ]
  );

  // Create an expression that calls the function with listCode as its argument.
  // This evaluates listCode once and passes the resulting list to the function.
  const code = `${functionName}(${listCode})`;
  return [code, Order.FUNCTION_CALL];
}

เช่น หากโค้ดของบล็อกด้านในคือการเรียกใช้ฟังก์ชัน randomList() โค้ดที่สร้างขึ้นจะเป็นดังนี้

// This code is added to the overall code returned by the code generator.
function list_lastElement(list) {
  return list[list.length - 1];
}

// This code is returned by your inner block.
list_lastElement(randomList());

ระบุฟังก์ชัน

คุณกำหนดฟังก์ชันยูทิลิตีภายในเครื่องมือสร้างโค้ดบล็อกได้โดยใช้ provideFunction_ โดยรับชื่อที่ต้องการสำหรับฟังก์ชันยูทิลิตี และอาร์เรย์สตริงโค้ดที่มีคำจำกัดความของฟังก์ชัน ซึ่งจะแสดงผลชื่อที่เป็นผลลัพธ์ของฟังก์ชันยูทิลิตีหลังจาก (อาจ) แก้ไขเพื่อไม่ให้ทับซ้อนกับฟังก์ชันที่ผู้ใช้กำหนด

provideFunction_ จะกรองฟังก์ชันยูทิลิตีที่ซ้ำกันออกด้วย เพื่อให้ฟังก์ชันยูทิลิตีแต่ละรายการมีเพียงรายการเดียว แม้ว่าบล็อกประเภทที่กําหนดฟังก์ชันจะมีอยู่หลายรายการก็ตาม

อัปเดตลําดับความสําคัญ

เมื่อคุณกำหนดฟังก์ชันยูทิลิตี คุณควรอัปเดตลําดับความสําคัญ (ซึ่งกําหนดวิธีแทรกวงเล็บ) ที่รวมอยู่ในเครื่องมือสร้างโค้ดบล็อกด้วย

ลําดับความสําคัญจะอิงตามสตริงโค้ดที่บล็อกโค้ดเจนเนอเรเตอร์แสดงผลเสมอ โดยไม่สนใจโอเปอเรเตอร์ภายในฟังก์ชันยูทิลิตี ดังนั้นในตัวอย่างก่อนหน้า การเรียกใช้ valueToCode จึงเปลี่ยนเป็น Order.NONE และเปลี่ยน tupple ผลลัพธ์เป็น Order.FUNCTION_CALL