บล็อกค่าสอดคล้องกับนิพจน์ เมื่อใช้บล็อกค่าเป็นบล็อกภายใน คุณอาจต้องใช้นิพจน์ที่บล็อกสร้างขึ้นมากกว่า 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