值區塊會對應至運算式。當您將值區塊用作內部區塊時,可能需要在區塊的程式碼中使用該區塊產生的運算式多次。舉例來說,取得清單最後一個元素的區塊會使用兩次建立清單的運算式。
// 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]
為避免這個問題,程式碼應執行內部區塊的程式碼一次。方法有以下兩種:
臨時變數:在臨時變數中快取內部區塊程式碼的評估結果,並改用臨時變數。只有在區塊為陳述式區塊時,才能使用此方法。
公用函式:建立函式來執行您需要執行的工作,並將評估內部區塊程式碼的結果,做為引數傳遞給這個函式。您可以對值和陳述式區塊使用此方法。
暫時變數
暫存變數會儲存內部區塊的程式碼字串值,因此程式碼只會評估一次,之後這個值就可以多次參照。
由於值區塊必須傳回單一行程式碼,因此無法在值區塊中使用暫時變數。請改用公用函式。
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
,而傳回的元組則變更為 Order.FUNCTION_CALL
。