ブロックコード ジェネレータでは、内部ブロックのコードを複数回参照しなければならない場合があります。
たとえば、リストの最後の要素を取得するブロックがある場合、リストコードに複数回アクセスする必要があります。
// Incorrect block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
// listCode gets referenced twice.
const code = `${listCode}[${listCode}.length - 1]`;
return [code, Order.MEMBER];
}
ただし、内部ブロックのコードの結果の値が一致しない場合や、副作用が生じる場合は、問題が発生する可能性があります。たとえば、内部コードが実際には関数呼び出しである場合、次の特定のコードは範囲外の状態になる可能性があります。
randomList()[randomList().length - 1]
ユーティリティ関数を使用すると、内部ブロックのコードを 1 回だけ評価できます。
ユーティリティ関数
ユーティリティ関数は、生成されたコード文字列の一部として含まれる、デベロッパー定義の関数です。これを使用して、内部ブロックのコードが 1 回だけ評価され、その後その値が複数回参照されるようにできます。
import {javascriptGenerator, Order} from 'blockly/javascript';
// Correct block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
const listCode = generator.valueToCode(block, 'LIST', Order.NONE);
const functionName = generator.provideFunction_(
'list_lastElement',
[
`function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) {`,
` return list[list.length - 1];`,
`}`
]
);
// listCode only gets evaluated once.
const code = `${functionName}(${listCode})`;
return [code, Order.FUNCTION_CALL];
}
関数を指定する
provideFunction_
を使用して、ブロックコード ジェネレータ内でユーティリティ関数を定義できます。この関数は、ユーティリティ関数に付ける名前と、関数の機能を定義するコード文字列の配列を受け取ります。ユーザー定義関数と競合しないように(場合によっては)ユーティリティ関数を変更した後、その結果としてそのユーティリティ関数の名前が返されます。
また、provideFunction_
はユーティリティ関数の定義を重複させるので、各ユーティリティ関数は、それを定義するブロックタイプが複数回存在しても、存在するのは 1 回だけです。
優先度を更新する
ユーティリティ関数を定義するときは、ブロックコード ジェネレータに含まれる優先順位(かっこの挿入方法を定義する)も更新する必要があります。
優先度は常に、ブロックコード ジェネレータが返すコード文字列に基づきます。ユーティリティ関数内の演算子は考慮されません。そのため、前の例では、valueToCode
呼び出しが Order.NONE
に変更され、戻りタプルが Order.FUNCTION_CALL
に変更されました。