JavaScript ปิดกับฟังก์ชั่นที่ไม่ระบุชื่อ


562

เพื่อนของฉันและฉันกำลังพูดถึงสิ่งที่ปิดใน JS และสิ่งที่ไม่ เราแค่ต้องการให้แน่ใจว่าเราเข้าใจมันอย่างถูกต้อง

ลองดูตัวอย่างนี้ เรามีการนับลูปและต้องการพิมพ์ตัวแปรตัวนับบนคอนโซลล่าช้า ดังนั้นเราจึงใช้setTimeoutและปิดเพื่อจับค่าของตัวแปรตัวนับเพื่อให้แน่ใจว่ามันจะไม่พิมพ์ N คูณค่า N

วิธีการแก้ปัญหาที่ผิดโดยไม่ปิดหรืออะไรก็ตามที่ใกล้กับการปิดคือ:

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

ซึ่งแน่นอนว่าจะพิมพ์ค่าของiลูปหลัง10 เท่าคือ 10

ดังนั้นความพยายามของเขาคือ:

for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i;
        setTimeout(function(){
            console.log(i2);
        }, 1000)
    })();
}

พิมพ์ 0 ถึง 9 ตามที่คาดไว้

ฉันบอกเขาว่าเขาไม่ได้ใช้การปิดเพื่อจับภาพiแต่เขายืนยันว่าเขาเป็น ฉันพิสูจน์ว่าเขาไม่ได้ใช้การปิดโดยการใส่สำหรับลูปบอดี้ภายในอีกอันsetTimeout(ผ่านฟังก์ชั่นนิรนามของเขาไปที่setTimeout) โดยพิมพ์ 10 ครั้ง 10 อีกครั้ง เช่นเดียวกับถ้าผมเก็บฟังก์ชั่นของเขาในvarและดำเนินการได้หลังจากที่วงยังพิมพ์ 10 ครั้ง 10. ดังนั้นอาร์กิวเมนต์ของฉันคือการที่เขาไม่ได้จริงๆจับค่าของการiทำให้รุ่นของเขาไม่ได้ปิด

ความพยายามของฉันคือ:

for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2);
        }
    })(i), 1000);
}

ดังนั้นฉันจึงจับi(ตั้งชื่อi2ภายในการปิด) แต่ตอนนี้ฉันกลับฟังก์ชั่นอื่นและส่งผ่านสิ่งนี้ ในกรณีของฟังก์ชั่นที่ส่งผ่านไป setTimeout iจับจริงๆ

ตอนนี้ใครกำลังใช้การปิดและใครไม่ได้

โปรดทราบว่าโซลูชันทั้งสองพิมพ์ 0 ถึง 9 บนคอนโซลล่าช้าดังนั้นพวกเขาจึงแก้ปัญหาเดิม แต่เราต้องการเข้าใจว่าโซลูชันสองรายการใดที่ใช้การปิดเพื่อให้บรรลุสิ่งนี้



1
@ เลย์เอาต์: ดูการแก้ไขนินจาของฉันสำหรับลิงก์ที่สอง
เครื่องปั่น

2
เราเพิ่งทำข้อตกลง: ผู้ที่มีสิทธิ์จะได้รับคะแนน SO ที่เกี่ยวข้องกับคำถามนี้
brillout

1
@leemes - คุณทั้งคู่กำลังใช้การปิด คุณทั้งคู่ทำสองฟังก์ชั่น - ฟังก์ชั่นด้านนอกและฟังก์ชั่นภายใน และฟังก์ชั่นภายในทั้งสองของคุณปิด ฟังก์ชั่นทั้งหมดของคุณเป็น lambdas ( ฟังก์ชั่นที่ไม่ระบุชื่อ ) อ่านคำตอบของฉันสำหรับรายละเอียด
Aadit M Shah

1
@blesh - ฉันไม่รู้เลยว่าการปิดที่ได้รับการแก้ไขคืออะไร ฉันเห็นว่าลิงก์ของคุณชี้ไปที่รหัส C # JavaScript ได้รับการสนับสนุนการแก้ไขมีการปิดแล้วหรือยัง?
Aadit M Shah

คำตอบ:


650

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

คำอธิบายง่ายๆสำหรับการปิด:

  1. ใช้ฟังก์ชั่น เรียกมันว่าเอฟ
  2. แสดงรายการตัวแปรทั้งหมดของ F
  3. ตัวแปรอาจมีสองประเภท:
    1. ตัวแปรท้องถิ่น (ตัวแปรที่ถูกผูกไว้)
    2. ตัวแปรที่ไม่ใช่ท้องถิ่น (ตัวแปรอิสระ)
  4. ถ้า F ไม่มีตัวแปรอิสระมันจะไม่สามารถปิดได้
  5. ถ้า F มีตัวแปรใด ๆ ฟรี (ซึ่งถูกกำหนดไว้ในขอบเขตแม่ของ F) แล้ว:
    1. ต้องมีขอบเขตเพียงผู้ปกครองหนึ่ง F ถึงซึ่งตัวแปรอิสระที่ถูกผูกไว้
    2. ถ้า F จะอ้างอิงจากภายนอกว่าขอบเขตของพ่อแม่แล้วจะกลายเป็นปิดหาว่าตัวแปรฟรี
    3. ว่าตัวแปรอิสระที่เรียกว่า upvalue ของการปิดเอฟ

ตอนนี้ลองใช้สิ่งนี้เพื่อหาว่าใครใช้การปิดและไม่ใช้ (เพื่อคำอธิบายฉันได้ตั้งชื่อฟังก์ชั่น):

กรณีที่ 1: โปรแกรมเพื่อนของคุณ

for (var i = 0; i < 10; i++) {
    (function f() {
        var i2 = i;
        setTimeout(function g() {
            console.log(i2);
        }, 1000);
    })();
}

ในโปรแกรมดังกล่าวข้างต้นมีสองฟังก์ชั่น: และf gมาดูกันว่าพวกมันปิดอยู่หรือไม่:

