การแนะนำสั้น ๆ เกี่ยวกับการกำหนดขอบเขตคำศัพท์คืออะไร?
การแนะนำสั้น ๆ เกี่ยวกับการกำหนดขอบเขตคำศัพท์คืออะไร?
คำตอบ:
ฉันเข้าใจพวกเขาผ่านตัวอย่าง :)
ครั้งแรกขอบเขตคำศัพท์ (หรือที่เรียกว่าขอบเขตคงที่ ) ในรูปแบบ C-like:
void fun()
{
int x = 5;
void fun2()
{
printf("%d", x);
}
}
ทุกระดับภายในสามารถเข้าถึงระดับนอกของมัน
มีอีกวิธีหนึ่งที่เรียกว่าขอบเขตแบบไดนามิกที่ใช้โดยการใช้งานLispครั้งแรกอีกครั้งในรูปแบบ C-like:
void fun()
{
printf("%d", x);
}
void dummy1()
{
int x = 5;
fun();
}
void dummy2()
{
int x = 10;
fun();
}
ที่นี่fun
สามารถเข้าถึงx
ในdummy1
หรือdummy2
หรือใด ๆx
ในการทำงานใด ๆ ที่โทรfun
ที่มีx
การประกาศในนั้น
dummy1();
จะพิมพ์ 5
dummy2();
จะพิมพ์ 10
อันแรกเรียกว่าสแตติกเพราะมันสามารถอนุมานได้ในเวลารวบรวมและที่สองเรียกว่าแบบไดนามิกเพราะขอบเขตด้านนอกเป็นแบบไดนามิกและขึ้นอยู่กับการเรียกโซ่ของฟังก์ชั่น
ฉันพบว่าการกำหนดขอบเขตคงที่ง่ายขึ้นสำหรับดวงตา ภาษาส่วนใหญ่ใช้วิธีนี้ในที่สุดแม้แต่เสียงกระเพื่อม (สามารถทำทั้งสองอย่างได้ไหม) การกำหนดขอบเขตแบบไดนามิกเปรียบเสมือนการส่งการอ้างอิงของตัวแปรทั้งหมดไปยังฟังก์ชันที่เรียกว่า
เป็นตัวอย่างของสาเหตุที่คอมไพเลอร์ไม่สามารถอนุมานขอบเขตไดนามิกด้านนอกของฟังก์ชันได้ให้พิจารณาตัวอย่างสุดท้ายของเรา ถ้าเราเขียนอะไรแบบนี้:
if(/* some condition */)
dummy1();
else
dummy2();
ห่วงโซ่การโทรขึ้นอยู่กับเงื่อนไขเวลาทำงาน หากเป็นจริงแล้วเชนการโทรจะมีลักษณะดังนี้:
dummy1 --> fun()
หากเงื่อนไขเป็นเท็จ:
dummy2 --> fun()
ขอบเขตด้านนอกของfun
ทั้งสองกรณีคือผู้โทรบวกผู้โทรของผู้โทรและอื่นๆ
เพียงพูดถึงว่าภาษา C ไม่อนุญาตให้ใช้ฟังก์ชันที่ซ้อนกันหรือการกำหนดขอบเขตแบบไดนามิก
JavaScript
แต่คำถามคือติดแท็กด้วย ดังนั้นฉันคิดว่านี่ไม่ควรทำเครื่องหมายว่าเป็นคำตอบที่ยอมรับได้ ขอบเขตคำศัพท์เฉพาะใน JS แตกต่างกัน
for
ลูปเป็นปัญหาทั่วไป ขอบเขตของคำศัพท์สำหรับ JavaScript เป็นเฉพาะในระดับฟังก์ชั่นเว้นแต่ ES6 let
หรือconst
ถูกนำมาใช้
ให้ลองนิยามที่สั้นที่สุด:
คำศัพท์กำหนดขอบเขตกำหนดวิธีการชื่อตัวแปรได้รับการแก้ไขในการทำงานที่ซ้อนกัน: ฟังก์ชั่นภายในประกอบด้วยขอบเขตของฟังก์ชั่นที่ผู้ปกครองแม้ว่าฟังก์ชั่นที่ผู้ปกครองได้กลับ
นั่นคือทั้งหมดที่มีให้มัน!
var scope = "I am global";
function whatismyscope(){
var scope = "I am just a local";
function func() {return scope;}
return func;
}
whatismyscope()()
รหัสด้านบนจะส่งคืน "ฉันเป็นแค่คนท้องถิ่น" จะไม่ส่งคืน "ฉันเป็นคนทั่วโลก" เพราะฟังก์ชั่น func () นับว่ามีการกำหนดไว้ แต่เดิมซึ่งอยู่ภายใต้ขอบเขตของฟังก์ชัน whatismyscope
มันจะไม่รบกวนสิ่งที่มันถูกเรียก (ขอบเขตทั่วโลก / จากฟังก์ชั่นอื่นแม้) นั่นคือเหตุผลที่ค่าขอบเขตทั่วโลกที่ฉันเป็นสากลจะไม่ถูกพิมพ์
สิ่งนี้เรียกว่าการกำหนดขอบเขตศัพท์โดยที่ " ฟังก์ชันจะถูกดำเนินการโดยใช้ขอบเขตของห่วงโซ่ที่มีผลเมื่อถูกกำหนด " - ตามคู่มือนิยาม JavaScript
ขอบเขตคำศัพท์เป็นแนวคิดที่ทรงพลังมาก
หวังว่านี่จะช่วย .. :)
การกำหนดขอบเขตของคำศัพท์ Lexical (คงที่ AKA) หมายถึงการกำหนดขอบเขตของตัวแปรตามตำแหน่งในคลังข้อความของรหัสเท่านั้น ตัวแปรอ้างถึงสภาพแวดล้อมระดับบนสุดเสมอ เป็นการดีที่จะเข้าใจในส่วนที่เกี่ยวกับขอบเขตไดนามิก
ขอบเขตกำหนดพื้นที่ที่มีฟังก์ชั่นตัวแปรและอื่น ๆ ตัวอย่างความพร้อมใช้งานของตัวแปรนั้นถูกกำหนดไว้ในบริบทสมมติว่าฟังก์ชั่นไฟล์หรือวัตถุถูกกำหนดไว้โดยปกติเราเรียกตัวแปรท้องถิ่นเหล่านี้
ส่วนคำศัพท์หมายถึงคุณสามารถสืบทอดขอบเขตจากการอ่านซอร์สโค้ด
ขอบเขตคำศัพท์หรือที่เรียกว่าขอบเขตคงที่
ขอบเขตแบบไดนามิกกำหนดตัวแปรทั่วโลกที่สามารถเรียกหรืออ้างอิงได้จากทุกที่หลังจากถูกกำหนด บางครั้งพวกเขาถูกเรียกว่าตัวแปรทั่วโลกแม้ว่าตัวแปรทั่วโลกในภาษาโปรแกรมส่วนใหญ่มีขอบเขตศัพท์ ซึ่งหมายความว่ามันสามารถได้มาจากการอ่านรหัสว่าตัวแปรมีอยู่ในบริบทนี้ อาจจะต้องทำตามการใช้งานหรือรวมถึงข้อเพื่อค้นหา instatiation หรือคำนิยาม แต่รหัส / คอมไพเลอร์รู้เกี่ยวกับตัวแปรในสถานที่นี้
ในการกำหนดขอบเขตแบบไดนามิกในทางตรงกันข้ามคุณค้นหาในฟังก์ชั่นท้องถิ่นก่อนจากนั้นคุณค้นหาในฟังก์ชั่นที่เรียกว่าฟังก์ชั่นท้องถิ่นจากนั้นคุณค้นหาในฟังก์ชั่นที่เรียกว่าฟังก์ชั่นนั้น ๆ "Dynamic" หมายถึงการเปลี่ยนแปลงโดยที่ call stack สามารถแตกต่างกันได้ทุกครั้งที่มีการเรียกใช้ฟังก์ชั่นที่กำหนดดังนั้นฟังก์ชันอาจชนกับตัวแปรที่แตกต่างกันขึ้นอยู่กับตำแหน่งที่เรียกใช้ (ดูที่นี่ )
หากต้องการดูตัวอย่างที่น่าสนใจสำหรับขอบเขตแบบไดนามิกดูที่นี่
สำหรับรายละเอียดเพิ่มเติมดูที่นี่และที่นี่
ตัวอย่างบางส่วนใน Delphi / Object Pascal
Delphi มีขอบเขตคำศัพท์
unit Main;
uses aUnit; // makes available all variables in interface section of aUnit
interface
var aGlobal: string; // global in the scope of all units that use Main;
type
TmyClass = class
strict private aPrivateVar: Integer; // only known by objects of this class type
// lexical: within class definition,
// reserved word private
public aPublicVar: double; // known to everyboday that has access to a
// object of this class type
end;
implementation
var aLocalGlobal: string; // known to all functions following
// the definition in this unit
end.
Delphi ที่ใกล้เคียงที่สุดจะได้รับขอบเขตแบบไดนามิกคือคู่ฟังก์ชัน RegisterClass () / GetClass () สำหรับการใช้งานดูที่นี่
สมมติว่าเวลา RegisterClass ([TmyClass]) ถูกเรียกให้ลงทะเบียนคลาสที่แน่นอนไม่สามารถทำนายได้โดยการอ่านรหัส (มันถูกเรียกในวิธีการคลิกปุ่มที่เรียกโดยผู้ใช้) รหัสที่เรียก GetClass ('TmyClass') จะได้รับ ผลลัพธ์หรือไม่ การเรียกใช้ RegisterClass () ไม่จำเป็นต้องอยู่ในขอบเขตศัพท์ของหน่วยโดยใช้ GetClass ();
ความเป็นไปได้อีกอย่างสำหรับขอบเขตแบบไดนามิกคือวิธีการที่ไม่ระบุตัวตน (การปิด) ใน Delphi 2009 เนื่องจากพวกเขารู้ว่าตัวแปรของฟังก์ชันการโทร มันไม่ได้เป็นไปตามเส้นทางการโทรจากที่นั่นซ้ำและดังนั้นจึงไม่เต็ม
ฉันรักคำตอบที่โดดเด่นและไม่ใช้ภาษาจากคนอย่าง @Arak เนื่องจากคำถามนี้ถูกแท็กJavaScriptแต่ฉันต้องการที่จะชิปในบันทึกย่อบางอย่างที่เฉพาะเจาะจงกับภาษานี้
ใน JavaScript ตัวเลือกของเราสำหรับการกำหนดขอบเขตคือ:
var _this = this; function callback(){ console.log(_this); }
callback.bind(this)
มันเป็นมูลค่า noting ผมคิดว่า JavaScript ไม่ได้จริงๆมีการกำหนดขอบเขตแบบไดนามิก .bind
ปรับthis
คำหลักและใกล้เคียงกัน แต่ไม่เหมือนกันในทางเทคนิค
นี่คือตัวอย่างที่แสดงให้เห็นถึงวิธีการทั้งสอง คุณทำสิ่งนี้ทุกครั้งที่คุณตัดสินใจเกี่ยวกับวิธีกำหนดขอบเขตการเรียกกลับดังนั้นสิ่งนี้ใช้กับสัญญาตัวจัดการเหตุการณ์และอื่น ๆ
นี่คือสิ่งที่คุณอาจทราบLexical Scoping
ถึงการเรียกกลับใน JavaScript:
var downloadManager = {
initialize: function() {
var _this = this; // Set up `_this` for lexical access
$('.downloadLink').on('click', function () {
_this.startDownload();
});
},
startDownload: function(){
this.thinking = true;
// Request the file from the server and bind more callbacks for when it returns success or failure
}
//...
};
อีกวิธีในการกำหนดขอบเขตคือการใช้Function.prototype.bind
:
var downloadManager = {
initialize: function() {
$('.downloadLink').on('click', function () {
this.startDownload();
}.bind(this)); // Create a function object bound to `this`
}
//...
วิธีการเหล่านี้เท่าที่ฉันรู้เทียบเท่ากับพฤติกรรม
bind
ไม่ส่งผลกระทบต่อขอบเขต
การกำหนดขอบเขตคำศัพท์: ตัวแปรที่ประกาศภายนอกฟังก์ชั่นเป็นตัวแปรทั่วโลกและสามารถมองเห็นได้ทุกที่ในโปรแกรม JavaScript ตัวแปรที่ประกาศภายในฟังก์ชันมีขอบเขตฟังก์ชันและสามารถมองเห็นได้เฉพาะโค้ดที่ปรากฏในฟังก์ชันนั้น
IBMกำหนดเป็น:
ส่วนของโปรแกรมหรือหน่วยส่วนที่มีการประกาศใช้ ตัวระบุที่ประกาศในชุดคำสั่งนั้นเป็นที่รู้จักกันในชุดคำสั่งนั้นและภายในชุดคำสั่งที่ซ้อนกันทั้งหมด ถ้ารูทีนที่ซ้อนกันประกาศไอเท็มที่มีชื่อเดียวกันไอเท็มด้านนอกจะไม่พร้อมใช้งานในรูทีนที่ซ้อนกัน
ตัวอย่างที่ 1:
function x() {
/*
Variable 'a' is only available to function 'x' and function 'y'.
In other words the area defined by 'x' is the lexical scope of
variable 'a'
*/
var a = "I am a";
function y() {
console.log( a )
}
y();
}
// outputs 'I am a'
x();
ตัวอย่างที่ 2:
function x() {
var a = "I am a";
function y() {
/*
If a nested routine declares an item with the same name,
the outer item is not available in the nested routine.
*/
var a = 'I am inner a';
console.log( a )
}
y();
}
// outputs 'I am inner a'
x();
ขอบเขตของคำศัพท์หมายถึงว่าในกลุ่มที่ซ้อนกันของฟังก์ชั่นฟังก์ชั่นด้านในมีการเข้าถึงตัวแปรและทรัพยากรอื่น ๆ ของขอบเขตของพ่อแม่ ซึ่งหมายความว่าฟังก์ชันลูกถูกผูกไว้กับบริบทการดำเนินการของผู้ปกครอง ขอบเขตของคำศัพท์บางครั้งก็ยังเรียกว่าขอบเขตแบบคงที่
function grandfather() {
var name = 'Hammad';
// 'likes' is not accessible here
function parent() {
// 'name' is accessible here
// 'likes' is not accessible here
function child() {
// Innermost level of the scope chain
// 'name' is also accessible here
var likes = 'Coding';
}
}
}
สิ่งที่คุณจะสังเกตเห็นเกี่ยวกับขอบเขตศัพท์คือมันทำงานไปข้างหน้าชื่อความหมายสามารถเข้าถึงได้โดยบริบทการดำเนินการของเด็ก แต่มันไม่ทำงานย้อนกลับไปที่พ่อแม่ของมันหมายความว่าพ่อแม่ของมันlikes
ไม่สามารถเข้าถึงตัวแปรได้
นอกจากนี้ยังบอกเราว่าตัวแปรที่มีชื่อเดียวกันในบริบทการดำเนินการที่แตกต่างกันจะมีความสำคัญกว่าจากบนลงล่างของสแตกการเรียกใช้ ตัวแปรที่มีชื่อคล้ายกับตัวแปรอื่นในฟังก์ชั่นด้านในสุด (บริบทสูงสุดของสแต็คการเรียกใช้งาน) จะมีลำดับความสำคัญสูงกว่า
ในภาษาง่ายๆขอบเขตศัพท์เป็นตัวแปรที่กำหนดไว้นอกขอบเขตหรือขอบเขตด้านบนของคุณโดยอัตโนมัติมีอยู่ในขอบเขตของคุณซึ่งหมายความว่าคุณไม่จำเป็นต้องผ่านมันไปที่นั่น
ตัวอย่าง:
let str="JavaScript";
const myFun = () => {
console.log(str);
}
myFun();
// เอาท์พุท: JavaScript
bind
ได้ กับพวกเขาbind
ไม่จำเป็นต้องใช้อีกต่อไป สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงตรวจสอบstackoverflow.com/a/34361380/11127383
มีส่วนสำคัญของการสนทนาที่ล้อมรอบศัพท์และการกำหนดขอบเขตแบบไดนามิกที่ขาดหายไป: คำอธิบายทั่วไปของอายุการใช้งานของตัวแปรที่กำหนดขอบเขต - หรือเมื่อสามารถเข้าถึงตัวแปรได้
การกำหนดขอบเขตแบบไดนามิกมีความสัมพันธ์กับการกำหนดขอบเขตแบบ "โกลบอล" แบบเดียวกับที่เราคิดเกี่ยวกับมันแบบดั้งเดิม (เหตุผลที่ฉันนำมาเปรียบเทียบระหว่างทั้งสองคือมันได้ถูกกล่าวถึงแล้ว- และฉันไม่ชอบคำอธิบายของบทความที่เชื่อมโยง ); มันอาจจะดีที่สุดที่เราจะไม่ทำการเปรียบเทียบระหว่างโลกกับพลวัต - แม้ว่าตามที่กล่าวไว้ในบทความที่เชื่อมโยงว่า "... [มัน] มีประโยชน์ในฐานะที่เป็นตัวแทนของตัวแปรที่มีการกำหนดขอบเขตทั่วโลก"
ดังนั้นในภาษาอังกฤษธรรมดาความแตกต่างที่สำคัญระหว่างกลไกการกำหนดขอบเขตทั้งสองคืออะไร
การกำหนดขอบเขตของคำศัพท์ได้ถูกกำหนดไว้เป็นอย่างดีตลอดคำตอบข้างต้น: ตัวแปรขอบเขตที่กำหนดขอบเขตมีให้ใช้ - หรือสามารถเข้าถึงได้ - ในระดับท้องถิ่นของฟังก์ชันที่กำหนดไว้
อย่างไรก็ตาม - เนื่องจากมันไม่ได้เป็นจุดสนใจของ OP - การกำหนดขอบเขตแบบไดนามิกไม่ได้รับความสนใจอย่างมากและความสนใจที่ได้รับหมายความว่ามันอาจต้องใช้อีกเล็กน้อย (นั่นไม่ใช่คำวิจารณ์ของคำตอบอื่น ๆ แต่เป็น "โอ้ คำตอบนั้นทำให้เราหวังว่าจะมีอีกเล็กน้อย ") ดังนั้นนี่เป็นอีกเล็กน้อย:
การกำหนดขอบเขตแบบไดนามิกหมายความว่าตัวแปรสามารถเข้าถึงโปรแกรมขนาดใหญ่ในช่วงอายุการใช้งานของการเรียกใช้ฟังก์ชัน - หรือในขณะที่ฟังก์ชันกำลังดำเนินการอยู่ จริงๆแล้ว Wikipedia ทำได้ดีมากโดยอธิบายความแตกต่างระหว่างสองอย่างนี้ เพื่อไม่ให้สับสนนี่คือข้อความที่อธิบายการกำหนดขอบเขตแบบไดนามิก:
... [I] n การกำหนดขอบเขตแบบไดนามิก (หรือขอบเขตแบบไดนามิก) หากขอบเขตของชื่อตัวแปรเป็นฟังก์ชันที่แน่นอนขอบเขตนั้นจะเป็นช่วงเวลาระหว่างที่ฟังก์ชันกำลังดำเนินการ: ในขณะที่ฟังก์ชันกำลังทำงานอยู่ชื่อตัวแปรจะมีอยู่ และถูกผูกไว้กับตัวแปร แต่หลังจากฟังก์ชันส่งคืนชื่อตัวแปรจะไม่มีอยู่
ขอบเขตศัพท์หมายถึงฟังก์ชั่นการค้นหาตัวแปรในบริบทที่มันถูกกำหนดและไม่ได้อยู่ในขอบเขตรอบ ๆ มันทันที
ดูว่าขอบเขตคำศัพท์ทำงานอย่างไร Lispหากคุณต้องการรายละเอียดเพิ่มเติม คำตอบที่เลือกโดย Kyle Cronin ในตัวแปร Dynamic และ Lexical ใน Common LISPนั้นชัดเจนกว่าคำตอบที่นี่
ฉันได้เรียนรู้เรื่องนี้โดยบังเอิญในชั้นเรียน Lisp และมันก็เกิดขึ้นกับการใช้งานใน JavaScript เช่นกัน
ฉันรันรหัสนี้ในคอนโซลของ Chrome
// JavaScript Equivalent Lisp
var x = 5; //(setf x 5)
console.debug(x); //(print x)
function print_x(){ //(defun print-x ()
console.debug(x); // (print x)
} //)
(function(){ //(let
var x = 10; // ((x 10))
console.debug(x); // (print x)
print_x(); // (print-x)
})(); //)
เอาท์พุท:
5
10
5
ขอบเขตศัพท์ใน JavaScript หมายความว่าตัวแปรที่กำหนดไว้ภายนอกฟังก์ชั่นสามารถเข้าถึงได้ภายในฟังก์ชั่นอื่นที่กำหนดไว้หลังจากการประกาศตัวแปร แต่สิ่งที่ตรงกันข้ามนั้นไม่เป็นความจริง ตัวแปรที่กำหนดภายในฟังก์ชั่นจะไม่สามารถเข้าถึงได้นอกฟังก์ชั่นนั้น
แนวคิดนี้ถูกใช้อย่างมากในการปิดใน JavaScript
สมมติว่าเรามีรหัสด้านล่าง
var x = 2;
var add = function() {
var y = 1;
return x + y;
};
ตอนนี้เมื่อคุณโทรเพิ่ม () -> สิ่งนี้จะพิมพ์ 3
ดังนั้นฟังก์ชั่นเพิ่ม () กำลังเข้าถึงตัวแปรทั่วโลกx
ที่กำหนดไว้ก่อนที่จะเพิ่มฟังก์ชั่นวิธี สิ่งนี้เรียกว่าเนื่องจากการกำหนดขอบเขตคำศัพท์ใน JavaScript
add()
ฟังก์ชั่นที่เรียกใช้ทันทีต่อจากโค้ดขนาดที่กำหนดมันจะพิมพ์ 3 การกำหนดขอบเขตคำศัพท์ไม่ได้หมายความว่าฟังก์ชันสามารถเข้าถึงตัวแปรโกลบอลนอกบริบทโลคัล ดังนั้นโค้ดตัวอย่างจึงไม่ช่วยแสดงความหมายของการกำหนดขอบเขตคำศัพท์ การแสดงการกำหนดขอบเขตคำศัพท์ในโค้ดต้องการตัวอย่างตัวนับหรืออย่างน้อยคำอธิบายของการตีความที่เป็นไปได้อื่น ๆ ของรหัส
ขอบเขตคำศัพท์หมายถึงศัพท์ของตัวระบุ (เช่นตัวแปรฟังก์ชัน ฯลฯ ) ที่มองเห็นได้จากตำแหน่งปัจจุบันในสแตกการเรียกใช้
- global execution context
- foo
- bar
- function1 execution context
- foo2
- bar2
- function2 execution context
- foo3
- bar3
foo
และ bar
อยู่ในส่วนของพจนานุกรมของตัวระบุที่มีอยู่เสมอเพราะอยู่ในระดับโลก
เมื่อfunction1
ถูกดำเนินการก็มีการเข้าถึงพจนานุกรมของfoo2
, bar2
, และfoo
bar
เมื่อfunction2
ถูกดำเนินการก็มีการเข้าถึงพจนานุกรมของfoo3
, bar3
, foo2
, bar2
, foo
และbar
และ
เหตุผลที่ฟังก์ชั่นทั่วโลกและ / หรือด้านนอกไม่สามารถเข้าถึงตัวระบุฟังก์ชั่นด้านในเป็นเพราะการดำเนินการของฟังก์ชั่นที่ยังไม่เกิดขึ้นและดังนั้นจึงไม่มีตัวระบุของมันได้รับการจัดสรรให้กับหน่วยความจำ ยิ่งไปกว่านั้นเมื่อบริบทภายในดำเนินการแล้วจะถูกลบออกจากสแตกการดำเนินการซึ่งหมายความว่าตัวระบุทั้งหมดของมันถูกรวบรวมขยะและไม่สามารถใช้ได้อีกต่อไป
ในที่สุดนี่คือเหตุผลที่บริบทการดำเนินการที่ซ้อนกันสามารถเข้าถึงบริบทการดำเนินการของบรรพบุรุษได้ตลอดเวลาและเหตุใดจึงสามารถเข้าถึงพจนานุกรมของตัวระบุที่มากขึ้น
ดู:
ขอขอบคุณเป็นพิเศษที่@ robr3rdสำหรับความช่วยเหลือในการทำให้คำจำกัดความข้างต้นง่ายขึ้น
ต่อไปนี้เป็นมุมที่แตกต่างกันสำหรับคำถามนี้ที่เราสามารถทำได้โดยย้อนกลับไปดูบทบาทของการกำหนดขอบเขตในกรอบการตีความที่กว้างขึ้น (เรียกใช้โปรแกรม) ลองจินตนาการว่าคุณกำลังสร้างล่าม (หรือคอมไพเลอร์) สำหรับภาษาและรับผิดชอบในการคำนวณผลลัพธ์ให้โปรแกรมและอินพุทบางอย่าง
การตีความเกี่ยวข้องกับการติดตามสามสิ่ง:
สถานะ - คือตัวแปรและตำแหน่งหน่วยความจำที่อ้างอิงบนฮีปและสแต็ก
การทำงานกับสถานะนั้น - คือรหัสทุกบรรทัดในโปรแกรมของคุณ
สภาพแวดล้อมในการที่ได้รับการดำเนินการทำงาน - คือการฉายของรัฐในการดำเนินการ
ล่ามเริ่มต้นที่บรรทัดแรกของรหัสในโปรแกรมคำนวณสภาพแวดล้อมของมันเรียกใช้บรรทัดในสภาพแวดล้อมนั้นและจับผลของมันที่มีต่อสถานะของโปรแกรม จากนั้นจะติดตามโฟลว์การควบคุมของโปรแกรมเพื่อเรียกใช้โค้ดถัดไปและทำซ้ำกระบวนการจนกว่าโปรแกรมจะสิ้นสุด
วิธีที่คุณคำนวณสภาพแวดล้อมสำหรับการดำเนินการใด ๆ คือผ่านชุดกฎที่เป็นทางการซึ่งกำหนดโดยภาษาการเขียนโปรแกรม คำว่า "การผูก" มักใช้เพื่ออธิบายการแมปสถานะโดยรวมของโปรแกรมกับค่าในสภาพแวดล้อม โปรดทราบว่าโดย "สถานะโดยรวม" เราไม่ได้หมายถึงสถานะโลก แต่รวมผลรวมของคำจำกัดความที่เข้าถึงได้ทุกจุดในการดำเนินการ)
นี่คือกรอบงานที่กำหนดปัญหาการกำหนดขอบเขตไว้ ตอนนี้ไปยังส่วนถัดไปของตัวเลือกของเรา
นี่คือส่วนสำคัญของการกำหนดขอบเขตแบบไดนามิกซึ่งในสภาพแวดล้อมที่มีรหัสใด ๆ ที่ทำงานอยู่จะถูกผูกไว้กับสถานะของโปรแกรมตามที่กำหนดโดยบริบทการดำเนินการของมัน
กล่าวอีกนัยหนึ่งกับขอบเขตคำศัพท์สภาพแวดล้อมที่เห็นรหัสใด ๆ ที่ถูกผูกไว้กับรัฐที่เกี่ยวข้องกับขอบเขตที่กำหนดไว้อย่างชัดเจนในภาษาเช่นบล็อกหรือฟังก์ชั่น
คำถามโบราณ แต่นี่คือสิ่งที่ฉันทำ
ขอบเขตคำศัพท์ (คงที่) หมายถึงขอบเขตของตัวแปรในซอร์สโค้ดรหัสที่มา
ในภาษาอย่าง JavaScript ซึ่งฟังก์ชันสามารถส่งผ่านไปมาและเชื่อมต่อและแนบอีกครั้งกับวัตถุเบ็ดเตล็ดคุณอาจมีแม้ว่าขอบเขตนั้นจะขึ้นอยู่กับผู้ที่กำลังเรียกใช้ฟังก์ชันในเวลานั้น แต่ก็ไม่ได้ การเปลี่ยนขอบเขตนั้นจะเป็นขอบเขตแบบไดนามิกและ JavaScript จะไม่ทำเช่นนั้นthis
อ้างอิงวัตถุ
เพื่อแสดงจุด:
var a='apple';
function doit() {
var a='aardvark';
return function() {
alert(a);
}
}
var test=doit();
test();
ในตัวอย่างตัวแปรa
ถูกกำหนดแบบโกลบอล แต่มีเงาในdoit()
ฟังก์ชัน ฟังก์ชั่นนี้จะส่งกลับฟังก์ชั่นอื่นซึ่งตามที่คุณเห็นอาศัยa
ตัวแปรนอกขอบเขตของตัวเอง
หากคุณรันสิ่งนี้คุณจะพบว่าค่าที่ใช้คือaardvark
ไม่ใช่apple
ซึ่งจะอยู่ในขอบเขตของtest()
ฟังก์ชันซึ่งไม่ได้อยู่ในขอบเขตของฟังก์ชันดั้งเดิม นั่นคือขอบเขตที่ใช้เป็นขอบเขตตามที่ปรากฏในซอร์สโค้ดไม่ใช่ขอบเขตที่ใช้ฟังก์ชันจริง
ความจริงเรื่องนี้อาจมีผลกระทบที่น่ารำคาญ ตัวอย่างเช่นคุณอาจตัดสินใจว่าการจัดระเบียบฟังก์ชั่นของคุณแยกจากกันง่ายกว่าและใช้เมื่อถึงเวลาเช่นในตัวจัดการเหตุการณ์:
var a='apple',b='banana';
function init() {
var a='aardvark',b='bandicoot';
document.querySelector('button#a').onclick=function(event) {
alert(a);
}
document.querySelector('button#b').onclick=doB;
}
function doB(event) {
alert(b);
}
init();
<button id="a">A</button>
<button id="b">B</button>
ตัวอย่างรหัสนี้ทำอย่างใดอย่างหนึ่ง คุณจะเห็นว่าเนื่องจากการกำหนดขอบเขตคำศัพท์ปุ่มA
ใช้ตัวแปรภายในขณะที่ปุ่มB
ไม่ได้ คุณอาจท้ายฟังก์ชั่นการทำรังมากกว่าที่คุณจะชอบ
ในทั้งสองตัวอย่างคุณจะสังเกตเห็นว่าตัวแปรที่อยู่ภายในขอบเขตศัพท์ยังคงอยู่แม้ว่าฟังก์ชั่นที่มีฟังก์ชั่นจะทำงาน สิ่งนี้เรียกว่าการปิดและหมายถึงการเข้าถึงฟังก์ชันที่ซ้อนกันของตัวแปรภายนอกแม้ว่าฟังก์ชันภายนอกจะเสร็จสิ้นแล้ว จาวาสคริปต์ต้องฉลาดพอที่จะตัดสินว่าตัวแปรเหล่านั้นไม่ต้องการอีกต่อไปหรือไม่และหากไม่สามารถเก็บขยะได้
ปกติฉันเรียนรู้จากตัวอย่างและนี่คือสิ่งเล็กน้อย:
const lives = 0;
function catCircus () {
this.lives = 1;
const lives = 2;
const cat1 = {
lives: 5,
jumps: () => {
console.log(this.lives);
}
};
cat1.jumps(); // 1
console.log(cat1); // { lives: 5, jumps: [Function: jumps] }
const cat2 = {
lives: 5,
jumps: () => {
console.log(lives);
}
};
cat2.jumps(); // 2
console.log(cat2); // { lives: 5, jumps: [Function: jumps] }
const cat3 = {
lives: 5,
jumps: () => {
const lives = 3;
console.log(lives);
}
};
cat3.jumps(); // 3
console.log(cat3); // { lives: 5, jumps: [Function: jumps] }
const cat4 = {
lives: 5,
jumps: function () {
console.log(lives);
}
};
cat4.jumps(); // 2
console.log(cat4); // { lives: 5, jumps: [Function: jumps] }
const cat5 = {
lives: 5,
jumps: function () {
var lives = 4;
console.log(lives);
}
};
cat5.jumps(); // 4
console.log(cat5); // { lives: 5, jumps: [Function: jumps] }
const cat6 = {
lives: 5,
jumps: function () {
console.log(this.lives);
}
};
cat6.jumps(); // 5
console.log(cat6); // { lives: 5, jumps: [Function: jumps] }
const cat7 = {
lives: 5,
jumps: function thrownOutOfWindow () {
console.log(this.lives);
}
};
cat7.jumps(); // 5
console.log(cat7); // { lives: 5, jumps: [Function: thrownOutOfWindow] }
}
catCircus();
หัวข้อนี้จะเกี่ยวข้องอย่างมากกับในตัวbind
ฟังก์ชั่นและนำมาใช้ใน ECMAScript 6 ฟังก์ชั่นลูกศร มันน่ารำคาญจริง ๆ เพราะสำหรับวิธี "คลาส" (ฟังก์ชั่นจริง ๆ ) ใหม่ทุกครั้งที่เราต้องการใช้เราต้องทำbind
สิ่งนี้เพื่อให้สามารถเข้าถึงขอบเขตได้
JavaScript โดยค่าเริ่มต้นไม่ได้ตั้งขอบเขตthis
ไว้ที่ฟังก์ชั่น (มันไม่ได้ตั้งบริบทบนthis
) โดยค่าเริ่มต้นคุณจะต้องพูดอย่างชัดเจนว่าบริบทใดต้องการใด
ฟังก์ชั่นลูกศรโดยอัตโนมัติได้รับสิ่งที่เรียกว่าขอบเขตของคำศัพท์ (มีการเข้าถึงความหมายของตัวแปรในการมีบล็อกของมัน) เมื่อใช้ฟังก์ชั่นลูกศรมันจะผูกthis
กับสถานที่ที่ฟังก์ชั่นลูกศรถูกกำหนดในสถานที่แรกโดยอัตโนมัติและบริบทของฟังก์ชั่นลูกศรนี้คือบล็อกที่มี
ดูวิธีการใช้งานจริงในตัวอย่างที่ง่ายที่สุดด้านล่าง
ก่อนฟังก์ชั่นลูกศร (ไม่มีขอบเขตคำศัพท์ตามค่าเริ่มต้น):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const globalScope = programming.getLanguage;
console.log(globalScope()); // Output: undefined
const localScope = programming.getLanguage.bind(programming);
console.log(localScope()); // Output: "JavaScript"
ด้วยฟังก์ชั่นลูกศร (ขอบเขตคำศัพท์ตามค่าเริ่มต้น):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const arrowFunction = () => {
console.log(programming.getLanguage());
}
arrowFunction(); // Output: "JavaScript"