สร้างและเรียกใช้ JavaScript

แอปพลิเคชัน Blockly มักจะสร้าง JavaScript เป็นภาษาเอาต์พุต โดยทั่วไปเพื่อเรียกใช้ภายในหน้าเว็บ (อาจเป็นหน้าเดียวกันหรือ WebView ที่ฝังอยู่) เช่นเดียวกับเครื่องมือสร้างอื่นๆ ขั้นตอนแรกคือการรวมเครื่องมือสร้าง JavaScript

import {javascriptGenerator} from 'blockly/javascript';

หากต้องการสร้าง JavaScript จากพื้นที่ทำงาน ให้เรียกใช้

javascriptGenerator.addReservedWords('code');
var code = javascriptGenerator.workspaceToCode(workspace);

คุณสามารถเรียกใช้โค้ดที่ได้ในหน้าเว็บปลายทางได้โดยตรง

try {
  eval(code);
} catch (e) {
  alert(e);
}

โดยพื้นฐานแล้ว ข้อมูลโค้ดข้างต้นจะสร้างโค้ดและประเมินโค้ดนั้น อย่างไรก็ตาม มีการปรับแต่งเล็กน้อย การปรับแต่งอย่างหนึ่งคือการห่อหุ้ม eval ไว้ใน try/catch เพื่อให้มองเห็นข้อผิดพลาดรันไทม์แทนที่จะล้มเหลว อย่างเงียบๆ การปรับปรุงอีกอย่างคือมีการเพิ่ม code ลงในรายการคำที่สงวนไว้ เพื่อให้หากโค้ดของผู้ใช้มีตัวแปรชื่อดังกล่าว ระบบจะเปลี่ยนชื่อตัวแปรนั้นโดยอัตโนมัติแทนที่จะเกิดการขัดกัน ตัวแปรในเครื่องควร สงวนไว้ในลักษณะนี้

บล็อกไฮไลต์

การไฮไลต์บล็อกที่กำลังดำเนินการขณะที่โค้ดทำงานจะช่วยให้ผู้ใช้เข้าใจลักษณะการทำงานของโปรแกรมได้ การไฮไลต์อาจทำได้ในระดับคำสั่งโดยคำสั่งโดยการตั้งค่า STATEMENT_PREFIX ก่อนที่จะสร้างโค้ด JavaScript

javascriptGenerator.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
javascriptGenerator.addReservedWords('highlightBlock');

กำหนด highlightBlock เพื่อทำเครื่องหมายบล็อกในพื้นที่ทำงาน

function highlightBlock(id) {
  workspace.highlightBlock(id);
}

ซึ่งจะส่งผลให้มีการเพิ่มข้อความ highlightBlock('123'); ไว้ก่อน ทุกข้อความ โดยที่ 123 คือหมายเลขซีเรียลของบล็อกที่จะ ไฮไลต์

Infinite Loops

แม้ว่าโค้ดที่ได้จะรับประกันว่าถูกต้องตามไวยากรณ์ตลอดเวลา แต่ก็อาจมีลูปที่ไม่มีที่สิ้นสุด เนื่องจากการแก้ปัญหาการหยุดทำงานอยู่นอกเหนือขอบเขตของ Blockly (!) วิธีที่ดีที่สุดในการจัดการกับกรณีเหล่านี้คือการ รักษามูลค่าตัวนับและลดค่าทุกครั้งที่มีการทำซ้ำ หากต้องการดำเนินการนี้ ให้ตั้งค่า javascriptGenerator.INFINITE_LOOP_TRAP เป็นโค้ด ข้อมูลโค้ดที่จะแทรกลงในลูปและฟังก์ชันทั้งหมด ตัวอย่างมีดังนี้

window.LoopTrap = 1000;
javascriptGenerator.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = javascriptGenerator.workspaceToCode(workspace);

ตัวอย่าง

นี่คือการสาธิต แบบเรียลไทม์ของ การสร้างและเรียกใช้ JavaScript

JS-Interpreter

หากคุณต้องการให้การบล็อกของผู้ใช้ทำงานอย่างถูกต้อง โปรเจ็กต์ JS-Interpreter คือตัวเลือกที่เหมาะสม โปรเจ็กต์นี้แยกจาก Blockly แต่เขียนขึ้นมาสำหรับ Blockly โดยเฉพาะ

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

เรียกใช้ล่าม

ก่อนอื่น ให้ดาวน์โหลด JS-Interpreter จาก GitHub โดยทำดังนี้

จากนั้นเพิ่มลงในหน้าเว็บโดยทำดังนี้

<script src="acorn_interpreter.js"></script>

วิธีที่ง่ายที่สุดในการเรียกใช้คือการสร้าง JavaScript, สร้าง อินเทอร์พรีเตอร์ และเรียกใช้โค้ด

var code = javascriptGenerator.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();

Step the Interpreter

หากต้องการเรียกใช้โค้ดให้ช้าลงหรือในลักษณะที่มีการควบคุมมากขึ้น ให้แทนที่การเรียกใช้ run ด้วยลูปที่ก้าว (ในกรณีนี้คือ 1 ก้าวทุกๆ 10 มิลลิวินาที) ดังนี้

function nextStep() {
  if (myInterpreter.step()) {
    setTimeout(nextStep, 10);
  }
}
nextStep();

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

เพิ่ม API

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

function initApi(interpreter, globalObject) {
  // Add an API function for the alert() block.
  var wrapper = function(text) {
    return alert(arguments.length ? text : '');
  };
  interpreter.setProperty(globalObject, 'alert',
      interpreter.createNativeFunction(wrapper));

  // Add an API function for the prompt() block.
  wrapper = function(text) {
    return prompt(text);
  };
  interpreter.setProperty(globalObject, 'prompt',
      interpreter.createNativeFunction(wrapper));
}

จากนั้นแก้ไขการเริ่มต้นตัวแปลเพื่อส่งฟังก์ชัน initApi ดังนี้

var myInterpreter = new Interpreter(code, initApi);

บล็อกการแจ้งเตือนและบล็อกข้อความแจ้งเป็น 2 บล็อกเดียวในชุดบล็อกเริ่มต้น ที่ต้องใช้ API ที่กำหนดเองสำหรับตัวแปล

กำลังเชื่อมต่อกับ highlightBlock()

เมื่อเรียกใช้ใน JS-Interpreter highlightBlock() ควรดำเนินการ ทันทีนอกแซนด์บ็อกซ์ ขณะที่ผู้ใช้ทำตามขั้นตอนในโปรแกรม หากต้องการทำเช่นนี้ ให้สร้างฟังก์ชัน Wrapper highlightBlock() เพื่อบันทึกอาร์กิวเมนต์ของฟังก์ชัน และลงทะเบียนเป็นฟังก์ชันดั้งเดิม

function initApi(interpreter, globalObject) {
  // Add an API function for highlighting blocks.
  var wrapper = function(id) {
    return workspace.highlightBlock(id);
  };
  interpreter.setProperty(globalObject, 'highlightBlock',
      interpreter.createNativeFunction(wrapper));
}

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

ตัวอย่าง JS-Interpreter

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