สำหรับf:

  1. รายการตัวแปร:
    1. i2เป็นตัวแปรท้องถิ่น
    2. iเป็นตัวแปรอิสระ
    3. setTimeoutเป็นตัวแปรอิสระ
    4. gเป็นตัวแปรท้องถิ่น
    5. consoleเป็นตัวแปรอิสระ
  2. ค้นหาขอบเขตพาเรนต์ที่แต่ละตัวแปรอิสระถูกผูก:
    1. iถูกผูกไว้กับขอบเขตส่วนกลาง
    2. setTimeoutถูกผูกไว้กับขอบเขตส่วนกลาง
    3. consoleถูกผูกไว้กับขอบเขตส่วนกลาง
  3. ฟังก์ชันอ้างอิงในขอบเขตใด ขอบเขตทั่วโลก
    1. ดังนั้นiไม่ได้ปิดมากกว่าfโดย
    2. ดังนั้นsetTimeoutไม่ได้ปิดมากกว่าfโดย
    3. ดังนั้นconsoleไม่ได้ปิดมากกว่าfโดย

ดังนั้นฟังก์ชั่นfนี้ไม่ได้เป็นการปิด

สำหรับg:

  1. รายการตัวแปร:
    1. consoleเป็นตัวแปรอิสระ
    2. i2เป็นตัวแปรอิสระ
  2. ค้นหาขอบเขตพาเรนต์ที่แต่ละตัวแปรอิสระถูกผูก:
    1. consoleถูกผูกไว้กับขอบเขตส่วนกลาง
    2. i2จะผูกพันfขอบเขตของ
  3. ฟังก์ชันอ้างอิงในขอบเขตใด ขอบเขตของ setTimeout
    1. ดังนั้นconsoleไม่ได้ปิดมากกว่าgโดย
    2. ดังนั้นi2จะปิดมากกว่าgโดย

ดังนั้นฟังก์ชั่นgคือการปิดสำหรับตัวแปรอิสระi2(ซึ่งเป็น upvalue สำหรับg) เมื่อมันอ้างอิงsetTimeoutจากภายใน

ไม่ดีสำหรับคุณ:เพื่อนของคุณกำลังปิดตัว ฟังก์ชั่นภายในคือการปิด

กรณีที่ 2: โปรแกรมของคุณ

for (var i = 0; i < 10; i++) {
    setTimeout((function f(i2) {
        return function g() {
            console.log(i2);
        };
    })(i), 1000);
}

ในโปรแกรมดังกล่าวข้างต้นมีสองฟังก์ชั่น: และf gมาดูกันว่าพวกมันปิดอยู่หรือไม่:

สำหรับf:

  1. รายการตัวแปร:
    1. i2เป็นตัวแปรท้องถิ่น
    2. gเป็นตัวแปรท้องถิ่น
    3. consoleเป็นตัวแปรอิสระ
  2. ค้นหาขอบเขตพาเรนต์ที่แต่ละตัวแปรอิสระถูกผูก:
    1. consoleถูกผูกไว้กับขอบเขตส่วนกลาง
  3. ฟังก์ชันอ้างอิงในขอบเขตใด ขอบเขตทั่วโลก
    1. ดังนั้นconsoleไม่ได้ปิดมากกว่าfโดย

ดังนั้นฟังก์ชั่นfนี้ไม่ได้เป็นการปิด

สำหรับg:

  1. รายการตัวแปร:
    1. consoleเป็นตัวแปรอิสระ
    2. i2เป็นตัวแปรอิสระ
  2. ค้นหาขอบเขตพาเรนต์ที่แต่ละตัวแปรอิสระถูกผูก:
    1. consoleถูกผูกไว้กับขอบเขตส่วนกลาง
    2. i2จะผูกพันfขอบเขตของ
  3. ฟังก์ชันอ้างอิงในขอบเขตใด ขอบเขตของ setTimeout
    1. ดังนั้นconsoleไม่ได้ปิดมากกว่าgโดย
    2. ดังนั้นi2จะปิดมากกว่าgโดย

ดังนั้นฟังก์ชั่นgคือการปิดสำหรับตัวแปรอิสระi2(ซึ่งเป็น upvalue สำหรับg) เมื่อมันอ้างอิงsetTimeoutจากภายใน

ดีสำหรับคุณ:คุณกำลังปิดตัว ฟังก์ชั่นภายในคือการปิด

ดังนั้นทั้งคุณและเพื่อนของคุณกำลังใช้การปิด หยุดเถียง ฉันหวังว่าฉันจะล้างแนวคิดของการปิดและวิธีการระบุพวกเขาสำหรับคุณทั้งคู่

แก้ไข:คำอธิบายง่ายๆว่าทำไมฟังก์ชั่นทั้งหมดถึงปิด (เครดิต @Peter):

ก่อนอื่นมาลองพิจารณาโปรแกรมต่อไปนี้ (มันเป็นตัวควบคุม ):

lexicalScope();

function lexicalScope() {
    var message = "This is the control. You should be able to see this message being alerted.";

    regularFunction();

    function regularFunction() {
        alert(eval("message"));
    }
}

  1. เรารู้ว่าทั้งสองlexicalScopeและregularFunctionไม่ได้ปิดจากนิยามดังกล่าวข้างต้น
  2. เมื่อเรารันโปรแกรมที่เราคาดว่า messageจะได้รับการแจ้งเตือนเนื่องจาก regularFunctionไม่ใช่การปิด (เช่นมีการเข้าถึงตัวแปรทั้งหมดในขอบเขตหลัก - รวมถึงmessage)
  3. เมื่อเรารันโปรแกรมที่เราสังเกตเห็นว่าmessageมีการแจ้งเตือนแน่นอน

ถัดไปลองพิจารณาโปรแกรมต่อไปนี้ (เป็นทางเลือก ):

var closureFunction = lexicalScope();

closureFunction();

function lexicalScope() {
    var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";

    return function closureFunction() {
        alert(eval("message"));
    };
}

  1. เรารู้ว่ามีเพียงclosureFunctionคือการปิดจากนิยามดังกล่าวข้างต้น
  2. เมื่อเรารันโปรแกรมที่เราคาดว่าจะ messageไม่ได้รับการแจ้งเตือนเนื่องจาก closureFunctionเป็นการปิด (นั่นคือมีเพียงการเข้าถึงตัวแปรที่ไม่ใช่ท้องถิ่นทั้งหมดในเวลาที่ฟังก์ชั่นถูกสร้างขึ้น ( ดูคำตอบนี้ ) - นี้ไม่รวมmessage)
  3. เมื่อเรารันโปรแกรมที่เราสังเกตเห็นว่าmessageมีการแจ้งเตือนจริง

