ステートメント ブロックによる引数のキャッシュ保存

場合によっては、ブロックコード ジェネレータは内部のコードを参照し、 複数回ブロックされます。

たとえば、リストの最後の要素を出力するブロックがある場合、 リストコードに複数回アクセスする必要がある場合:

// Incorrect block-code generator.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);

  // listCode gets referenced twice.
  return `print(${listCode}[${listCode}.length - 1]);\n`;
}

ただし、内部ブロックのコードの結果値が 副作用が生じますたとえば、内部コードが実際に 関数呼び出しを行うと、この特定のコードは範囲外の条件になる可能性があります。

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

一時変数に代入すると、内部ブロックのコードが 評価するのは 1 回だけです。

一時変数

一時変数には、内部ブロックのコード文字列の値が格納されるため、 コードが評価されるのは 1 回だけで、値は複数回参照可能 あります。

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

// Correct block-code generator.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
  const listVar = generator.nameDB_.getDistinctName(
      'temp_list', Blockly.names.NameType.VARIABLE);

  // listCode only gets evaluated once.
  const code = `var ${listVar} = ${listCode};\n`;
  return `print(${listVar}[${listVar}.length - 1]);\n`;
}

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`;
}