ทุกครั้งที่ฉันเห็น "การปิด" ถูกกล่าวถึงและฉันพยายามค้นหา แต่ Wiki ไม่ได้ให้คำอธิบายที่ฉันเข้าใจ มีคนช่วยฉันที่นี่ได้ไหม
ทุกครั้งที่ฉันเห็น "การปิด" ถูกกล่าวถึงและฉันพยายามค้นหา แต่ Wiki ไม่ได้ให้คำอธิบายที่ฉันเข้าใจ มีคนช่วยฉันที่นี่ได้ไหม
คำตอบ:
(ข้อจำกัดความรับผิดชอบ: นี่เป็นคำอธิบายพื้นฐานเท่าที่คำจำกัดความมีความหมายฉันจะลดความซับซ้อนลงเล็กน้อย)
วิธีที่ง่ายที่สุดในการนึกถึงการปิดคือฟังก์ชั่นที่สามารถจัดเก็บเป็นตัวแปร (เรียกว่า "ฟังก์ชั่นชั้นหนึ่ง") ที่มีความสามารถพิเศษในการเข้าถึงตัวแปรอื่น ๆ ภายในขอบเขตที่มันถูกสร้างขึ้น
ตัวอย่าง (JavaScript):
var setKeyPress = function(callback) {
document.onkeypress = callback;
};
var initialize = function() {
var black = false;
document.onclick = function() {
black = !black;
document.body.style.backgroundColor = black ? "#000000" : "transparent";
}
var displayValOfBlack = function() {
alert(black);
}
setKeyPress(displayValOfBlack);
};
initialize();
ฟังก์ชั่น1ได้รับมอบหมายให้document.onclick
และdisplayValOfBlack
กำลังปิด คุณสามารถเห็นว่าพวกเขาทั้งสองอ้างอิงตัวแปรบูลีนblack
แต่ตัวแปรนั้นได้รับมอบหมายนอกฟังก์ชั่น เนื่องจากblack
อยู่ภายในขอบเขตที่ฟังก์ชันถูกกำหนดตัวชี้ไปยังตัวแปรนี้จึงถูกสงวนไว้
หากคุณใส่สิ่งนี้ลงในหน้า HTML:
นี่แสดงให้เห็นว่าทั้งสองมีการเข้าถึงเหมือนกัน black
และสามารถใช้ในการจัดเก็บสถานะโดยไม่มีวัตถุห่อหุ้มใด ๆ
การเรียกใช้setKeyPress
เพื่อแสดงให้เห็นว่าสามารถส่งผ่านฟังก์ชันเหมือนกับตัวแปรใด ๆ ได้อย่างไร ขอบเขตเก็บรักษาไว้ในการปิดยังคงเป็นหนึ่งที่ฟังก์ชั่นที่ถูกกำหนดไว้
ปกติจะใช้การปิดเป็นตัวจัดการเหตุการณ์โดยเฉพาะใน JavaScript และ ActionScript การใช้งานการปิดที่ดีจะช่วยให้คุณผูกตัวแปรกับตัวจัดการเหตุการณ์โดยไม่ต้องสร้าง wrapper วัตถุ อย่างไรก็ตามการใช้อย่างไม่ระมัดระวังจะนำไปสู่การรั่วไหลของหน่วยความจำ (เช่นเมื่อตัวจัดการเหตุการณ์ที่ไม่ได้ใช้ แต่เก็บรักษาไว้เป็นสิ่งเดียวที่จะเก็บวัตถุขนาดใหญ่ไว้ในหน่วยความจำโดยเฉพาะอย่างยิ่งวัตถุ DOM ป้องกันการรวบรวมขยะ)
1: ที่จริงแล้วฟังก์ชั่นทั้งหมดใน JavaScript ปิดลง
black
มีการประกาศภายในฟังก์ชันจะไม่ถูกทำลายเมื่อกองซ้อนคลาย ... ?
black
มีการประกาศภายในฟังก์ชันจะไม่ถูกทำลาย" โปรดจำไว้ว่าถ้าคุณประกาศวัตถุในฟังก์ชั่นแล้วกำหนดให้กับตัวแปรที่อาศัยอยู่ที่อื่นวัตถุนั้นจะถูกเก็บรักษาไว้เพราะมีการอ้างอิงอื่น ๆ
การปิดเป็นวิธีที่แตกต่างกันในการมองวัตถุ วัตถุคือข้อมูลที่มีอย่างน้อยหนึ่งฟังก์ชั่นผูกอยู่กับมัน การปิดเป็นฟังก์ชั่นที่มีตัวแปรอย่างน้อยหนึ่งตัวผูกอยู่กับมัน ทั้งสองมีความเหมือนกันโดยทั่วไปในระดับการดำเนินการอย่างน้อย ความแตกต่างที่แท้จริงคือสิ่งที่พวกเขามาจาก
ในการเขียนโปรแกรมเชิงวัตถุคุณประกาศคลาสอ็อบเจ็กต์โดยกำหนดตัวแปรสมาชิกและวิธีการ (ฟังก์ชันสมาชิก) ล่วงหน้าแล้วคุณสร้างอินสแตนซ์ของคลาสนั้น แต่ละอินสแตนซ์มาพร้อมกับสำเนาของข้อมูลสมาชิกซึ่งเริ่มต้นโดยตัวสร้าง จากนั้นคุณจะมีตัวแปรชนิดของวัตถุและส่งผ่านเป็นข้อมูลเพราะโฟกัสนั้นเป็นลักษณะของข้อมูล
ในทางกลับกันวัตถุไม่ได้ถูกกำหนดไว้ล่วงหน้าเช่นคลาสอ็อบเจ็กต์หรืออินสแตนซ์ผ่านการเรียกคอนสตรัคเตอร์ในรหัสของคุณ แต่คุณเขียนการปิดเป็นฟังก์ชันภายในของฟังก์ชันอื่น การปิดสามารถอ้างถึงตัวแปรท้องถิ่นของฟังก์ชั่นด้านนอกและคอมไพเลอร์ตรวจจับและย้ายตัวแปรเหล่านี้จากพื้นที่สแต็คของฟังก์ชั่นด้านนอกไปยังการประกาศวัตถุที่ซ่อนอยู่ของการปิด จากนั้นคุณมีตัวแปรประเภทการปิดและแม้ว่าโดยทั่วไปแล้วมันเป็นวัตถุภายใต้ประทุนคุณจะส่งมันไปรอบ ๆ เพื่ออ้างอิงการทำงานเนื่องจากโฟกัสอยู่ที่ธรรมชาติของมันในฐานะฟังก์ชัน
การปิดเทอมมาจากข้อเท็จจริงที่ว่าส่วนหนึ่งของรหัส (บล็อกฟังก์ชัน) สามารถมีตัวแปรอิสระที่ถูกปิด (เช่นถูกผูกไว้กับค่า) โดยสภาพแวดล้อมที่มีการกำหนดบล็อกของรหัส
ยกตัวอย่างนิยามฟังก์ชั่น Scala:
def addConstant(v: Int): Int = v + k
ในส่วนของฟังก์ชั่นมีสองชื่อ (ตัวแปร) v
และk
ระบุค่าจำนวนเต็มสองค่า ชื่อv
ถูกผูกไว้เพราะมันถูกประกาศเป็นอาร์กิวเมนต์ของฟังก์ชั่นaddConstant
(โดยดูที่การประกาศฟังก์ชั่นที่เรารู้ว่าv
จะได้รับการกำหนดค่าเมื่อมีการเรียกใช้ฟังก์ชั่น) ชื่อk
เป็นอิสระ wrt ฟังก์ชั่นaddConstant
เพราะฟังก์ชั่นที่มีเงื่อนงำไม่เป็นสิ่งที่ค่าk
ถูกผูกไว้กับ (และวิธี)
เพื่อประเมินการโทรเช่น:
val n = addConstant(10)
เราต้องกำหนดk
ค่าซึ่งสามารถเกิดขึ้นได้หากชื่อk
ถูกกำหนดในบริบทที่addConstant
กำหนดไว้เท่านั้น ตัวอย่างเช่น:
def increaseAll(values: List[Int]): List[Int] =
{
val k = 2
def addConstant(v: Int): Int = v + k
values.map(addConstant)
}
ตอนนี้เราได้นิยามไว้addConstant
ในบริบทที่k
ถูกกำหนดแล้วaddConstant
กลายเป็นการปิดเพราะตัวแปรอิสระทั้งหมดตอนนี้ปิด (ผูกกับค่า): addConstant
สามารถเรียกใช้และส่งผ่านได้ราวกับว่ามันเป็นฟังก์ชั่น หมายเหตุตัวแปรอิสระที่k
ถูกผูกไว้กับค่าเมื่อปิดจะถูกกำหนดไว้ในขณะที่ตัวแปรอาร์กิวเมนต์v
ที่ถูกผูกไว้เมื่อปิดจะเรียก
ดังนั้นการปิดจึงเป็นฟังก์ชั่นหรือบล็อคโค้ดที่สามารถเข้าถึงค่าที่ไม่ใช่ของท้องถิ่นผ่านตัวแปรอิสระหลังจากสิ่งเหล่านี้ถูกผูกไว้กับบริบท
ในหลายภาษาถ้าคุณใช้การปิดเพียงครั้งเดียวคุณสามารถทำให้มันไม่ระบุชื่อเช่น
def increaseAll(values: List[Int]): List[Int] =
{
val k = 2
values.map(v => v + k)
}
โปรดทราบว่าฟังก์ชั่นที่ไม่มีตัวแปรอิสระเป็นกรณีพิเศษของการปิด (พร้อมชุดว่างของตัวแปรอิสระ) Analogously เป็นฟังก์ชั่นที่ไม่ระบุชื่อเป็นกรณีพิเศษของการปิดไม่ระบุชื่อคือฟังก์ชั่นที่ไม่ระบุชื่อเป็นปิดที่ไม่ระบุชื่อกับไม่มีตัวแปรฟรี
คำอธิบายง่ายๆใน JavaScript:
var closure_example = function() {
var closure = 0;
// after first iteration the value will not be erased from the memory
// because it is bound with the returned alertValue function.
return {
alertValue : function() {
closure++;
alert(closure);
}
};
};
closure_example();
alert(closure)
closure
จะใช้ค่าที่สร้างขึ้นก่อนหน้านี้ alertValue
เนมสเปซของฟังก์ชันที่ส่งคืนจะเชื่อมต่อกับเนมสเปซที่มีclosure
ตัวแปรอยู่ เมื่อคุณลบฟังก์ชั่นทั้งหมดค่าของclosure
ตัวแปรจะถูกลบ แต่จนกว่าจะถึงเวลานั้นalertValue
ฟังก์ชันจะสามารถอ่าน / เขียนค่าของตัวแปรclosure
ได้เสมอ
หากคุณเรียกใช้รหัสนี้การวนซ้ำครั้งแรกจะกำหนดค่า 0 ให้กับclosure
ตัวแปรและเขียนฟังก์ชันใหม่เพื่อ:
var closure_example = function(){
alertValue : function(){
closure++;
alert(closure);
}
}
และเนื่องจากalertValue
ความต้องการตัวแปรท้องถิ่นที่จะดำเนินการฟังก์ชั่นที่มันผูกตัวเองด้วยค่าของตัวแปรในท้องถิ่นที่ได้รับมอบหมายก่อนหน้านี้closure
closure
และตอนนี้ทุกครั้งที่คุณเรียกใช้closure_example
ฟังก์ชันมันจะเขียนค่าที่เพิ่มขึ้นของclosure
ตัวแปรเพราะalert(closure)
ถูกผูกไว้
closure_example.alertValue()//alerts value 1
closure_example.alertValue()//alerts value 2
closure_example.alertValue()//alerts value 3
//etc.
"การปิด" คือสิ่งสำคัญรัฐท้องถิ่นและรหัสบางอย่างรวมกันเป็นแพคเกจ โดยทั่วไปแล้วสถานะโลคัลมาจากขอบเขตโดยรอบ (คำศัพท์) และโค้ดคือ (โดยพื้นฐาน) ฟังก์ชั่นด้านในซึ่งจะส่งกลับไปด้านนอก การปิดคือการรวมกันของตัวแปรที่จับที่ฟังก์ชั่นภายในเห็นและรหัสของฟังก์ชั่นภายใน
เป็นหนึ่งในสิ่งที่น่าเสียดายที่อธิบายได้ยากเนื่องจากไม่คุ้นเคย
สิ่งหนึ่งที่ฉันใช้ประสบความสำเร็จในอดีตคือ "จินตนาการว่าเรามีบางสิ่งที่เราเรียกว่า 'หนังสือ' ในการปิดห้อง 'หนังสือ' คือสำเนานั่นตรงนั้นมุม TAOCP แต่อยู่บนโต๊ะ มันคือสำเนาของหนังสือของเดรสเดนไฟล์ดังนั้นขึ้นอยู่กับว่าคุณปิดหนังสืออะไร
static
ตัวแปรโลคัลสามารถถูกพิจารณาว่าเป็นการปิดหรือไม่? การปิดใน Haskell เกี่ยวข้องกับรัฐหรือไม่
static
ตัวแปรเฉพาะที่คุณมีอย่างเดียว)
เป็นการยากที่จะกำหนดว่าการปิดใดโดยไม่กำหนดแนวคิดของ 'สถานะ'
โดยทั่วไปในภาษาที่มีการกำหนดขอบเขตคำศัพท์เต็มรูปแบบที่ปฏิบัติหน้าที่เป็นค่านิยมชั้นหนึ่งมีสิ่งพิเศษเกิดขึ้น ถ้าฉันจะทำสิ่งที่ชอบ:
function foo(x)
return x
end
x = foo
ตัวแปรx
ไม่เพียงfunction foo()
แต่อ้างอิงแต่ยังอ้างอิงสถานะที่foo
ถูกทิ้งไว้ในครั้งสุดท้ายที่มันกลับมา เวทมนตร์ที่แท้จริงเกิดขึ้นเมื่อfoo
มีฟังก์ชั่นอื่น ๆ ที่กำหนดไว้เพิ่มเติมภายในขอบเขตของมัน; มันเหมือนกับสภาพแวดล้อมขนาดเล็กของตัวเอง (เช่นเดียวกับ 'ปกติ' เรากำหนดฟังก์ชั่นในสภาพแวดล้อมระดับโลก)
ในทางปฏิบัติมันสามารถแก้ปัญหาต่าง ๆ มากมายเช่นเดียวกับคำสำคัญ 'แบบคงที่' ของ C ++ (C?) ซึ่งยังคงสถานะของตัวแปรท้องถิ่นตลอดการเรียกฟังก์ชั่นหลาย ๆ แต่มันก็เหมือนกับการใช้หลักการเดียวกันนั้น (ตัวแปรคงที่) กับฟังก์ชั่นเนื่องจากฟังก์ชั่นเป็นค่าที่ดีที่สุด การปิดเพิ่มการสนับสนุนสำหรับสถานะทั้งหมดของฟังก์ชั่นที่จะบันทึก (ไม่มีส่วนเกี่ยวข้องกับฟังก์ชั่นคงที่ของ C ++)
การรักษาฟังก์ชั่นเป็นค่าชั้นหนึ่งและเพิ่มการสนับสนุนสำหรับการปิดก็หมายความว่าคุณสามารถมีฟังก์ชั่นเดียวกันมากกว่าหนึ่งอินสแตนซ์ในหน่วยความจำ (คล้ายกับคลาส) สิ่งนี้หมายความว่าคุณสามารถใช้รหัสเดิมซ้ำได้โดยไม่ต้องรีเซ็ตสถานะของฟังก์ชันตามที่ต้องการเมื่อจัดการกับตัวแปรสแตติก C ++ ภายในฟังก์ชัน (อาจผิดเกี่ยวกับเรื่องนี้?)
นี่คือการทดสอบการสนับสนุนการปิดของ Lua
--Closure testing
--By Trae Barlow
--
function myclosure()
print(pvalue)--nil
local pvalue = pvalue or 10
return function()
pvalue = pvalue + 10 --20, 31, 42, 53(53 never printed)
print(pvalue)
pvalue = pvalue + 1 --21, 32, 43(pvalue state saved through multiple calls)
return pvalue
end
end
x = myclosure() --x now references anonymous function inside myclosure()
x()--nil, 20
x() --21, 31
x() --32, 42
--43, 53 -- if we iterated x() again
ผล:
nil
20
31
42
มันอาจจะยุ่งยากและอาจแตกต่างกันไปในแต่ละภาษา แต่ดูเหมือนว่า Lua ทุกครั้งที่มีการใช้งานฟังก์ชั่นสถานะของมันก็จะถูกรีเซ็ต ฉันพูดแบบนี้เพราะผลลัพธ์จากรหัสด้านบนจะแตกต่างกันถ้าเราเข้าถึงmyclosure
ฟังก์ชั่น / สถานะโดยตรง (แทนที่จะผ่านฟังก์ชั่นที่ไม่ระบุชื่อมันจะส่งคืน) ตามที่pvalue
จะถูกรีเซ็ตกลับเป็น 10; แต่ถ้าเราเข้าถึงสถานะของการรู้แจ้งผ่าน x (ฟังก์ชั่นนิรนาม) คุณจะเห็นว่าpvalue
มันยังมีชีวิตอยู่และอยู่ในความทรงจำ ฉันสงสัยว่ามีอะไรเพิ่มเติมอีกเล็กน้อยบางทีบางคนสามารถอธิบายลักษณะของการปฏิบัติได้ดีกว่า
PS: ฉันไม่รู้จัก C ++ 11 (นอกเหนือจากรุ่นก่อนหน้า) ดังนั้นโปรดทราบว่านี่ไม่ใช่การเปรียบเทียบระหว่างการปิดใน C ++ 11 และ Lua นอกจากนี้ 'เส้นที่ลาก' ทั้งหมดจาก Lua ถึง C ++ มีความคล้ายคลึงกันเนื่องจากตัวแปรแบบสแตติกและการปิดไม่เหมือนกัน 100% แม้ว่าบางครั้งพวกเขาจะใช้เพื่อแก้ปัญหาที่คล้ายกัน
สิ่งที่ฉันไม่แน่ใจก็คือในตัวอย่างโค้ดข้างต้นไม่ว่าจะเป็นฟังก์ชั่นที่ไม่ระบุชื่อหรือฟังก์ชั่นการสั่งซื้อที่สูงกว่าถือว่าเป็นการปิดหรือไม่?
การปิดเป็นฟังก์ชันที่มีสถานะเชื่อมโยง:
ใน Perl คุณสร้างการปิดเช่นนี้:
#!/usr/bin/perl
# This function creates a closure.
sub getHelloPrint
{
# Bind state for the function we are returning.
my ($first) = @_;a
# The function returned will have access to the variable $first
return sub { my ($second) = @_; print "$first $second\n"; };
}
my $hw = getHelloPrint("Hello");
my $gw = getHelloPrint("Goodby");
&$hw("World"); // Print Hello World
&$gw("World"); // PRint Goodby World
ถ้าเราดูการทำงานใหม่ที่มาพร้อมกับ C ++
นอกจากนี้ยังช่วยให้คุณผูกสถานะปัจจุบันกับวัตถุ:
#include <string>
#include <iostream>
#include <functional>
std::function<void(std::string const&)> getLambda(std::string const& first)
{
// Here we bind `first` to the function
// The second parameter will be passed when we call the function
return [first](std::string const& second) -> void
{ std::cout << first << " " << second << "\n";
};
}
int main(int argc, char* argv[])
{
auto hw = getLambda("Hello");
auto gw = getLambda("GoodBye");
hw("World");
gw("World");
}
ลองพิจารณาฟังก์ชั่นง่าย ๆ :
function f1(x) {
// ... something
}
ฟังก์ชั่นนี้เรียกว่าฟังก์ชั่นระดับบนสุดเพราะมันไม่ซ้อนอยู่ภายในฟังก์ชั่นอื่น ๆ ทุกคนร่วมงานฟังก์ชั่นจาวาสคริปต์ด้วยตัวเองรายชื่อของวัตถุที่เรียกว่า"ขอบเขตในเครือ" ขอบเขตขอบเขตนี้เป็นรายการของวัตถุที่เรียงลำดับแล้ว แต่ละวัตถุเหล่านี้กำหนดตัวแปรบางอย่าง
ในฟังก์ชั่นระดับบนสุดห่วงโซ่ขอบเขตประกอบด้วยวัตถุเดียววัตถุทั่วโลก ตัวอย่างเช่นฟังก์ชั่นf1
ด้านบนมีห่วงโซ่ขอบเขตที่มีวัตถุเดียวในนั้นที่กำหนดตัวแปรทั่วโลกทั้งหมด (โปรดทราบว่าคำว่า "วัตถุ" ที่นี่ไม่ได้หมายถึงวัตถุ JavaScript มันเป็นเพียงวัตถุที่กำหนดการใช้งานที่ทำหน้าที่เป็นตัวแปรคอนเทนเนอร์ซึ่ง JavaScript สามารถ "ค้นหา" ตัวแปร)
เมื่อมีการเรียกใช้ฟังก์ชันนี้ JavaScript จะสร้างสิ่งที่เรียกว่า"วัตถุการเปิดใช้งาน"และวางไว้ที่ด้านบนของห่วงโซ่ขอบเขต วัตถุนี้มีตัวแปรท้องถิ่นทั้งหมด (เช่นx
ที่นี่) ดังนั้นตอนนี้เรามีวัตถุสองรายการในห่วงโซ่ขอบเขต: สิ่งแรกคือวัตถุการเปิดใช้งานและด้านล่างเป็นวัตถุทั่วโลก
โปรดระมัดระวังอย่างยิ่งว่าวัตถุทั้งสองจะถูกใส่เข้าไปในห่วงโซ่ขอบเขตในเวลาที่ต่างกัน วัตถุทั่วโลกจะถูกวางเมื่อฟังก์ชั่นที่ถูกกำหนด (เช่นเมื่อ JavaScript แยกวิเคราะห์ฟังก์ชั่นและสร้างวัตถุฟังก์ชั่น) และวัตถุการเปิดใช้งานเข้าสู่เมื่อมีการเรียกใช้ฟังก์ชั่น
ดังนั้นตอนนี้เรารู้สิ่งนี้:
สถานการณ์น่าสนใจเมื่อเราจัดการกับฟังก์ชั่นที่ซ้อนกัน ดังนั้นเรามาสร้างหนึ่ง:
function f1(x) {
function f2(y) {
// ... something
}
}
เมื่อf1
ได้รับการกำหนดเราจะได้รับห่วงโซ่ขอบเขตสำหรับมันมีเพียงวัตถุทั่วโลก
ตอนนี้เมื่อ f1
ถูกเรียกใช้ขอบเขตของการf1
รับวัตถุการเปิดใช้งาน วัตถุการเปิดใช้งานนี้มีตัวแปรx
และตัวแปรf2
ซึ่งเป็นฟังก์ชั่น และทราบว่าf2
ได้รับการกำหนด ดังนั้นที่จุดนี้, f2
จาวาสคริปต์ยังช่วยประหยัดโซ่ขอบเขตใหม่สำหรับ ห่วงโซ่ขอบเขตที่บันทึกไว้สำหรับฟังก์ชั่นภายในนี้เป็นห่วงโซ่ขอบเขตปัจจุบันที่มีผล ห่วงโซ่ขอบเขตในปัจจุบันมีผลเป็นที่ของf1
's ดังนั้นf2
'ห่วงโซ่ขอบเขตเป็นf1
' s ปัจจุบันห่วงโซ่ขอบเขต - ซึ่งมีการเปิดใช้งานวัตถุf1
และวัตถุทั่วโลก
เมื่อf2
ถูกเรียกมันจะได้รับเป็นวัตถุเปิดใช้งานของตัวเองที่มีy
เพิ่มลงในห่วงโซ่ขอบเขตซึ่งมีวัตถุเปิดใช้งานf1
และวัตถุทั่วโลกแล้ว
หากมีฟังก์ชันซ้อนกันอีกหนึ่งรายการที่กำหนดไว้ภายในf2
ขอบเขตของห่วงโซ่นั้นจะมีวัตถุสามรายการในเวลาที่กำหนด (2 วัตถุการเปิดใช้งานของสองฟังก์ชันภายนอกและวัตถุทั่วโลก) และ 4 ในเวลาที่เรียกใช้
ดังนั้นตอนนี้เราเข้าใจวิธีการทำงานของขอบเขตการทำงาน แต่เรายังไม่ได้พูดคุยเกี่ยวกับการปิด
การรวมกันของฟังก์ชั่นวัตถุและขอบเขต (ชุดของการผูกตัวแปร) ซึ่งตัวแปรของฟังก์ชั่นได้รับการแก้ไขเรียกว่าการปิดในวรรณคดีวิทยาศาสตร์คอมพิวเตอร์ - JavaScript คำแนะนำที่ชัดเจนโดย David Flanagan
ฟังก์ชั่นส่วนใหญ่ถูกเรียกใช้โดยใช้ขอบเขตลูกโซ่เดียวกันที่มีผลเมื่อฟังก์ชั่นถูกกำหนดและมันไม่สำคัญว่าจะมีการปิดที่เกี่ยวข้อง การปิดเป็นเรื่องที่น่าสนใจเมื่อมีการเรียกใช้ภายใต้ขอบเขตของขอบเขตที่แตกต่างจากที่ระบุไว้ สิ่งนี้เกิดขึ้นได้บ่อยที่สุดเมื่อวัตถุฟังก์ชันที่ซ้อนกันถูกส่งคืนจากฟังก์ชันที่มันถูกกำหนดไว้
เมื่อฟังก์ชันส่งคืนวัตถุการเปิดใช้งานนั้นจะถูกลบออกจากห่วงโซ่ขอบเขต หากไม่มีฟังก์ชั่นซ้อนกันจะไม่มีการอ้างอิงไปยังวัตถุการเปิดใช้งานอีกต่อไปและจะได้รับการรวบรวมขยะ หากมีฟังก์ชั่นที่ซ้อนกันที่กำหนดไว้แล้วแต่ละฟังก์ชั่นเหล่านั้นมีการอ้างอิงถึงห่วงโซ่ขอบเขตและห่วงโซ่ขอบเขตนั้นหมายถึงวัตถุเปิดใช้งาน
หากวัตถุที่ซ้อนกันเหล่านั้นยังคงอยู่ในฟังก์ชั่นด้านนอกของพวกเขาแล้วพวกเขาเองจะถูกเก็บรวบรวมขยะพร้อมกับวัตถุการเปิดใช้งานที่พวกเขาอ้างถึง แต่ถ้าฟังก์ชั่นกำหนดฟังก์ชั่นที่ซ้อนกันและส่งกลับหรือเก็บไว้ในสถานที่ให้บริการแล้วจะมีการอ้างอิงภายนอกไปยังฟังก์ชั่นที่ซ้อนกัน มันจะไม่ถูกรวบรวมขยะและวัตถุการเปิดใช้งานที่อ้างถึงจะไม่ถูกเก็บขยะด้วยเช่นกัน
ในตัวอย่างข้างต้นของเราเราไม่ได้กลับf2
จากf1
ดังนั้นเมื่อมีการเรียกร้องให้f1
ผลตอบแทนวัตถุการเปิดใช้งานจะถูกลบออกจากห่วงโซ่ขอบเขตและเก็บขยะ แต่ถ้าเรามีบางอย่างเช่นนี้:
function f1(x) {
function f2(y) {
// ... something
}
return f2;
}
ที่นี่การส่งคืนf2
จะมีห่วงโซ่ขอบเขตที่จะมีวัตถุการเปิดใช้งานf1
และด้วยเหตุนี้มันจะไม่ถูกรวบรวมขยะ ณ จุดนี้ถ้าเราเรียกf2
มันจะสามารถเข้าถึงf1
ตัวแปรของx
เราได้แม้ว่าเราจะไม่อยู่f1
ก็ตาม
ดังนั้นเราจะเห็นได้ว่าฟังก์ชั่นเก็บรักษาขอบเขตของห่วงโซ่ไว้ด้วยและด้วยห่วงโซ่ขอบเขตจะมีวัตถุการเปิดใช้งานทั้งหมดของฟังก์ชั่นด้านนอก นี่คือสาระสำคัญของการปิด เราบอกว่าฟังก์ชั่นในจาวาสคริปต์เป็น"ขอบเขตขอบเขต"ซึ่งหมายความว่าพวกเขาบันทึกขอบเขตที่แอ็คทีฟเมื่อถูกกำหนดเมื่อเทียบกับขอบเขตที่แอ็คทีฟเมื่อถูกเรียก
มีเทคนิคการเขียนโปรแกรมที่ทรงพลังมากมายที่เกี่ยวข้องกับการปิดเช่นการประมาณตัวแปรส่วนตัวการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์การใช้งานบางส่วนเป็นต้น
นอกจากนี้โปรดทราบว่าทั้งหมดนี้ใช้กับภาษาทั้งหมดที่สนับสนุนการปิด ตัวอย่างเช่น PHP (5.3+), Python, Ruby และอื่น ๆ
การปิดคือการเพิ่มประสิทธิภาพคอมไพเลอร์ (หรือที่รู้จักว่าน้ำตาล syntactic?) บางคนเรียกสิ่งนี้ว่าเป็นวัตถุของชายผู้น่าสงสารเช่นกัน
ดูคำตอบของ Eric Lippert : (ตัดตอนมาด้านล่าง)
คอมไพเลอร์จะสร้างรหัสดังนี้:
private class Locals
{
public int count;
public void Anonymous()
{
this.count++;
}
}
public Action Counter()
{
Locals locals = new Locals();
locals.count = 0;
Action counter = new Action(locals.Anonymous);
return counter;
}
ทำให้รู้สึก?
นอกจากนี้คุณขอเปรียบเทียบ VB และ JScript ทั้งคู่สร้างการปิดในลักษณะเดียวกัน