เราสรุปอะไรจากสิ่งนี้

  1. ล่าม JavaScript ไม่ได้ปิดการทำงานที่แตกต่างจากวิธีที่พวกเขาปฏิบัติหน้าที่อื่น ๆ
  2. ทุกฟังก์ชั่นมีห่วงโซ่ขอบเขตพร้อมกับมัน การปิดไม่ได้มีสภาพแวดล้อมการอ้างอิงแยกต่างหาก
  3. การปิดเป็นเหมือนทุกฟังก์ชั่นอื่น ๆ เราเพียงแค่เรียกพวกเขาปิดเมื่อพวกเขาถูกอ้างอิงในขอบเขตนอกขอบเขตที่พวกเขาอยู่เพราะเป็นกรณีที่น่าสนใจ

40
ยอมรับเพราะคุณทำรายละเอียดได้ดีมากอธิบายได้ดีว่าเกิดอะไรขึ้น และในที่สุดตอนนี้ฉันเข้าใจดียิ่งขึ้นว่าการปิดคืออะไรหรือดีกว่าพูดว่า: การผูกผันของตัวแปรใน JS เป็นอย่างไร
leemes

3
ในกรณีที่ 1 คุณพูดว่าgทำงานในขอบเขตของsetTimeoutแต่ในกรณีที่ 2 คุณบอกว่าfทำงานในขอบเขตทั่วโลก พวกเขาทั้งคู่อยู่ใน setTimeout ดังนั้นอะไรคือความแตกต่าง
rosscj2533

9
คุณจะโปรดระบุแหล่งที่มาของคุณสำหรับสิ่งนี้หรือไม่? ฉันไม่เคยเห็นคำจำกัดความที่ฟังก์ชั่นอาจเป็นการปิดถ้าเรียกในขอบเขตหนึ่ง แต่ไม่ใช่ในอีกขอบเขต ดังนั้นคำจำกัดความนี้ดูเหมือนว่าเป็นส่วนหนึ่งของคำจำกัดความทั่วไปที่ฉันคุ้นเคย (ดูคำตอบของ kev ) ที่การปิดคือการปิดเป็นการปิดโดยไม่คำนึงถึงขอบเขตที่เรียกว่าหรือแม้กระทั่งว่ามันไม่เคยถูกเรียก!
Briguy37

11
@AititMShah ฉันเห็นด้วยกับคุณเกี่ยวกับสิ่งที่ปิด แต่คุณพูดราวกับว่ามีความแตกต่างระหว่างฟังก์ชั่นปกติและการปิดใน JavaScript ไม่มีความแตกต่าง; ภายในทุก ๆ ฟังก์ชั่นจะมีการอ้างอิงถึงห่วงโซ่ขอบเขตเฉพาะที่มันถูกสร้างขึ้น เอ็นจิน JS ไม่พิจารณาว่าเป็นกรณีอื่น ไม่จำเป็นต้องมีรายการตรวจสอบที่ซับซ้อน เพิ่งรู้ว่าทุกฟังก์ชั่นวัตถุมีขอบเขตคำศัพท์ ความจริงที่ว่าตัวแปร / คุณสมบัตินั้นพร้อมใช้งานทั่วโลกไม่ได้ทำให้ฟังก์ชั่นการปิดน้อยลง
ปีเตอร์

13
@Peter - คุณรู้อะไรถูกต้อง ไม่มีความแตกต่างระหว่างฟังก์ชั่นปกติและการปิด ฉันวิ่งทดสอบเพื่อพิสูจน์เรื่องนี้และมันจะส่งผลในความโปรดปรานของคุณ: นี่คือการควบคุมและนี่เป็นอีกทางเลือกหนึ่ง สิ่งที่คุณพูดไม่สมเหตุสมผล ล่าม JavaScript ต้องทำบัญชีพิเศษสำหรับการปิด พวกมันเป็นผลพลอยได้จากภาษาที่ จำกัด ขอบเขตด้วยฟังก์ชันชั้นหนึ่ง ความรู้ของฉันถูก จำกัด ในสิ่งที่ฉันอ่าน (ซึ่งเป็นเท็จ) ขอบคุณสำหรับการแก้ไขฉัน ฉันจะอัปเดตคำตอบของฉันเพื่อสะท้อนถึงสิ่งเดียวกัน
Aadit M Shah

96

ตามclosureคำนิยาม:

"closure" คือนิพจน์ (โดยทั่วไปคือฟังก์ชัน) ที่สามารถมีตัวแปรอิสระพร้อมกับสภาพแวดล้อมที่ผูกตัวแปรเหล่านั้น (ซึ่ง "ปิด" นิพจน์)

คุณกำลังใช้closureถ้าคุณกำหนดฟังก์ชั่นที่ใช้ตัวแปรที่กำหนดไว้ด้านนอกของฟังก์ชั่น (เราเรียกตัวแปรว่าเป็นตัวแปรอิสระ )
พวกเขาทั้งหมดใช้closure(แม้ในตัวอย่างที่ 1)


1
รุ่นที่สามใช้ตัวแปรที่กำหนดไว้นอกฟังก์ชั่นได้อย่างไร
Jon

1
@ จอนใช้ฟังก์ชันที่ส่งคืนi2ซึ่งถูกกำหนดไว้ภายนอก
kev

1
@kev คุณกำลังใช้การปิดถ้าคุณกำหนดฟังก์ชั่นที่ใช้ตัวแปรที่กำหนดไว้นอกฟังก์ชั่น ...... จากนั้นใน "กรณีที่ 1: โปรแกรมเพื่อนของคุณ" ของ "Aadit M Shah" คำตอบคือ "ฟังก์ชั่น f" ปิดหรือไม่ มันใช้ i (ตัวแปรที่กำหนดไว้นอกฟังก์ชั่น) ขอบเขตทั่วโลกอ้างอิงตัวกำหนดหรือไม่
internals-in


54

สรุปJavascript ปิดอนุญาตให้มีฟังก์ชั่นการเข้าถึงตัวแปรที่ประกาศไว้ในฟังก์ชั่นคำศัพท์ผู้ปกครอง

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

ขอบเขต

ในขอบเขต JavaScript ถูกกำหนดด้วยฟังก์ชัน ทุกฟังก์ชั่นจะกำหนดขอบเขตใหม่

