การสืบทอดต้นแบบ
ข้อมูลพื้นฐานแต่ละประเภทมีการยกเว้น ยกเว้น null
และ undefined
ต้นแบบ ซึ่งเป็น Wrapper ของออบเจ็กต์ที่สอดคล้องกันซึ่งมีวิธีในการทำงาน
เมื่อมีการเรียกใช้การค้นหาเมธอดหรือพร็อพเพอร์ตี้ในแบบพื้นฐาน
JavaScript จะรวมการทำงานพื้นฐานในเบื้องหลังและเรียกเมธอดหรือ
ระบบจะค้นหาพร็อพเพอร์ตี้ในออบเจ็กต์ Wrapper แทน
ตัวอย่างเช่น สตริงลิเทอรัลไม่มีเมธอดของตัวเอง แต่คุณสามารถเรียกเมธอด
.toUpperCase()
ในเมธอดด้วยออบเจ็กต์ String
ที่เกี่ยวข้อง
Wrapper:
"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL
วิธีนี้เรียกว่าการสืบทอดต้นแบบ ซึ่งเป็นการรับช่วงคุณสมบัติและเมธอด จากตัวสร้างที่เกี่ยวข้องของค่า
Number.prototype
> Number { 0 }
> constructor: function Number()
> toExponential: function toExponential()
> toFixed: function toFixed()
> toLocaleString: function toLocaleString()
> toPrecision: function toPrecision()
> toString: function toString()
> valueOf: function valueOf()
> <prototype>: Object { … }
คุณสามารถสร้างวัตถุพื้นฐานโดยใช้ตัวสร้างเหล่านี้ แทนที่จะกำหนด
ตามมูลค่าของพวกเขา ตัวอย่างเช่น การใช้ตัวสร้าง String
จะสร้าง
ออบเจ็กต์สตริง ไม่ใช่ลิเทอรัลสตริง: ออบเจ็กต์ที่ไม่เพียงมีสตริงของเราเท่านั้น
แต่พร็อพเพอร์ตี้และวิธีที่ได้รับช่วงมาจากเครื่องมือสร้างนั้นทั้งหมด
const myString = new String( "I'm a string." );
myString;
> String { "I'm a string." }
typeof myString;
> "object"
myString.valueOf();
> "I'm a string."
โดยส่วนใหญ่แล้ว ออบเจ็กต์ที่ได้จะทำงานเป็นค่าที่เราเคยใช้
กำหนดได้ ตัวอย่างเช่น แม้ว่าการกำหนดค่าตัวเลขโดยใช้แอตทริบิวต์
ตัวสร้าง new Number
ให้ผลลัพธ์เป็นออบเจ็กต์ที่มีเมธอดและ
ของต้นแบบ Number
คุณสามารถใช้โอเปอเรเตอร์ทางคณิตศาสตร์
วัตถุเหล่านั้นเช่นเดียวกับที่คุณดำเนินการกับลิเทอรัลจำนวน
const numberOne = new Number(1);
const numberTwo = new Number(2);
numberOne;
> Number { 1 }
typeof numberOne;
> "object"
numberTwo;
> Number { 2 }
typeof numberTwo;
> "object"
numberOne + numberTwo;
> 3
คุณแทบจะไม่ต้องใช้ตัวสร้างเหล่านี้เลย เนื่องจาก JavaScript ในตัว การสืบทอดต้นแบบหมายความว่าพวกมันไม่ได้ให้ประโยชน์ในเชิงปฏิบัติ กำลังสร้าง เบื้องต้นที่ใช้ตัวสร้างอาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิดเช่นกัน เนื่องจาก ผลการค้นหาเป็นออบเจ็กต์ ไม่ใช่ลิเทอรัลง่ายๆ
let stringLiteral = "String literal."
typeof stringLiteral;
> "string"
let stringObject = new String( "String object." );
stringObject
> "object"
ซึ่งอาจทําให้ใช้โอเปอเรเตอร์การเปรียบเทียบที่เข้มงวดได้ยาก ดังนี้
const myStringLiteral = "My string";
const myStringObject = new String( "My string" );
myStringLiteral === "My string";
> true
myStringObject === "My string";
> false
การแทรกเซมิโคลอนอัตโนมัติ (ASI)
ขณะแยกวิเคราะห์สคริปต์ ล่าม JavaScript สามารถใช้ฟีเจอร์ที่เรียกว่า การแทรกเซมิโคลอนอัตโนมัติ (ASI) เพื่อพยายามแก้ไขอินสแตนซ์ที่ละเลย เครื่องหมายเซมิโคลอน หากโปรแกรมแยกวิเคราะห์ JavaScript พบโทเค็นที่ไม่ได้รับอนุญาต พยายามเพิ่มเครื่องหมายเซมิโคลอนก่อนโทเค็นนั้นเพื่อแก้ไขข้อผิดพลาดทางไวยากรณ์ที่เป็นไปได้ หากเงื่อนไขต่อไปนี้เป็นจริงอย่างน้อย 1 เงื่อนไข
- โทเค็นดังกล่าวจะแยกออกจากโทเค็นก่อนหน้าด้วยการขึ้นบรรทัดใหม่
- โทเค็นนั้นคือ
}
- โทเค็นก่อนหน้าคือ
)
และเครื่องหมายเซมิโคลอนที่แทรกไว้จะเป็นส่วนท้าย เครื่องหมายเซมิโคลอนของคำสั่งdo
...while
ดูข้อมูลเพิ่มเติมได้ที่กฎของ ASI
ตัวอย่างเช่น การละเครื่องหมายเซมิโคลอนหลังคำสั่งต่อไปนี้จะไม่ทำให้เกิด ข้อผิดพลาดทางไวยากรณ์เนื่องจาก ASI:
const myVariable = 2
myVariable + 3
> 5
อย่างไรก็ตาม ASI ไม่สามารถระบุใบแจ้งยอดหลายรายการในบรรทัดเดียวกันได้ หากคุณ เขียนมากกว่าหนึ่งข้อความในบรรทัดเดียวกัน อย่าลืมแยกข้อความกับ เครื่องหมายเซมิโคลอน:
const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier
const myVariable = 2; myVariable + 3;
> 5
ASI เป็นการแก้ไขข้อผิดพลาด ไม่ใช่ความยืดหยุ่นเชิงไวยากรณ์ที่สร้างขึ้น ลงใน JavaScript ตรวจสอบว่าใช้เครื่องหมายเซมิโคลอนตามความเหมาะสมเพื่อไม่ต้องพึ่ง เพื่อสร้างโค้ดที่ถูกต้อง
โหมดจำกัด
มาตรฐานที่ควบคุมวิธีเขียน JavaScript นั้นได้พัฒนาไปไกลกว่า อะไรก็ได้ที่นำมาพิจารณาระหว่างการออกแบบภาษา ในช่วงแรก ทุกการเปลี่ยนแปลงใหม่สำหรับ ลักษณะการทำงานที่คาดไว้ของ JavaScript ต้องหลีกเลี่ยงการทําให้เกิดข้อผิดพลาดในเว็บไซต์เก่า
ES5 แก้ปัญหาที่มีมาอย่างยาวนานเกี่ยวกับอรรถศาสตร์ JavaScript โดยไม่มี
ที่ไม่ส่งผลกับการนำไปใช้งาน โดยการนำ "โหมดเข้มงวด" วิธีเลือกใช้
ลงในชุดกฎภาษาที่เข้มงวดขึ้นสำหรับสคริปต์ทั้งสคริปต์หรือ
แต่ละฟังก์ชัน หากต้องการเปิดใช้โหมดจำกัด ให้ใช้สัญพจน์ของสตริง
"use strict"
ตามด้วยเซมิโคลอนในบรรทัดแรกของสคริปต์ หรือ
ฟังก์ชัน:
"use strict";
function myFunction() {
"use strict";
}
โหมดเข้มงวดช่วยป้องกัน "ไม่ปลอดภัย" บางอย่าง การดำเนินการหรือฟีเจอร์ที่เลิกใช้งานแล้ว
ข้อผิดพลาดที่ชัดแจ้งแทนข้อความ "ปิดเสียง" ที่พบบ่อย และห้ามมิให้ใช้
ที่อาจขัดแย้งกับคุณลักษณะทางภาษาในอนาคต เช่น ในช่วงแรก
การตัดสินใจออกแบบเกี่ยวกับขอบเขตตัวแปร
ทำให้นักพัฒนาซอฟต์แวร์มีแนวโน้ม
ที่จะ "ก่อให้เกิดมลพิษ" โดยไม่ได้ตั้งใจ ขอบเขตรวมทั้งหมดเมื่อ
การประกาศตัวแปรโดยไม่คำนึงถึงบริบทที่มีอยู่โดยการละเว้น
คีย์เวิร์ด var
รายการ:
(function() {
mySloppyGlobal = true;
}());
mySloppyGlobal;
> true
รันไทม์ของ JavaScript สมัยใหม่ไม่สามารถแก้ไขลักษณะการทำงานนี้โดยปราศจากความเสี่ยง ทำให้เว็บไซต์ที่จำเป็นต้องใช้เว็บไซต์ดังกล่าวโดยไม่ได้ตั้งใจหรือจงใจ แต่ JavaScript สมัยใหม่จะป้องกันโดยให้นักพัฒนาซอฟต์แวร์เลือกใช้ สำหรับงานใหม่ และเปิดใช้ โหมดเข้มงวดโดยค่าเริ่มต้น เฉพาะในบริบทของ ฟีเจอร์ใหม่ๆ ในภาษาที่จะไม่ละเมิดการติดตั้งใช้งานเดิม ได้แก่
(function() {
"use strict";
mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal
คุณต้องเขียน "use strict"
เป็น
สตริงลิเทอรัล
ลิเทอรัลเทมเพลต
(use strict
) จะไม่ทำงาน คุณต้องใส่ "use strict"
ก่อน
โค้ดสั่งการในบริบทที่กำหนด มิฉะนั้น ล่ามจะไม่สนใจ
(function() {
"use strict";
let myVariable = "String.";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal
(function() {
let myVariable = "String.";
"use strict";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope
ตามการอ้างอิง, ตามค่า
ตัวแปรใดก็ได้ รวมถึงคุณสมบัติของออบเจ็กต์ พารามิเตอร์ฟังก์ชัน และองค์ประกอบในองค์ประกอบ array set หรือ แผนที่ อาจมีประเภท หรือค่าอ้างอิง
เมื่อกำหนดค่าพื้นฐานจากตัวแปรหนึ่งไปเป็นอีกตัวแปรหนึ่ง JavaScript Engine จะสร้างสำเนาของค่านั้นและกำหนดให้กับตัวแปร
เมื่อคุณกำหนดออบเจ็กต์ (อินสแตนซ์คลาส อาร์เรย์ และฟังก์ชัน) ให้กับ แทนที่จะสร้างสำเนาใหม่ของออบเจ็กต์นั้น ตัวแปรจะมี อ้างอิงถึงตำแหน่งที่จัดเก็บของออบเจ็กต์ในหน่วยความจำ ด้วยเหตุนี้ การเปลี่ยนแปลง ออบเจ็กต์ที่ตัวแปรอ้างอิงจะเปลี่ยนออบเจ็กต์ที่อ้างอิง ไม่ใช่แค่ ค่าที่ตัวแปรนั้นอยู่ภายใน เช่น หากคุณกำหนดค่า พร้อมกับตัวแปรที่มีการอ้างอิงออบเจ็กต์ แล้วใช้ตัวแปร ตัวแปรเพื่อเพิ่มพร็อพเพอร์ตี้ลงในออบเจ็กต์นั้น ระบบจะเพิ่มพร็อพเพอร์ตี้และค่าของพร็อพเพอร์ตี้นั้น กับออบเจ็กต์ต้นฉบับ
const myObject = {};
const myObjectReference = myObject;
myObjectReference.myProperty = true;
myObject;
> Object { myProperty: true }
ขั้นตอนนี้ไม่เพียงมีความสำคัญสำหรับการปรับเปลี่ยนออบเจ็กต์เท่านั้น แต่ยังรวมถึงการดำเนินการที่เข้มงวด
เนื่องจากความเท่าเทียมกันอย่างเคร่งครัดระหว่างออบเจ็กต์จะทำให้ตัวแปรทั้ง 2 ตัว
อ้างอิงออบเจ็กต์เดียวกันเพื่อประเมินกับ true
จะอ้างอิงไม่ได้
วัตถุต่างๆ แม้ว่าวัตถุเหล่านั้นจะมีโครงสร้างเหมือนกันก็ตาม:
const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};
myObject === myNewObject;
> false
myObject === myReferencedObject;
> true
การจัดสรรหน่วยความจำ
JavaScript ใช้การจัดการหน่วยความจำอัตโนมัติ ซึ่งหมายความว่าหน่วยความจำไม่จำเป็นต้อง ต้องมีการจัดสรรหรือดีลอย่างชัดแจ้งในระหว่างการพัฒนา ขณะที่ รายละเอียดของเครื่องมือ JavaScript ในการจัดการหน่วยความจำนั้นก็มากกว่าแค่ ขอบเขตของโมดูลนี้ การทำความเข้าใจวิธีจัดสรรหน่วยความจำ มีประโยชน์ บริบทสำหรับการทำงานกับค่าอ้างอิง
มี 2 "พื้นที่" ในความทรงจำ: "กองซ้อน" และ "ฮีป" สแต็กสโตร์ ข้อมูลแบบคงที่ ซึ่งก็คือค่าพื้นฐานและการอ้างอิงไปยังออบเจ็กต์ เนื่องจาก สามารถจัดสรรพื้นที่ที่แน่นอนซึ่งจำเป็นต้องใช้ในการจัดเก็บข้อมูลนี้ก่อน สคริปต์จะทำงาน ฮีปจัดเก็บออบเจ็กต์ซึ่งต้องใช้พื้นที่ที่จัดสรรแบบไดนามิก เนื่องจากขนาดอาจเปลี่ยนแปลงในระหว่างการดำเนินการ เพิ่มหน่วยความจำโดยกระบวนการ ที่เรียกว่า "เก็บขยะ" ซึ่งจะลบวัตถุที่ไม่มีการอ้างอิงจาก ความทรงจำ
เทรดหลัก
JavaScript คือภาษาหลักแบบเทรดเดี่ยวที่มีฟังก์ชัน "ซิงโครนัส" รูปแบบการดำเนินการ ซึ่งหมายความว่าจะสามารถเรียกใช้งานได้เพียงงานเดียวเท่านั้น ต่อครั้ง บริบทการดำเนินการตามลำดับนี้เรียกว่าเทรดหลัก
งานอื่นๆ ของเบราว์เซอร์จะแชร์เทรดหลัก เช่น การแยกวิเคราะห์ HTML การแสดงผลและ การแสดง ซ้ำของส่วนต่างๆ ของหน้าเว็บ การเรียกใช้ภาพเคลื่อนไหว CSS และ จัดการการโต้ตอบของผู้ใช้ตั้งแต่แบบทั่วไป (เช่น การไฮไลต์ข้อความ) ไปจนถึง ที่ซับซ้อน (เช่น การโต้ตอบกับองค์ประกอบแบบฟอร์ม) ผู้ให้บริการเบราว์เซอร์พบ วิธีเพิ่มประสิทธิภาพของงานที่ดำเนินการโดยเทรดหลัก แต่มีความซับซ้อนกว่า สคริปต์ยังคงใช้ทรัพยากรของเทรดหลักมากเกินไปและส่งผลกระทบโดยรวม ประสิทธิภาพของหน้าเว็บ
งานบางอย่างสามารถดำเนินการได้ใน ชุดข้อความพื้นหลังที่เรียกว่าผู้ปฏิบัติงานบนเว็บ โดยมีข้อจำกัดบางประการดังนี้
- ชุดข้อความของผู้ปฏิบัติงานจะดำเนินการกับไฟล์ JavaScript แบบสแตนด์อโลนได้เท่านั้น
- บุคคลเหล่านี้ได้ลดสิทธิ์เข้าถึงหน้าต่างเบราว์เซอร์และ UI ลงอย่างมากหรือไม่สามารถเข้าถึงได้เลย
- การสื่อสารกับเทรดหลักมีข้อจำกัด
ข้อจำกัดเหล่านี้ทำให้เหมาะสำหรับงานที่มุ่งเน้นและต้องใช้ทรัพยากรจำนวนมาก อาจใช้เทรดหลัก
สแต็กการเรียกใช้
โครงสร้างข้อมูลที่ใช้ในการจัดการ "บริบทการดำเนินการ" ซึ่งเป็นโค้ดที่กำลัง กำลังดำเนินการอยู่ เป็นรายการที่เรียกว่า สแต็กการเรียกใช้ (มักจะเรียกสั้นๆ ว่า "กองซ้อน") เมื่อมีการเรียกใช้สคริปต์ครั้งแรก อินเตอร์พรีเตอร์ของ JavaScript สร้าง "บริบทการดำเนินการทั่วโลก" และพุชไปยังสแต็กการเรียกใช้ ภายในบริบทส่วนกลางที่ดำเนินการทีละรายการ จากบนลงล่าง ด้านล่าง เมื่อล่ามพบการเรียกใช้ฟังก์ชันขณะเรียกใช้ ระดับโลกได้มีการผลักดัน "บริบทการเรียกใช้ฟังก์ชัน" สำหรับการติดต่อนั้นไปยัง ที่ด้านบนของสแต็ก หยุดบริบทการดำเนินการส่วนกลางไว้ชั่วคราว และเรียกใช้ฟังก์ชัน บริบทการดำเนินการ
ทุกครั้งที่มีการเรียกใช้ฟังก์ชัน บริบทการดำเนินการของฟังก์ชันสำหรับการเรียกนั้นจะเป็น ที่ด้านบนของสแต็ก เหนือบริบทการดำเนินการปัจจุบัน Call Stack ทำงานแบบ "เข้าก่อน ก่อน" ซึ่งหมายความว่า การเรียกใช้ฟังก์ชันล่าสุด ซึ่งเป็นค่าสูงสุดในสแต็กจะดำเนินการ แล้วดำเนินการต่อ จนกว่าจะแก้ไขได้ เมื่อฟังก์ชันนั้นเสร็จสมบูรณ์ ล่ามจะนำออก จากสแต็กการเรียกใช้ และบริบทการดำเนินการที่มีการเรียกใช้ฟังก์ชันนั้น จะกลายเป็นรายการสูงสุดในสแต็กอีกครั้งและกลับมาดำเนินการอีกครั้ง
บริบทการดำเนินการเหล่านี้จะบันทึกค่าที่จำเป็นต่อการดำเนินการ โฆษณาเหล่านี้
กำหนดตัวแปรและฟังก์ชันที่ใช้ได้ภายในขอบเขตของ
ขึ้นอยู่กับบริบทระดับบนสุด และระบุและกำหนดค่าของฟังก์ชัน
คีย์เวิร์ด this
รายการในบริบทของฟังก์ชัน
การวนซ้ำเหตุการณ์และคิวการติดต่อกลับ
การดำเนินการตามลำดับนี้หมายความว่างานแบบอะซิงโครนัสที่มี Callback
เช่น การดึงข้อมูลจากเซิร์ฟเวอร์ การตอบสนองการโต้ตอบของผู้ใช้
หรือกำลังรอตัวจับเวลาที่ตั้งค่าด้วย setTimeout
หรือ setInterval
จะบล็อก
เทรดหลักจนกว่างานนั้นจะเสร็จสมบูรณ์ หรือขัดจังหวะ
บริบทการดำเนินการปัจจุบัน ณ เวลาที่บริบทการดำเนินการของฟังก์ชัน Callback
ลงในกองซ้อนแล้ว JavaScript จะจัดการงานที่ไม่พร้อมกันเพื่อแก้ไขปัญหานี้
โดยใช้ "โมเดลการเกิดขึ้นพร้อมกัน" ที่ขับเคลื่อนด้วยเหตุการณ์ ที่สร้างขึ้นจาก "ลูปเหตุการณ์" และ
"คิวการโทรกลับ" (บางครั้งเรียกว่า "คิวข้อความ")
เมื่อมีการเรียกใช้งานแบบไม่พร้อมกันในเทรดหลัก ระบบจะ Callback บริบทการดำเนินการของฟังก์ชันจะอยู่ในคิว Callback ไม่ใช่ด้านบนของฟังก์ชัน สแต็กการเรียกใช้ การวนซ้ำเหตุการณ์เป็นรูปแบบที่บางครั้งเรียกว่า reactor ซึ่งใช้อย่างต่อเนื่อง สำรวจสถานะของสแต็กการเรียกใช้และคิว Callback หากมีงานใน คิว Callback และ Event Loop จะระบุว่าสแต็กการเรียกใช้ว่างเปล่า งานจากคิว Callback จะถูกพุชไปยังสแต็กทีละรายการ ดำเนินการแล้ว