การสร้างและเรียกใช้ 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);
}

โดยพื้นฐานแล้ว ข้อมูลโค้ดด้านบนจะสร้างโค้ดและประเมิน อย่างไรก็ตาม มีการปรับแต่ง 2 แบบ การปรับเกณฑ์การค้นหาอย่างหนึ่งคือ 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 คือหมายเลขซีเรียลของบล็อก ไฮไลต์ไว้

วนซ้ำไม่สิ้นสุด

แม้ว่าโค้ดที่ได้จะรับประกันความถูกต้องทางไวยากรณ์เลย อาจมีการวนรอบไปเรื่อยๆ ได้ไม่รู้จบ ตั้งแต่การแก้โจทย์ การแก้ไขปัญหานั้นไม่ใช่แค่ ขอบเขต (!) ของ 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

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

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

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

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

ดาวน์โหลดไฟล์ ZIP ดาวน์โหลด TAR Ball ดูใน GitHub

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

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

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

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

ก้าวเข้าสู่ล่าม

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

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

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

เพิ่ม API

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

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

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