ลองพิจารณาตัวอย่างต่อไปนี้

function f()
{//begin of scope f
  var foo='hello'; //foo is declared in scope f
  for(var i=0;i<2;i++){//i is declared in scope f
     //the for loop is not a function, therefore we are still in scope f
     var bar = 'Am I accessible?';//bar is declared in scope f
     console.log(foo);
  }
  console.log(i);
  console.log(bar);
}//end of scope f

เรียก f พิมพ์

hello
hello
2
Am I Accessible?

ตอนนี้ขอให้พิจารณากรณีที่เราได้ฟังก์ชั่นที่กำหนดไว้ในฟังก์ชั่นอื่นgf

function f()
{//begin of scope f
  function g()
  {//being of scope g
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

เราจะเรียกปกครองศัพท์ของ ตามที่อธิบายไว้ก่อนหน้านี้เรามี 2 ขอบเขต; ขอบเขตและขอบเขตfgfg

แต่ขอบเขตหนึ่งคือ "ภายใน" ขอบเขตอื่นดังนั้นขอบเขตของฟังก์ชันลูกเป็นส่วนหนึ่งของขอบเขตของฟังก์ชันหลักหรือไม่ เกิดอะไรขึ้นกับตัวแปรที่ประกาศในขอบเขตของฟังก์ชันพาเรนต์ ฉันจะสามารถเข้าถึงพวกเขาจากขอบเขตของฟังก์ชันลูกได้หรือไม่ นั่นคือสิ่งที่ขั้นตอนการปิด

ปิด

ใน JavaScript ฟังก์ชั่นgไม่เพียง แต่สามารถเข้าถึงตัวแปรใด ๆ ที่ประกาศในขอบเขตgแต่ยังเข้าถึงตัวแปรใด ๆ fที่ประกาศในขอบเขตของฟังก์ชั่นแม่

พิจารณาดังนี้

function f()//lexical parent function
{//begin of scope f
  var foo='hello'; //foo declared in scope f
  function g()
  {//being of scope g
    var bar='bla'; //bar declared in scope g
    console.log(foo);
  }//end of scope g
  g();
  console.log(bar);
}//end of scope f

เรียก f พิมพ์

hello
undefined

ดู Let 's console.log(foo);ที่บรรทัด ณ จุดนี้เราอยู่ในขอบเขตgและเราพยายามที่จะเข้าถึงตัวแปรที่ประกาศอยู่ในขอบเขตfoo fแต่ตามที่ระบุไว้ก่อนที่เราจะสามารถเข้าถึงตัวแปรใด ๆ ที่ประกาศในฟังก์ชั่นผู้ปกครองศัพท์ซึ่งเป็นกรณีที่นี่; เป็นผู้ปกครองของคำศัพท์g fดังนั้นhelloจะถูกพิมพ์ ตอนนี้ขอให้ดูที่บรรทัด
console.log(bar);ณ จุดนี้เราอยู่ในขอบเขตfและเราพยายามที่จะเข้าถึงตัวแปรที่ประกาศอยู่ในขอบเขตbar ไม่ได้ประกาศในขอบเขตปัจจุบันและฟังก์ชั่นไม่ได้เป็นพาเรนต์ของดังนั้นจึงไม่ได้กำหนดgbargfbar

ที่จริงแล้วเราสามารถเข้าถึงตัวแปรที่ประกาศในขอบเขตของฟังก์ชัน "ผู้ปกครองหลัก" ศัพท์ ดังนั้นหากจะมีฟังก์ชั่นที่hกำหนดไว้ภายในฟังก์ชั่นg

function f()
{//begin of scope f
  function g()
  {//being of scope g
    function h()
    {//being of scope h
      /*...*/
    }//end of scope h
    /*...*/
  }//end of scope g
  /*...*/
}//end of scope f

แล้วhจะสามารถเข้าถึงตัวแปรทั้งหมดที่ประกาศในขอบเขตของฟังก์ชั่นh, และg fนี้จะกระทำด้วยการปิด ในการปิด JavaScript ช่วยให้เราสามารถเข้าถึงตัวแปรใด ๆ ที่ประกาศในฟังก์ชั่นผู้ปกครองศัพท์, ในฟังก์ชั่นผู้ปกครองระดับสูง, ในฟังก์ชั่นผู้ปกครองระดับสูงศัพท์ ฯลฯ นี้สามารถมองเห็นเป็นห่วงโซ่ขอบเขต ; scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ... จนกระทั่งฟังก์ชันพาเรนต์สุดท้ายที่ไม่มีพาเรนต์ศัพท์

วัตถุหน้าต่าง

อันที่จริงเชนไม่ได้หยุดที่ฟังก์ชันพาเรนต์สุดท้าย มีขอบเขตพิเศษอีกหนึ่งข้อ ขอบเขตทั่วโลก ทุกตัวแปรที่ไม่ได้ประกาศในฟังก์ชั่นนั้นจะถูกประกาศให้อยู่ในขอบเขตส่วนกลาง ขอบเขตทั่วโลกมีความเชี่ยวชาญสองประการ

  • ทุกตัวแปรที่ประกาศในขอบเขตทั่วโลกสามารถเข้าถึงได้ทุกที่
  • ตัวแปรที่ประกาศในขอบเขตทั่วโลกสอดคล้องกับคุณสมบัติของwindowวัตถุ

ดังนั้นจึงมีสองวิธีในการประกาศตัวแปรfooในขอบเขตส่วนกลาง โดยไม่ได้ประกาศในฟังก์ชั่นหรือโดยการตั้งค่าคุณสมบัติfooของวัตถุหน้าต่าง

ทั้งสองพยายามใช้การปิด

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

มาสร้างภาษาโปรแกรมใหม่กันเถอะ; ใช้ JavaScript ไม่มีปิด ตามชื่อที่แนะนำ JavaScript-No-Off จะเหมือนกับ JavaScript ยกเว้นว่าจะไม่รองรับการปิด

กล่าวอีกนัยหนึ่ง;

var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello

เอาล่ะเรามาดูกันว่าเกิดอะไรขึ้นกับโซลูชั่นแรกด้วย JavaScript-No-Off

for(var i = 0; i < 10; i++) {
  (function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2); //i2 is undefined in JavaScript-No-Closure 
    }, 1000)
  })();
}

ดังนั้นสิ่งนี้จะพิมพ์undefined10 ครั้งใน JavaScript-No-Off

ดังนั้นทางออกแรกใช้การปิด

ลองดูวิธีแก้ปัญหาที่สอง;

for(var i = 0; i < 10; i++) {
  setTimeout((function(i2){
    return function() {
        console.log(i2); //i2 is undefined in JavaScript-No-Closure
    }
  })(i), 1000);
}

ดังนั้นสิ่งนี้จะพิมพ์undefined10 ครั้งใน JavaScript-No-Off

โซลูชันทั้งสองใช้การปิด

แก้ไข: สันนิษฐานว่าตัวอย่างโค้ด 3 ตัวนี้ไม่ได้กำหนดไว้ในขอบเขตส่วนกลาง มิฉะนั้นตัวแปรfooและiจะผูกกับwindowวัตถุและสามารถเข้าถึงได้ผ่านwindowวัตถุทั้งใน JavaScript และ JavaScript-No-Off


ทำไมไม่ควรiกำหนด? คุณเพียงแค่อ้างถึงขอบเขตพาเรนต์ซึ่งยังคงใช้ได้หากไม่มีการปิด
leemes

ด้วยเหตุผลเดียวกันกับ foo ที่ไม่ได้กำหนดใน JavaScript-No-Off <code> i </code> ไม่ได้ถูกกำหนดใน JavaScript ด้วยคุณสมบัติใน JavaScript ที่ช่วยให้สามารถเข้าถึงตัวแปรที่กำหนดไว้ใน parent lexical คุณสมบัตินี้เรียกว่าการปิด
brillout

คุณไม่เข้าใจความแตกต่างระหว่างการอ้างอิงถึงตัวแปรที่กำหนดไว้แล้วและตัวแปรอิสระ ในการปิดเรากำหนดตัวแปรอิสระที่จะต้องถูกผูกไว้ในบริบทด้านนอก ในรหัสของคุณคุณเพียงแค่ตั้งค่า i2ไปiในช่วงเวลาที่คุณกำหนดฟังก์ชั่นของคุณ สิ่งนี้ทำให้iไม่ใช่ตัวแปรอิสระ แต่ถึงกระนั้นเราจะพิจารณาฟังก์ชั่นการปิดของคุณ แต่ไม่มีตัวแปรอิสระนั่นคือประเด็น
leemes

2
@leemes ฉันเห็นด้วย และเมื่อเปรียบเทียบกับคำตอบที่ยอมรับแล้วนี่ไม่ได้แสดงว่าเกิดอะไรขึ้นจริง ๆ :)
Abel

3
ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุดอธิบายการปิดโดยทั่วไปและจากนั้นก็เข้าสู่กรณีการใช้งานเฉพาะ ขอบคุณ!
ทิมปีเตอร์สัน

22

ฉันไม่เคยมีความสุขกับวิธีที่ทุกคนอธิบายเรื่องนี้

กุญแจสำคัญในการทำความเข้าใจการปิดคือการเข้าใจสิ่งที่ JS จะเป็นเหมือนไม่มีการปิด

หากไม่มีการปิดนี่จะทำให้เกิดข้อผิดพลาด

function outerFunc(){
    var outerVar = 'an outerFunc var';
    return function(){
        alert(outerVar);
    }
}

outerFunc()(); //returns inner function and fires it

เมื่อ outerFunc กลับมาใน JavaScript เวอร์ชันปิดการใช้งานในจินตนาการการอ้างอิงไปยัง outerVar จะถูกรวบรวมขยะและไม่ได้ทิ้งสิ่งใดไว้ที่ func ภายในเพื่ออ้างอิง

การปิดเป็นหลักกฎพิเศษที่เตะเข้าและทำให้มันเป็นไปได้สำหรับ vars เหล่านั้นที่จะอยู่เมื่อฟังก์ชั่นภายในอ้างอิงตัวแปรของฟังก์ชั่นด้านนอก ด้วยการปิด vars ที่อ้างอิงจะถูกเก็บรักษาไว้แม้หลังจากฟังก์ชั่นด้านนอกเสร็จสิ้นหรือ 'ปิด' หากช่วยให้คุณจดจำจุดได้

แม้จะมีการปิดวงจรชีวิตของ vars ท้องถิ่นในฟังก์ชั่นที่ไม่มีฟังก์ชั่นภายในที่อ้างอิงคนในท้องถิ่นของมันทำงานเช่นเดียวกับในเวอร์ชันที่ปิดน้อยกว่า เมื่อฟังก์ชั่นเสร็จสิ้นชาวบ้านจะได้รับการเก็บขยะ

เมื่อคุณมีการอ้างอิงใน func ภายในเพื่อ var ภายนอก แต่มันก็เหมือน doorjamb ได้รับการวางในทางของการเก็บขยะสำหรับ vars อ้างอิงเหล่านั้น

วิธีที่แม่นยำกว่าในการดูการปิดคือฟังก์ชั่นด้านในใช้ขอบเขตด้านในเป็นขอบเขตของตัวเอง foudnation

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

function outerFunc(){
    var incrementMe = 0;
    return function(){ incrementMe++; console.log(incrementMe); }
}
var inc = outerFunc();
inc(); //logs 1
inc(); //logs 2

คุณพูดถูกเกี่ยวกับ 'ภาพรวม' (ฉันคิดว่าคุณหมายถึงคำตอบของฉัน) โดยที่ ฉันกำลังมองหาคำที่จะบ่งบอกถึงพฤติกรรม ในตัวอย่างของคุณมันสามารถถูกมองว่าเป็นโครงสร้างการปิด 'ฮอตลิงค์' เมื่อจับการปิดเป็นพารามิเตอร์ในฟังก์ชั่นด้านในหนึ่งสามารถระบุว่ามันทำงานเป็น 'ภาพรวม' แต่ฉันเห็นด้วยคำที่ใช้ผิดเท่านั้นเพิ่มความสับสนให้กับเรื่อง หากคุณมีข้อเสนอแนะใด ๆ เกี่ยวกับสิ่งนั้นฉันจะอัปเดตคำตอบของฉัน
Andries

มันอาจช่วยอธิบายถ้าคุณให้ฟังก์ชั่นด้านในเป็นฟังก์ชั่นที่มีชื่อ
Phillip Senn

หากไม่มีการปิดคุณจะได้รับข้อผิดพลาดเนื่องจากคุณพยายามใช้ตัวแปรที่ไม่มีอยู่
Juan Mendes

อืม ... จุดดี การอ้างอิง var ที่ไม่ได้กำหนดไม่เคยส่งข้อผิดพลาดเนื่องจากท้ายที่สุดแล้วมันจะค้นหาคุณสมบัติในวัตถุทั่วโลกหรือฉันสับสนกับการมอบหมายให้ vars ที่ไม่ได้กำหนดหรือไม่?
Erik Reppen

17

คุณทั้งคู่ใช้การปิด

ฉันจะไปกับคำนิยามของวิกิพีเดียที่นี่:

ในวิทยาการคอมพิวเตอร์การปิด (เช่นการปิดคำศัพท์หรือการปิดฟังก์ชั่น) เป็นฟังก์ชั่นหรือการอ้างอิงไปยังฟังก์ชั่นร่วมกับสภาพแวดล้อมอ้างอิง - ตารางที่จัดเก็บการอ้างอิงถึงตัวแปรที่ไม่ใช่ท้องถิ่น (เรียกว่าตัวแปรอิสระ) ของฟังก์ชันนั้น . การปิด - ไม่เหมือนกับตัวชี้ฟังก์ชันธรรมดา - อนุญาตให้ฟังก์ชั่นเข้าถึงตัวแปรที่ไม่ได้อยู่ในเครื่องแม้ว่าจะถูกเรียกใช้นอกขอบเขตของคำศัพท์ทันที

ความพยายามของเพื่อนของคุณได้อย่างชัดเจนใช้ตัวแปรซึ่งเป็นท้องถิ่นที่ไม่ใช่โดยการใช้ความคุ้มค่าและการทำสำเนาไปยังร้านค้าเข้าสู่ท้องถิ่นii2

ความพยายามของคุณเองผ่านi(ซึ่งในไซต์การโทรอยู่ในขอบเขต) ไปยังฟังก์ชันที่ไม่ระบุชื่อเป็นอาร์กิวเมนต์ นี้ไม่ได้ปิดเพื่อให้ห่างไกล แต่แล้วฟังก์ชั่นที่ส่งกลับฟังก์ชั่นอื่น ๆ i2ที่อ้างอิงเดียวกัน เนื่องจากภายในฟังก์ชันที่ไม่ระบุตัวตนด้านในi2ไม่ใช่ของท้องถิ่นจึงสร้างการปิด


ใช่ แต่ฉันคิดว่าประเด็นคือวิธีที่เขาทำมัน เขาเพิ่งคัดลอกiไปยังi2จากนั้นกำหนดตรรกะบางอย่างและดำเนินการฟังก์ชันนี้ ถ้าฉันจะไม่ดำเนินการทันที แต่เก็บไว้ใน var และดำเนินการหลังจากวนรอบมันจะพิมพ์ 10 ใช่ไหม? ดังนั้นจึงไม่ได้จับฉัน
leemes

6
@ เลย์เอาต์: มันจับได้iดี พฤติกรรมที่คุณกำลังอธิบายไม่ใช่ผลลัพธ์ของการปิด vs การไม่ปิด มันเป็นผลมาจากตัวแปรปิดที่มีการเปลี่ยนแปลงในระหว่างนี้ คุณกำลังทำสิ่งเดียวกันโดยใช้ไวยากรณ์ที่แตกต่างกันโดยการเรียกใช้ฟังก์ชันและส่งผ่านiเป็นอาร์กิวเมนต์ทันที (ซึ่งคัดลอกค่าปัจจุบันไปที่จุดนั้น) หากคุณใส่ของคุณเองsetTimeoutในอีกsetTimeoutสิ่งเดียวกันจะเกิดขึ้น
Jon

13

คุณและเพื่อนของคุณใช้การปิด:

การปิดเป็นวัตถุชนิดพิเศษที่รวมสองสิ่ง: ฟังก์ชั่นและสภาพแวดล้อมที่ฟังก์ชันนั้นถูกสร้างขึ้น สภาพแวดล้อมประกอบด้วยตัวแปรท้องถิ่นใด ๆ ที่อยู่ในขอบเขตในเวลาที่ปิดการสร้าง

MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures

ในฟังก์ชั่นรหัสของเพื่อนของคุณfunction(){ console.log(i2); }กำหนดไว้ภายในปิดฟังก์ชั่นที่ไม่ระบุชื่อfunction(){ var i2 = i; ...และสามารถอ่าน / i2เขียนตัวแปรท้องถิ่น

ในฟังก์ชั่นรหัสของคุณfunction(){ console.log(i2); }กำหนดไว้ในการปิดฟังก์ชั่นfunction(i2){ return ...และสามารถอ่าน / เขียนที่มีคุณค่าในท้องถิ่นi2(ประกาศในกรณีนี้เป็นพารามิเตอร์)

ในทั้งสองกรณีฟังก์ชั่นผ่านไปแล้วเป็นfunction(){ console.log(i2); }setTimeout

การเทียบเท่าอื่น (แต่มีการใช้หน่วยความจำน้อยกว่า) คือ:

function fGenerator(i2){
    return function(){
        console.log(i2);
    }
}
for(var i = 0; i < 10; i++) {
    setTimeout(fGenerator(i), 1000);
}

1
ฉันไม่เห็นว่าทำไมโซลูชันของคุณกับโซลูชันของเพื่อนของฉัน "เร็วกว่าและใช้หน่วยความจำน้อยลง" คุณสามารถอธิบายเพิ่มเติมได้ไหม
brillout

3
ในการแก้ปัญหาที่คุณสร้างวัตถุฟังก์ชั่น 20 (2 วัตถุในแต่ละวง: 2x10 = 20) ผลลัพธ์เดียวกันในการแก้ปัญหาของ frend ของคุณ ในโซลูชัน "my" จะสร้างวัตถุของฟังก์ชันเพียง 11 รายการเท่านั้น: 1 ก่อนสำหรับลูปและ 10 "ภายใน" - 1 + 1x10 = 11 ผลลัพธ์ - ลดการใช้หน่วยความจำและเพิ่มความเร็ว
แอนดรูดี.

1
ในทางทฤษฎีนั้นจะเป็นจริง ในทางปฏิบัติเช่นกัน: ดูเกณฑ์มาตรฐาน JSPerf นี้: jsperf.com/closure-vs-name-function-in-a-loop/2
Rob W

10

การปิด

การปิดไม่ใช่ฟังก์ชันและไม่ใช่การแสดงออก มันจะต้องถูกมองว่าเป็น 'ภาพรวม' จากตัวแปรที่ใช้นอกฟังก์ชั่นและใช้ภายในฟังก์ชั่น ตามหลักไวยากรณ์แล้วควรพูดว่า: 'ทำการปิดตัวแปร'

อีกครั้งในคำอื่น ๆ : การปิดเป็นสำเนาของบริบทที่เกี่ยวข้องของตัวแปรที่ฟังก์ชั่นขึ้นอยู่กับ

อีกครั้ง (naïf): การปิดทำการเข้าถึงตัวแปรที่ไม่ได้ถูกส่งผ่านเป็นพารามิเตอร์

โปรดทราบว่าแนวคิดการทำงานเหล่านี้ขึ้นอยู่กับภาษาโปรแกรม / สภาพแวดล้อมที่คุณใช้ ใน JavaScript การปิดนั้นขึ้นอยู่กับการกำหนดขอบเขตศัพท์ (ซึ่งเป็นจริงในภาษา c ส่วนใหญ่)

ดังนั้นการคืนค่าฟังก์ชันส่วนใหญ่จะส่งคืนฟังก์ชันที่ไม่ระบุชื่อ / ไม่ระบุชื่อ เมื่อตัวแปรการเข้าถึงฟังก์ชั่นที่ไม่ผ่านเป็นพารามิเตอร์และภายในขอบเขต (คำศัพท์) จะมีการปิด

ดังนั้นเกี่ยวกับตัวอย่างของคุณ:

// 1
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i); // closure, only when loop finishes within 1000 ms,
    }, 1000);           // i = 10 for all functions
}
// 2
for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i; // closure of i (lexical scope: for-loop)
        setTimeout(function(){
            console.log(i2); // closure of i2 (lexical scope:outer function)
        }, 1000)
    })();
}
// 3
for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2); // closure of i2 (outer scope)

        }
    })(i), 1000); // param access i (no closure)
}

ทั้งหมดกำลังใช้การปิด อย่าสับสนระหว่างการประหารชีวิตด้วยการปิด หากมีการปิด 'snapshot' ของการปิดในเวลาที่ไม่ถูกต้องค่าอาจไม่คาดคิด แต่จะมีการปิดตัวลงอย่างแน่นอน!



10

ลองดูทั้งสองวิธี:

(function(){
    var i2 = i;
    setTimeout(function(){
        console.log(i2);
    }, 1000)
})();

ประกาศและดำเนินการฟังก์ชันที่ไม่ระบุตัวตนที่ทำงานsetTimeout()ภายในบริบทของตนเองทันที มูลค่าปัจจุบันของiจะถูกเก็บรักษาไว้โดยการทำสำเนาลงไปi2ก่อน มันทำงานได้เพราะการดำเนินการทันที

setTimeout((function(i2){
    return function() {
        console.log(i2);
    }
})(i), 1000);

ประกาศบริบทการดำเนินการสำหรับฟังก์ชั่นภายในโดยที่มูลค่าปัจจุบันของiจะถูกเก็บไว้ในi2; วิธีนี้ยังใช้การดำเนินการทันทีเพื่อรักษาคุณค่า

สิ่งสำคัญ

มันควรจะกล่าวว่า semantics ที่รันไม่เหมือนกันระหว่างทั้งสองวิธี ฟังก์ชั่นภายในของคุณจะถูกส่งไปยังsetTimeout()ในขณะที่ฟังก์ชั่นภายในของเขาเรียกsetTimeout()ตัวเองว่า

การห่อรหัสทั้งสองข้างไว้ในอีกอันsetTimeout()นั้นไม่ได้พิสูจน์ว่าวิธีการที่สองเท่านั้นที่ใช้การปิด

ข้อสรุป

ทั้งสองวิธีใช้การปิดดังนั้นมันจึงเป็นเรื่องส่วนตัว วิธีที่สองนั้นง่ายกว่าที่จะ "ย้าย" ไปรอบ ๆ หรือพูดคุยทั่วไป


ฉันคิดว่าความแตกต่างคือ: วิธีแก้ปัญหาของเขา (ที่ 1) คือการจับโดยการอ้างอิงของฉัน (ที่ 2) ถูกจับโดยค่า ในกรณีนี้มันไม่ได้สร้างความแตกต่าง แต่ถ้าฉันจะทำให้การดำเนินการใน setTimeout อื่นเราจะเห็นว่าวิธีการแก้ปัญหาของเขามีปัญหาว่ามันใช้ค่าสุดท้ายของ i ไม่ใช่กระแสในขณะที่ธรณีประตูของฉันใช้ มูลค่าปัจจุบัน (ตั้งแต่จับโดยมูลค่า)
leemes

@leemes คุณทั้งสองจับภาพในลักษณะเดียวกัน; ผ่านตัวแปรผ่านฟังก์ชั่นอาร์กิวเมนต์หรือการมอบหมายเป็นสิ่งเดียวกัน ... คุณสามารถเพิ่มคำถามของคุณว่าคุณจะห่อการดำเนินการในอีกsetTimeout()หรือไม่
Ja͢ck

ให้ฉันตรวจสอบนี้ ... ฉันต้องการแสดงให้เห็นว่าฟังก์ชั่นวัตถุสามารถส่งผ่านและตัวแปรเดิมiสามารถเปลี่ยนแปลงได้โดยไม่ส่งผลกระทบต่อสิ่งที่ฟังก์ชั่นที่พิมพ์ไม่ขึ้นอยู่กับที่หรือเมื่อเราดำเนินการ
leemes

รอคุณไม่ได้ผ่านฟังก์ชั่นไปยัง (นอก) setTimeout นำเหล่านั้น()จึงผ่านฟังก์ชั่นและคุณจะเห็น 10 10ครั้งเอาท์พุท
leemes

@leemes ดังกล่าวก่อนที่()เป็นสิ่งที่ทำให้การทำงานของรหัสของเขาเช่นเดียวกับคุณ(i); คุณไม่เพียงแค่ห่อรหัสของเขาคุณทำการเปลี่ยนแปลง .. ดังนั้นคุณจึงไม่สามารถทำการเปรียบเทียบที่ถูกต้องได้อีกต่อไป
Ja͢ck

8

ฉันได้เขียนสิ่งนี้เมื่อครู่ก่อนเพื่อเตือนตัวเองว่าการปิดคืออะไรและทำงานอย่างไรใน JS

การปิดเป็นฟังก์ชันที่เมื่อมีการเรียกใช้ขอบเขตที่ถูกประกาศไม่ใช่ขอบเขตที่ถูกเรียก ใน javaScript ฟังก์ชั่นทั้งหมดจะมีลักษณะเช่นนี้ ค่าตัวแปรในขอบเขตคงอยู่ตราบใดที่ยังมีฟังก์ชันที่ยังคงชี้ไป ข้อยกเว้นของกฎคือ 'นี่' ซึ่งหมายถึงวัตถุที่ฟังก์ชั่นอยู่ภายในเมื่อมันถูกเรียก

var z = 1;
function x(){
    var z = 2; 
    y(function(){
      alert(z);
    });
}
function y(f){
    var z = 3;
    f();
}
x(); //alerts '2' 

6

หลังจากตรวจสอบอย่างใกล้ชิดดูเหมือนว่าคุณทั้งคู่กำลังใช้กำลังปิดอยู่

ในกรณีที่เพื่อนของคุณiมีการเข้าถึงในฟังก์ชั่นที่ไม่ระบุชื่อ 1 และi2เข้าถึงได้ในฟังก์ชั่นที่ไม่ระบุชื่อ 2 ที่console.logมีอยู่

ในกรณีของคุณคุณกำลังเข้าถึงi2ฟังก์ชั่นที่ไม่ระบุชื่อที่console.logมีอยู่ เพิ่มdebugger;คำสั่งก่อนconsole.logและในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์โครมภายใต้ "ตัวแปรขอบเขต" มันจะบอกภายใต้ขอบเขตของตัวแปร


2
มีการใช้ส่วน "การปิด" ที่แผงด้านขวาเนื่องจากไม่มีชื่อเฉพาะอีกต่อไป "ท้องถิ่น" เป็นตัวบ่งชี้ที่แข็งแกร่งกว่า "การปิด"
Rob W


4

พิจารณาดังต่อไปนี้ สิ่งนี้จะสร้างและสร้างฟังก์ชั่นfที่ปิดiใหม่ แต่ต่างกัน!:

i=100;

f=function(i){return function(){return ++i}}(0);
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

f=function(i){return new Function('return ++i')}(0);        /*  function declarations ~= expressions! */
alert([f,f(),f(),f(),f(),f(),f(),f(),f(),f(),f()].join('\n\n'));

ในขณะที่สิ่งต่อไปนี้ปิดใน "a" function "ตัวมันเอง"
(ตัวมันเอง! snippet หลังจากสิ่งนี้ใช้การอ้างอิงเดียวf)

for(var i = 0; i < 10; i++) {
    setTimeout( new Function('console.log('+i+')'),  1000 );
}

หรือเพื่อให้ชัดเจนยิ่งขึ้น:

for(var i = 0; i < 10; i++) {
    console.log(    f = new Function( 'console.log('+i+')' )    );
    setTimeout( f,  1000 );
}

NB คำจำกัดความสุดท้ายของfคือfunction(){ console.log(9) } ก่อนที่จะ 0ถูกพิมพ์

ข้อแม้! แนวคิดการปิดสามารถทำให้ไขว้เขวบีบบังคับจากสาระสำคัญของการเขียนโปรแกรมระดับประถมศึกษา:

for(var i = 0; i < 10; i++) {     setTimeout( 'console.log('+i+')',  1000 );      }

x-refs:
การปิด JavaScript ทำงานอย่างไร
คำอธิบายการปิด Javascript การปิด
(JS) ต้องใช้ฟังก์ชันภายในฟังก์ชัน
การทำความเข้าใจกับการปิดใน Javascript อย่างไร
ความสับสนของตัวแปรในระดับท้องถิ่นและระดับ Javascript


ตัวอย่างพยายามเป็นครั้งที่ 1 - ไม่แน่ใจว่าจะควบคุมได้อย่างไร - Run' only was desired - not sure how to remove the Copy`
ekim

-1

ฉันต้องการแบ่งปันตัวอย่างและคำอธิบายเกี่ยวกับการปิด ฉันทำตัวอย่างไพ ธ อนและตัวเลขสองตัวเพื่อสาธิตสถานะสแต็ก

def maker(a, b, n):
    margin_top = 2
    padding = 4
    def message(msg):
        print('\n * margin_top, a * n, 
            ' ‘ * padding, msg, '  * padding, b * n)
    return message

f = maker('*', '#', 5)
g = maker('', '♥’, 3)

f('hello')
g(‘good bye!')

ผลลัพธ์ของรหัสนี้จะเป็นดังนี้:

*****      hello      #####

      good bye!    ♥♥♥

นี่คือตัวเลขสองตัวเพื่อแสดงสแต็คและการปิดที่แนบกับวัตถุฟังก์ชัน

เมื่อฟังก์ชันถูกส่งคืนจาก maker

เมื่อฟังก์ชั่นถูกเรียกในภายหลัง

เมื่อฟังก์ชันถูกเรียกใช้ผ่านพารามิเตอร์หรือตัวแปรที่ไม่ใช่โลคอลโค้ดต้องการการเชื่อมโยงตัวแปรโลคัลเช่น margin_top, การเติมเต็มและ a, b, n เพื่อให้แน่ใจว่าโค้ดฟังก์ชันสามารถทำงานได้เฟรมสแต็คของฟังก์ชัน maker ที่หายไปนานแล้วควรเข้าถึงได้ซึ่งถูกสำรองไว้ในการปิดเราสามารถค้นหาพร้อมกับวัตถุข้อความของฟังก์ชัน


ฉันต้องการลบคำตอบนี้ ฉันรู้ว่าคำถามไม่เกี่ยวกับสิ่งที่ปิดดังนั้นฉันต้องการย้ายไปยังคำถามอื่น
อึนจองลี

2
ฉันเชื่อว่าคุณมีความสามารถในการลบเนื้อหาของคุณเอง คลิกdeleteลิงก์ใต้คำตอบ
Rory McCrossan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.