javascript - كيف يتم إغلاق جافا سكريبت؟


كيف تفسر إقفال جافا سكريبت لشخص ما لديه معرفة بالمفاهيم التي تتكون منها (على سبيل المثال الوظائف والمتغيرات وما شابه ذلك)، ولكن لا يفهم الإغلاق نفسه؟

لقد رأيت المثال مخطط معين على ويكيبيديا، ولكن للأسف لم يساعد.



Answers



إغلاق جافا سكريبت للمبتدئين

مقدم من موريس في تو، 2006-02-21 10:19. المجتمع تحرير منذ ذلك الحين.

الإغلاق ليس سحريا

توضح هذه الصفحة عمليات الإغلاق حتى يتمكن مبرمج من فهمها - باستخدام شفرة جافا سكريبت العاملة. انها ليست للمعلمين أو المبرمجين وظيفية.

الإغلاق ليس من الصعب أن نفهم بمجرد غروكيد المفهوم الأساسي. ومع ذلك، فمن المستحيل أن نفهم من خلال قراءة أي أوراق أكاديمية أو معلومات موجهة أكاديميا عنها!

تهدف هذه المقالة للمبرمجين مع بعض الخبرة في البرمجة في اللغة السائدة، والذين يمكن قراءة وظيفة جافا سكريبت التالية:

function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');

مثال على الإغلاق

ملخصان جملة واحدة:

  • الإغلاق هو أحد طرق دعم وظائف من الدرجة الأولى . هو تعبير يمكن أن يشير إلى المتغيرات ضمن نطاقه (عندما تم الإعلان عنه لأول مرة)، يتم تعيينه إلى متغير، يتم تمريره كحجة إلى دالة، أو يتم إرجاعه كنتيجة دالة.

  • أو الإغلاق هو إطار مكدس يتم تخصيصه عندما تبدأ الدالة تنفيذها، ولا يتم تحريرها بعد عودة الدالة (كما لو تم تخصيص "إطار كومة" على كومة الذاكرة المؤقتة بدلا من كومة!).

ترجع التعليمات البرمجية التالية مرجع إلى دالة:

function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

سوف يفهم معظم المبرمجين جافا سكريبت كيف يتم إرجاع مرجع إلى دالة إلى متغير ( say2 ) في التعليمات البرمجية أعلاه. إذا كنت لا، ثم تحتاج إلى النظر في ذلك قبل أن تتمكن من تعلم الإغلاق. والمبرمج باستخدام C التفكير في وظيفة كما يعود مؤشر إلى وظيفة، وأن المتغيرات say و say2 كل مؤشر إلى وظيفة.

هناك فرق حرج بين مؤشر C لوظيفة ومرجع جافا سكريبت إلى دالة. في جافاسكريبت، يمكنك التفكير في متغير مرجع الدالة على أنه يحتوي على مؤشر إلى دالة بالإضافة إلى مؤشر مخفي لإغلاق.

يحتوي الرمز أعلاه على إغلاق لأن وظيفة function() { console.log(text); } المجهولة function() { console.log(text); } function() { console.log(text); } داخل وظيفة أخرى، sayHello2() في هذا المثال. في جافا سكريبت، إذا كنت تستخدم الكلمة الرئيسية function داخل وظيفة أخرى، كنت إنشاء إغلاق.

في C ومعظم اللغات الأخرى الشائعة، بعد عودة الدالة، جميع المتغيرات المحلية لم تعد متاحة لأنه يتم تدمير الإطار المكدس.

في جافاسكريبت، إذا قمت بإعلان دالة ضمن دالة أخرى، فإن المتغيرات المحلية يمكن أن تظل قابلة للوصول بعد عودتها من الدالة التي تسمىها. هذا هو موضح أعلاه، لأننا ندعو وظيفة say2() بعد أن say2() من sayHello2() . لاحظ أن الشفرة التي نسميها تشير إلى text المتغير، والذي كان متغيرا محليا للوظيفة sayHello2() .

function() { console.log(text); } // Output of say2.toString();

وبالنظر إلى إخراج say2.toString() ، يمكننا أن نرى أن التعليمات البرمجية تشير إلى text المتغير. الدالة المجهولة يمكن الرجوع text الذي يحمل قيمة 'Hello Bob' لأن المتغيرات المحلية من sayHello2() يتم الاحتفاظ في الإغلاق.

السحر هو أنه في جافاسكريبت مرجع وظيفة لديها أيضا إشارة سرية إلى إغلاق تم إنشاؤه في - على غرار كيف المندوبين هي مؤشر طريقة بالإضافة إلى إشارة سرية إلى كائن.

مزيد من الأمثلة

لسبب ما، يبدو أن الإغلاق يصعب فهمه عندما تقرأ عنهم، ولكن عندما ترى بعض الأمثلة يصبح واضحا كيف تعمل (استغرق مني بعض الوقت). أوصي بالعمل من خلال الأمثلة بعناية حتى تفهم كيف تعمل. إذا كنت تبدأ في استخدام الإغلاق دون فهم كامل كيف يعملون، وكنت قريبا خلق بعض الخلل غريب جدا!

المثال 3

يوضح هذا المثال أن المتغيرات المحلية لم يتم نسخها - يتم الاحتفاظ بها بالرجوع إليها. هو نوع من مثل حفظ كومة الإطار في الذاكرة عند خروج وظيفة الخارجي!

function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

المثال 4

جميع الوظائف العالمية الثلاث لها إشارة مشتركة إلى نفس الإغلاق لأن يتم الإعلان عنها جميعا في مكالمة واحدة ل setupSomeGlobals() .

var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

الوظائف الثلاث المشتركة الوصول إلى نفس الإغلاق - المتغيرات المحلية من setupSomeGlobals() عندما تم تعريف الوظائف الثلاث.

لاحظ أنه في المثال أعلاه، إذا قمت بالاتصال setupSomeGlobals() مرة أخرى، ثم يتم إنشاء إغلاق جديد (كومة الإطار!). يتم استبدال gLogNumber القديمة، gIncreaseNumber ، gSetNumber بوظائف جديدة لها الإغلاق الجديد. (في جافاسكريبت، كلما قمت بإعلان وظيفة داخل دالة أخرى، يتم إعادة إنشاء الدالة (الوظائف) الداخلية مرة أخرى في كل مرة يتم فيها استدعاء الدالة الخارجية.)

مثال 5

هذا واحد هو غوتشا حقيقية لكثير من الناس، لذلك تحتاج إلى فهم ذلك. كن حذرا جدا إذا كنت تعرف وظيفة داخل حلقة: المتغيرات المحلية من الإغلاق لا تعمل كما قد تفكر أولا.

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

result.push( function() {console.log(item + ' ' + list[i])} مرجع إلى وظيفة مجهولة ثلاث مرات إلى مصفوفة result.push( function() {console.log(item + ' ' + list[i])} لم تكن على دراية بالوظائف المجهولة، فكر في مثل:

pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);

لاحظ أنه عند تشغيل المثال، يتم تنبيه "item2 undefined" ثلاث مرات! وذلك لأن مثل الأمثلة السابقة فقط، هناك إغلاق واحد فقط للمتغيرات المحلية buildList . عندما يتم استدعاء الدالات المجهولة على الخط fnlist[j]() ؛ كلهم يستخدمون نفس الإغلاق المفرد، ويستخدمون القيمة الحالية item i item ضمن ذلك الإغلاق الأول (حيث 'item2' قيمة 3 لأن الحلقة قد اكتملت، item له قيمة 'item2' ). ملاحظة نحن الفهرسة من 0 وبالتالي item لديه قيمة item2 . و i ++ سوف زيادة i إلى قيمة 3 .

المثال 6

يوضح هذا المثال أن الإغلاق يحتوي على أي متغيرات محلية تم الإعلان عنها داخل الدالة الخارجية قبل أن تخرج. لاحظ أن المتغير alice أعلن فعلا بعد وظيفة مجهولة. يتم الإعلان عن وظيفة مجهول أولا. وعندما تسمى هذه الدالة فإنه يمكن الوصول إلى متغير alice لأن alice في نفس النطاق (جافاسكريبت لا متغير رفع ). أيضا sayAlice()() فقط يدعو مباشرة مرجع وظيفة عاد من sayAlice() - هو بالضبط نفس ما تم القيام به من قبل ولكن من دون متغير مؤقت.

function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

صعبة: لاحظ أيضا أن المتغير say هو أيضا داخل الإغلاق، ويمكن الوصول إليها من قبل أي وظيفة أخرى قد يتم الإعلان عنها في sayAlice() ، أو يمكن الوصول إليها بشكل متكرر داخل وظيفة داخل.

المثال 7

يوضح هذا المثال الأخير أن كل مكالمة تنشئ إغلاقا منفصلا للمتغيرات المحلية. ليس هناك إقفال واحد لكل إعلان وظيفة. هناك إغلاق لكل مكالمة إلى وظيفة.

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

ملخص

إذا كان كل شيء يبدو غير واضح تماما ثم أفضل شيء يجب القيام به هو اللعب مع الأمثلة. قراءة تفسير هو أصعب بكثير من أمثلة التفاهم. إن شروحي للإغلاق والأطر المكدسة، وما إلى ذلك ليست صحيحة من الناحية الفنية - فهي تبسيط شامل يهدف إلى المساعدة على الفهم. وبمجرد أن الفكرة الأساسية هي غروكيد، يمكنك التقاط التفاصيل في وقت لاحق.

النقاط النهائية:

  • كلما كنت تستخدم function داخل function أخرى، يتم استخدام إغلاق.
  • كلما استخدمت eval() داخل الدالة، يتم استخدام الإغلاق. النص الذي تقوم eval يمكن أن يشير إلى المتغيرات المحلية للوظيفة، وضمن eval يمكنك حتى إنشاء متغيرات محلية جديدة باستخدام eval('var foo = …')
  • عند استخدام new Function(…) ( منشئ الدالة ) داخل دالة، فإنه لا يخلق إغلاق. (لا يمكن أن تشير الدالة الجديدة إلى المتغيرات المحلية للوظيفة الخارجية.)
  • إغلاق في جافا سكريبت مثل حفظ نسخة من جميع المتغيرات المحلية، تماما كما كانت عندما خرجت وظيفة.
  • ربما يكون من األفضل أن نفكر بأن اإلغالق يتم إنشاؤه دائما مجرد إدخال إلى وظيفة، وتضاف المتغيرات المحلية إلى ذلك اإلغلاق.
  • يتم الاحتفاظ بمجموعة جديدة من المتغيرات المحلية في كل مرة يتم فيها استدعاء دالة مع الإغلاق (نظرا لأن الدالة تحتوي على تعريف دالة داخلها، ويتم إرجاع مرجع إلى تلك الوظيفة الداخلية أو يتم الاحتفاظ بمرجع خارجي لها بطريقة ما ).
  • قد تبدو وظيفتان كما لو كان لديهم نفس النص المصدر، ولكن لديهم سلوك مختلف تماما بسبب إغلاقهم "الخفية". لا أعتقد أن شفرة جافا سكريبت يمكن معرفة ما إذا كان مرجع وظيفة لديه إغلاق أم لا.
  • إذا كنت تحاول القيام بأي تعديلات شفرة مصدر ديناميكية (على سبيل المثال: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); )، فإنه لن يعمل إذا myFunction هو إغلاق ( وبطبيعة الحال، فإنك لن تفكر حتى في القيام شفرة المصدر استبدال سلسلة في وقت التشغيل، ولكن ...).
  • فمن الممكن للحصول على إعلانات وظيفة ضمن الإعلانات وظيفة داخل وظائف - ويمكنك الحصول على الإغلاق على مستوى أكثر من واحد.
  • أعتقد أن الإغلاق عادة هو المصطلح لكل من الدالة جنبا إلى جنب مع المتغيرات التي يتم التقاطها. لاحظ أنني لا تستخدم هذا التعريف في هذه المقالة!
  • أظن أن الإغلاق في جافا سكريبت يختلف عن تلك الموجودة عادة في اللغات الوظيفية.

الروابط

شكر

إذا كنت قد علمت للتو الإغلاق (هنا أو في أي مكان آخر!)، ثم أنا مهتم في أي ردود فعل منكم حول أي تغييرات قد تقترح التي يمكن أن تجعل هذه المقالة أكثر وضوحا. إرسال بريد إلكتروني إلى morrisjohns.com (morris_closure @). يرجى ملاحظة أنني لست المعلم على جافا سكريبت - ولا على الإغلاق.

يمكن العثور على آخر الأصلي من قبل موريس في أرشيف الإنترنت .




كلما رأيت الكلمة الرئيسية الدالة ضمن دالة أخرى، فإن الدالة الداخلية لها حق الوصول إلى المتغيرات في الدالة الخارجية.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

وهذا دائما تسجيل 16، لأن bar يمكن الوصول إلى x الذي تم تعريفه بأنه حجة foo ، ويمكن أيضا الوصول إلى tmp من foo .

هذا هو الإغلاق. وظيفة لا يجب أن يعود من أجل أن يسمى إغلاق. ببساطة الوصول إلى المتغيرات خارج نطاقك المعجمية الفورية يخلق إغلاق .

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

سوف وظيفة أعلاه أيضا تسجيل 16، لأن bar يمكن أن لا تزال تشير إلى x و tmp ، على الرغم من أنه لم يعد مباشرة داخل النطاق.

ومع ذلك، منذ tmp لا تزال معلقة داخل إغلاق bar ، كما يجري زيادة. سيتم زيادة في كل مرة كنت استدعاء bar .

وأبسط مثال على الإغلاق هو:

var a = 10;
function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

عند استدعاء وظيفة جافا سكريبت، يتم إنشاء سياق تنفيذ جديد. جنبا إلى جنب مع وسيطات الدالة والكائن الأصل، هذا السياق التنفيذ أيضا يتلقى كافة المتغيرات المعلنة خارج منه (في المثال أعلاه، على حد سواء 'أ' و 'ب').

ومن الممكن إنشاء أكثر من وظيفة إغلاق واحدة، إما عن طريق إعادة قائمة منهم أو عن طريق وضعها على المتغيرات العالمية. كل هذه سوف تشير إلى نفس x و نفس tmp ، فإنها لا تجعل النسخ الخاصة بهم.

هنا الرقم x هو عدد حرفي. كما هو الحال مع غيرها من الحرفية في جافا سكريبت، عندما يتم استدعاء foo ، يتم نسخ الرقم x إلى foo كما حجته x .

من ناحية أخرى، جافاسكريبت دائما يستخدم المراجع عند التعامل مع الكائنات. إذا قلت، كنت تسمى foo مع كائن، وإغلاق يعود سيشير هذا الكائن الأصلي!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

وكما هو متوقع، ستزيد كل دعوة x.memb bar(10) x.memb . ما قد لا يكون متوقعا، هو أن x يشير ببساطة إلى نفس الكائن مثل متغير age ! بعد اثنين من المكالمات إلى bar ، age.memb سيكون 2! هذه المرجعية هي أساس تسرب الذاكرة مع كائنات هتمل.




تم إعداد هذه الإجابة عندما كان السؤال:

مثل ألبرت القديمة قال: "إذا كنت لا يمكن أن يفسر ذلك إلى ست سنوات من العمر، كنت حقا لا أفهم ذلك بنفسك". حسنا حاولت أن أشرح جس إغلاق إلى صديق البالغ من العمر 27 عاما وفشلت تماما.

هل يمكن لأي شخص أن يعتبر أنني 6 مهتمة بشكل غريب بهذا الموضوع؟

أنا متأكد من أنني كنت واحدا من الناس الوحيد الذي حاول أن يأخذ السؤال الأولي حرفيا. منذ ذلك الحين، وقد تحور السؤال عدة مرات، لذلك جوابي قد يبدو الآن سخيفة بشكل لا يصدق والخروج من المكان. نأمل أن الفكرة العامة للقصة لا تزال ممتعة لبعض.

أنا مروحة كبيرة من التشبيه والاستعارة عند شرح المفاهيم الصعبة، لذلك اسمحوا لي أن أحاول يدي مع قصة.

في يوم من الأيام:

كانت هناك أميرة ...

function princess() {

عاشت في عالم رائع مليء بالمغامرات. التقت بها الأمير الساحرة، ركض حول عالمها على يونيكورن، التنين معارك، واجهت الحيوانات الحديث، والعديد من الأشياء الخيالية الأخرى.

    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

لكنها سوف تضطر دائما إلى العودة إلى عالمها مملة من الأعمال والكبار.

    return {

وقالت انها في كثير من الأحيان أقول لهم من أحدث مغامرة مذهلة باعتبارها أميرة.

        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

ولكن كل ما يراه هو فتاة صغيرة ...

var littleGirl = princess();

... رواية القصص عن السحر والخيال.

littleGirl.story();

وعلى الرغم من أن الكبار عرفوا الأميرات الحقيقية، فإنها لن تصدق أبدا في يونيكورن أو التنين لأنهم لا يمكن رؤيتها أبدا. وقال كبار السن أنهم لا توجد إلا داخل خيال الفتاة الصغيرة.

ولكننا نعرف الحقيقة الحقيقية؛ أن الفتاة الصغيرة مع الأميرة داخل ...

... حقا أميرة مع فتاة صغيرة في الداخل.




مع أخذ السؤال على محمل الجد، يجب علينا معرفة ما هو نموذجي البالغ من العمر 6 سنوات قادر على إدراكيا، على الرغم من اعتراف، واحد مهتم في جافا سكريبت ليست نموذجية جدا.

على تنمية الطفولة: 5 إلى 7 سنوات تقول:

سوف يكون طفلك قادرا على اتباع اتجاهين الاتجاهات. على سبيل المثال، إذا قلت لطفلك، "اذهب إلى المطبخ واحصل علي كيس القمامة" أنها سوف تكون قادرة على تذكر هذا الاتجاه.

يمكننا استخدام هذا المثال لتوضيح الإغلاق، كما يلي:

المطبخ هو الإغلاق الذي يحتوي على متغير محلي، ودعا trashBags . هناك وظيفة داخل المطبخ يسمى getTrashBag أن يحصل على كيس القمامة واحد getTrashBag .

يمكننا كتابة هذا الرمز في جافا سكريبت كما يلي:

function makeKitchen () {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

kitchen.getTrashBag(); // returns trash bag C
kitchen.getTrashBag(); // returns trash bag B
kitchen.getTrashBag(); // returns trash bag A

المزيد من النقاط التي توضح سبب الإغلاق مثيرة للاهتمام:

  • في كل مرة makeKitchen() يسمى، يتم إنشاء إغلاق جديد مع trashBags منفصلة الخاصة بها.
  • المتغير trashBags المحلية إلى الداخل من كل مطبخ ولا يمكن الوصول إليها في الخارج، ولكن الدالة الداخلية على الملكية getTrashBag له حق الوصول إليه.
  • كل مكالمة وظيفة يخلق إغلاق، ولكن لن تكون هناك حاجة للحفاظ على الإغلاق حول ما لم وظيفة داخلية، والتي لديها الوصول إلى داخل الإغلاق، يمكن استدعاؤها من خارج الإغلاق. إعادة الكائن مع الدالة getTrashBag يفعل ذلك هنا.



رجل سترو

أحتاج إلى معرفة عدد مرات النقر على الزر والقيام بشيء ما في كل نقرة ثالثة.

حل واضح إلى حد ما

// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

الآن هذا سوف يعمل، لكنه لا يتعدى في نطاق الخارجي عن طريق إضافة متغير، الذي يهدف الوحيد إلى تتبع العد. في بعض الحالات، سيكون هذا أفضل لأن التطبيق الخارجي الخاص بك قد تحتاج إلى الوصول إلى هذه المعلومات. ولكن في هذه الحالة، نحن فقط تغيير سلوك كل نقرة الثالثة، لذلك فمن الأفضل أن ترفق هذه الوظيفة داخل معالج الحدث .

ضع في اعتبارك هذا الخيار

var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

لاحظ بعض الأشياء هنا.

في المثال السابق، أستخدم سلوك إغلاق جافا سكريبت. يسمح هذا السلوك لأي وظيفة الوصول إلى النطاق الذي تم إنشاؤه، إلى أجل غير مسمى. لتطبيق هذا عمليا، أنا على الفور استدعاء الدالة التي ترجع وظيفة أخرى، ولأن وظيفة أعود لديه الوصول إلى المتغير العد الداخلي (بسبب سلوك الإغلاق المذكورة أعلاه) وهذا يؤدي إلى نطاق خاص للاستخدام من قبل الناتجة وظيفة ... ليست بهذه البساطة؟ دعونا نخففها ...

بسيطة إغلاق سطر واحد

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

تتوفر كافة المتغيرات خارج الدالة التي تم إرجاعها إلى الدالة التي تم إرجاعها ولكنها غير متوفرة مباشرة إلى كائن الدالة التي تم إرجاعها ...

func();  // Alerts "val"
func.a;  // Undefined

احصل عليه؟ لذلك في مثالنا الأساسي، يتم تضمين متغير العد داخل الإغلاق ومتاح دائما لمعالج الحدث، لذلك يحتفظ حالته من النقر فوق.

أيضا، هذه الدولة المتغيرة الخاصة يمكن الوصول إليها تماما ، لكل من القراءات والتنازل إلى المتغيرات نطاقها الخاص.

ها أنت ذا؛ كنت الآن تغليف تماما هذا السلوك.

مشاركة مدونة كاملة (بما في ذلك اعتبارات جكري)




الإغلاق يصعب تفسيره لأنها تستخدم لجعل بعض السلوك العمل أن الجميع يتوقع حدسي للعمل على أي حال. أجد أفضل طريقة لشرحها (والطريقة التي تعلمت بها) هي تخيل الوضع من دونهما:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

ماذا سيحدث هنا إذا لم تكن جافا سكريبت تعرف الإغلاق؟ مجرد استبدال المكالمة في السطر الأخير من قبل هيئة أسلوبها (وهو في الأساس ما وظيفة المكالمات القيام به) وتحصل على:

console.log(x + 3);

الآن، أين تعريف x ؟ لم نحدده في النطاق الحالي. الحل الوحيد هو السماح plus5 تحمل نطاقها (أو بالأحرى، نطاق والدها) حولها. وبهذه الطريقة، يتم تعريف x بشكل جيد، وهو ملزم بالقيمة 5.




هذه محاولة لتطهير العديد من (سوء) سوء الفهم حول الإغلاق التي تظهر في بعض الإجابات الأخرى.

  • لا يتم إنشاء إغلاق فقط عند إرجاع وظيفة الداخلية. والواقع أن الدالة المرفقة لا تحتاج إلى العودة على الإطلاق من أجل إغلاقها. يمكنك بدلا من ذلك تعيين الدالة الداخلية الخاصة بك إلى متغير في نطاق خارجي، أو تمريره كوسيطة إلى وظيفة أخرى حيث يمكن استدعاؤها فورا أو في أي وقت لاحق. لذلك، فإن إغلاق الدالة المحاطة قد يتم إنشاؤه حالما يتم استدعاء الدالة المغلقة لأن أي وظيفة داخلية لها حق الوصول إلى ذلك الإغلاق كلما استدعت الدالة الداخلية قبل أو بعد عودة الدالة المرفقة.
  • ولا يشير الإغلاق إلى نسخة من القيم القديمة للمتغيرات في نطاقها. المتغيرات نفسها هي جزء من الإغلاق، وبالتالي فإن القيمة ينظر عند الوصول إلى واحدة من تلك المتغيرات هي أحدث قيمة في الوقت الذي يتم الوصول إليها. هذا هو السبب في أن الوظائف الداخلية التي تم إنشاؤها داخل الحلقات يمكن أن تكون خادعة، حيث أن كل واحد لديه إمكانية الوصول إلى المتغيرات الخارجية نفسها بدلا من الاستيلاء على نسخة من المتغيرات في وقت إنشاء الدالة أو استدعاؤها.
  • وتشمل "المتغيرات" في الإغلاق أي وظائف مسماة معلنة ضمن الوظيفة.كما تشمل الحجج وظيفة. لديه الإغلاق أيضا الوصول إلى المتغيرات التي تحتوي على إغلاقها، وعلى طول الطريق حتى على النطاق العالمي.
  • إغلاق استخدام الذاكرة، لكنها لا تسبب التسرب في الذاكرة منذ جافا سكريبت في حد ذاته ينظف هياكل دائرية الخاصة التي لا يتم الرجوع إليها. يتم إنشاء برنامج Internet Explorer تسرب الذاكرة الإغلاق والحصار عندما يفشل في قطع DOM قيم السمات التي تشير إلى إغلاق، وبالتالي الحفاظ على كل ما يشير إلى الهياكل ربما دائرية.



موافق، مروحة إغلاق عمرها 6 سنوات. هل تريد أن تسمع أبسط مثال الإغلاق؟

دعونا نتصور الوضع القادم: سائق يجلس في سيارة. أن السيارة داخل الطائرة. الطائرة في المطار. قدرة السائق للوصول إلى الأشياء خارج سيارته، ولكن داخل الطائرة، حتى لو يترك أن الطائرة من المطار، هو الإغلاق. هذا هو.عند تشغيل 27، والنظر في شرح أكثر تفصيلا أو في المثال التالي.

هنا هو كيف يمكن تحويل قصة بلدي الطائرة في التعليمات البرمجية.

var plane = function (defaultAirport) {

    var lastAirportLeft = defaultAirport;

    var car = {
        driver: {
            startAccessPlaneInfo: function () {
                setInterval(function () {
                    console.log("Last airport was " + lastAirportLeft);
                }, 2000);
            }
        }
    };
    car.driver.startAccessPlaneInfo();

    return {
        leaveTheAirport: function (airPortName) {
            lastAirportLeft = airPortName;
        }
    }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");



A إغلاق يشبه إلى حد كبير كائن. يحصل مثيل كلما استدعاء دالة.

في نطاق إغلاق في جافا سكريبت هو المعجمي، وهو ما يعني أن كل شيء موجود ضمن الدالة إغلاق تنتمي إليها، لديه حق الوصول إلى أي متغير ما فيه.

ويرد متغير في إغلاق إذا كنت

  1. تعيين مع var foo=1; أو
  2. اكتب فقط var foo;

إذا وظيفة الداخلية (وظيفة الموجودة داخل وظيفة أخرى) يصل هذا المتغير دون تحديد في نطاقه الخاص مع فار، فإنه يعدل محتوى المتغير الخارجي في إغلاق .

A إغلاق outlives وقت التشغيل الدالة التي ولدت بها. إذا وظائف أخرى تجعل من إغلاق / نطاق التي يتم تعريفها (على سبيل المثال كقيم العودة)، وسوف تستمر تلك مرجع أن الإغلاق .

مثال

 function example(closure) {
   // define somevariable to live in the closure of example
   var somevariable = 'unchanged';

   return {
     change_to: function(value) {
       somevariable = value;
     },
     log: function(value) {
       console.log('somevariable of closure %s is: %s',
         closure, somevariable);
     }
   }
 }

 closure_one = example('one');
 closure_two = example('two');

 closure_one.log();
 closure_two.log();
 closure_one.change_to('some new value');
 closure_one.log();
 closure_two.log();

انتاج |

somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged



كتبت بلوق وظيفة في حين يعود لشرح الإغلاق. وهنا ما قلته عن الإغلاق من حيث لماذا كنت تريد واحدة.

الإغلاق هي وسيلة لترك وظيفة لها المتغيرات المستمرة، خاصة - أي المتغيرات التي وظيفة واحدة فقط يعرف عنه، حيث يمكن تتبع المعلومات من المرات السابقة التي تم تشغيله.

وبهذا المعنى، لكنهم سمحوا وظيفة تعمل قليلا مثل كائن مع سمات خاصة.

كاملة وظيفة:

فما هي هذه thingys الإغلاق؟




كيف كنت أشرح لطفل عمره ست سنوات:

أنت تعرف كيف نمت المنبثقة يمكن أن تمتلك منزلا، ويسمونه المنزل؟ عندما يكون أمي الطفل، والطفل لا حقا تملك أي شيء، أليس كذلك؟ ولكن والديه امتلاك منزل، لذلك كلما يسأل شخص ما للطفل "أين منزلك؟"، وقال انه / انها يمكن الإجابة "هذا البيت!"، وأشر إلى بيت والديه. A "إغلاق" هو ​​قدرة الطفل على دائما (حتى لو كان في الخارج) تكون قادرة على القول أنه لديه منزل، على الرغم من انها حقا الوالدين الذين يملكون المنزل.




إغلاق بسيطة:

يغطي مثال بسيط بعد كل النقاط الرئيسية لإغلاق جافا سكريبت. *

هنا هو المصنع الذي ينتج الآلات الحاسبة التي يمكن أن تضيف وتتكاثر:

function make_calculator() {
  var n = 0; // this calculator stores a single number n
  return {
    add: function(a) {
      n += a;
      return n;
    },
    multiply: function(a) {
      n *= a;
      return n;
    }
  };
}

first_calculator = make_calculator();
second_calculator = make_calculator();

first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400

first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

والنقطة الأساسية: كل دعوة إلى make_calculatorإنشاء متغير محلي جديد n، التي لا تزال صالحة للاستعمال من قبل أن آلة حاسبة في addو multiplyظائف بعد فترة طويلة من make_calculatorالعوائد.

إذا كنت معتادا على إطارات المكدس، هذه الآلات الحاسبة يبدو غريب: كيف يمكن أن تبقي الوصول nبعد make_calculatorعودة؟ والجواب هو أن نتصور أن جافا سكريبت لا تستخدم "إطارات المكدس"، ولكن بدلا من ذلك يستخدم "إطارات كومة"، والتي يمكن أن تستمر بعد استدعاء دالة التي جعلتهم العوائد.

وظائف الداخلية مثل addو multiply، الذي الوصول إلى المتغيرات في وظيفة الخارجية ** ، وتسمى الإغلاق .

وهذا هو الى حد كبير كل ما في الإغلاق.


* على سبيل المثال، فإنه يشمل جميع النقاط الواردة في "الإغلاق لالدمى" المادة الواردة في إجابة أخرى ، باستثناء سبيل المثال 6، مما يدل ببساطة أن المتغيرات يمكن استخدامها قبل أن يتم الإعلان عنها، وهذه حقيقة لطيفة أن يعرف ولكن لا علاقة لها تماما إلى الإغلاق. كما أنها تغطي كل نقطة في الإجابة مقبولة ، باستثناء نقطة (1) الذي يعمل نسخة حججهم إلى المتغيرات المحلية (الحجج وظيفة اسمه)، و (2) أن أعداد النسخ يخلق رقم جديد، ولكن نسخ مرجع كائن يعطيك إشارة أخرى إلى نفس الكائن. هذه هي أيضا جيدة لمعرفة ولكن مرة أخرى لا علاقة لها تماما إلى الإغلاق. وهي أيضا تشبه الى حد بعيد سبيل المثال في هذه الإجابة ولكن قليلا أقصر وأقل تجريدية. أنها لا تغطي وجهةهذه الإجابة أو هذا التعليق ، وهي أن جافا سكريبت يجعل من الصعب توصيل التيارقيمة متغير حلقة في وظيفة الداخلية الخاصة بك: إن "توصيل" خطوة يمكن أن يتم إلا مع وظيفة المساعد ان يرفق ظيفة الداخلية الخاصة بك ويتم استدعاء في كل حلقة تكرار. (بالمعنى الدقيق للكلمة، يصل إلى وظيفة داخلية نسخة وظيفة المساعد للمتغير، بدلا من وجود أي شيء في الوتر.) مرة أخرى، مفيدة جدا عند إنشاء الإغلاق، ولكن ليست جزءا من ما هو إغلاق أو كيف يعمل. هناك التباس إضافية بسبب إغلاق تعمل بشكل مختلف لغات وظيفية مثل ML، حيث لا بد المتغيرات إلى القيم بدلا من مساحة التخزين، وتوفير تيار مستمر من الناس الذين يفهمون الإغلاق بطريقة (وهي "توصيل" الطريق) التي هي ببساطة غير صحيحة لجافا سكريبت، حيث يتم المتغيرات متجهة دائما إلى مساحة التخزين، وأبدا إلى القيم.

** أي وظيفة الخارجية، إذا يتم تداخل عدة، أو حتى في السياق العالمي، و هذا الجواب يشير بوضوح.




هل يمكن ان توضح الإغلاق ليبلغ من العمر 5؟ *

ما زلت اعتقد تفسير جوجل يعمل بشكل جيد جدا وموجزة:

/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

* AC # السؤال




أنا أميل إلى معرفة أفضل عن طريق المقارنات جيدة / سيئة. أود أن أرى كود متبوعا برمز غير العاملة أن أحدا من المرجح أن تواجه العمل. لقد وضعت لjsFiddle أن يفعل المقارنة ويحاول أن تختزل الخلافات لأبسط التفسيرات I يمكن أن تصل.

إغلاق الحق في القيام به:

console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • في رمز أعلاه createClosure(n)يتم استدعاء في كل التكرار من الحلقة. علما بأنني اسمه المتغير nلتسليط الضوء على أنه هو جديد متغير تم إنشاؤها في نطاق وظيفة جديدة وليس نفس المتغير كما indexالذي لا بد أن نطاق الخارجي.

  • وهذا يخلق مجالا جديدا و n لا بد أن هذا النطاق. هذا يعني أن لدينا 10 نطاقات منفصلة، واحدة لكل التكرار.

  • createClosure(n) إرجاع دالة ترجع ن ضمن هذا النطاق.

  • داخل كل نطاق nلا بد أن كل ما القيمة التي كان عندما createClosure(n)تم استدعاء وبالتالي فإن وظيفة المتداخلة التي يحصل عاد سوف دوما بإرجاع قيمة nأنه كان عندما createClosure(n)تم استدعاء.

إغلاق يأتيه الباطل:

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • في رمز أعلاه تم نقل حلقة ضمن createClosureArray() وظيفة وظيفة الآن يعود فقط مجموعة تكتمل، والتي للوهلة الأولى يبدو أكثر سهولة.

  • ما قد لا يكون واضحا هو أنه منذ createClosureArray() تم استدعاء مرة واحدة فقط يتم إنشاء نطاق واحد فقط لهذه المهمة بدلا من واحد لكل التكرار من الحلقة.

  • ضمن هذه الوظيفة متغير اسمه indexيعرف. تدير حلقة وتضيف وظائف إلى مجموعة التي ترجع index. لاحظ أنه indexيتم تعريف داخلcreateClosureArray وظيفة منها فقط من أي وقت مضى يحصل الاحتجاج مرة واحدة.

  • لأنه لم يكن هناك مجال واحد فقط ضمن createClosureArray()وظيفة، indexلا بد إلا أن قيمة ضمن هذا النطاق. وبعبارة أخرى، في كل مرة حلقة تتغير قيمةindex ، فإنه يغير ذلك عن كل ما يحيل عليه ضمن هذا النطاق.

  • جميع وظائف تضاف إلى مجموعة نعود لنفس indexالمتغير من نطاق الأصل حيث تم تعريفه بدلا من 10 مختلفة منها من 10 نطاقات مختلفة كما في المثال الأول. والنتيجة النهائية هي أن جميع وظائف 10 نعود لنفس المتغير من نفس النطاق.

  • بعد حلقة منتهية و indexكان عمله يتم تعديل بلغت قيمة نهاية 10، لذلك كل وظيفة تضاف إلى مجموعة بإرجاع قيمة واحد indexمتغير التي تم تعيينها الآن إلى 10.

نتيجة

CLOSURES الحق في القيام به
ن = 0
ن = 1
ن = 2
ن = 3
ن = 4
ن = 5
ن = 6
ن = 7
ن = 8
ن = 9

CLOSURES يأتيه الباطل
ن = 10
ن = 10
ن = 10
ن = 10
ن = 10
ن = 10
ن = 10
ن = 10
ن = 10
ن = 10




ويكيبيديا على الإغلاق :

في علوم الكمبيوتر، والإغلاق هو وظيفة مع بيئة الرجوع لأسماء غير محلي (المتغيرات الحرة) من تلك الوظيفة.

من الناحية الفنية، في جافا سكريبت ، كل وظيفة هي إغلاق . لديها دائما الوصول إلى المتغيرات المحددة في نطاق المحيطة بها.

منذ البناء تحديد نطاق في جافا سكريبت هي وظيفة ، وليس كتلة التعليمات البرمجية كما هو الحال في العديد من اللغات الأخرى، ما يعني عادة إغلاق في جافا سكريبت هو وظيفة العمل مع المتغيرات غير محلي محددة في وظيفة المحيطة تنفيذها بالفعل .

وغالبا ما تستخدم الإغلاق لخلق وظائف مع بعض البيانات الخاصة الخفية (ولكن هذا ليس هو الحال دائما).

var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

نظم الإدارة البيئية

في المثال أعلاه يستخدم وظيفة غير معروفة والتى تم تنفيذها مرة واحدة. ولكن لا يجب أن تكون. ويمكن الكشف عن اسمه (على سبيل المثال mkdb) وأعدم في وقت لاحق، وتوليد وظيفة قاعدة البيانات في كل مرة يتم استدعاء ذلك. وكل وظيفة ولدت لديها الخاصة كائن قاعدة بيانات مخفية لها. مثال آخر استخدام الإغلاق هو عندما لا نعود وظيفة، ولكن كائن يحتوي على وظائف متعددة لأغراض مختلفة، كل تلك وظيفة بعد الوصول إلى نفس البيانات.




لقد وضعت البرنامج التعليمي التفاعلي جافا سكريبت لشرح كيفية عمل الإغلاق. ما هو لإغلاق؟

هنا واحد من الأمثلة على ذلك:

var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here



The children will always remember the secrets they have shared with their parents, even after their parents are gone. This is what closures are for functions.

The secrets for JavaScript functions are the private variables

var parent = function() {
 var name = "Mary"; // secret
}

Every time you call it, local variable "name" is created and given name "Mary". And every time the function exits the variable is lost and the name is forgotten.

كما قد يتبادر إلى ذهنك، لأن المتغيرات وإعادة خلق كل مرة يتم استدعاء وظيفة، وسوف لا أحد يعرف مكان لهم، يجب أن يكون هناك مكان سري حيث يتم تخزينها. فإنه يمكن أن يسمى حجرة الأسرار أو كومة أو نطاق محلي ولكن لا يهم حقا. ونحن نعرف أنهم هناك، في مكان ما، كانت مخبأة في الذاكرة.

ولكن، في جافا سكريبت هناك هذا شيء خاص جدا أن الوظائف التي يتم إنشاؤها داخل وظائف أخرى، ويمكن أيضا معرفة المتغيرات المحلية والديهم والاحتفاظ بها طالما يعيشون فيها.

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

لذلك، طالما نحن في -function الأم، فإنه يمكن إنشاء واحد أو أكثر من الوظائف الطفل التي لا تشارك المتغيرات سرية من مكان سري.

ولكن الشيء المحزن هو، إذا كان الطفل هو أيضا متغير الخاص من وظيفة الأم، فإنه يموت أيضا عندما ينتهي الوالد، وسوف أسرار يموت معهم.

حتى للعيش، وكان الطفل لديه لمغادرة قبل فوات الاوان

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 

والآن، على الرغم من ماري "لم تعد تعمل"، ذكرى لها لا تضيع وسوف طفلها نتذكر دائما اسمها وغيرها من الأسرار التي المشتركة خلال فترة وجودهم معا.

لذا، إذا كنت استدعاء الطفل "أليس"، وقالت انها سترد

child("Alice") => "My name is Alice, child of Mary"

هذا كل ما هناك هو أن أقول.




أنا لا أفهم لماذا الأجوبة معقدة جدا هنا.

هنا هو إغلاق:

var a = 42;

function b() { return a; }

نعم فعلا. ربما كنت تستخدم ذلك عدة مرات في اليوم.


ليس هناك سبب للاعتقاد الاغلاق الإختراق التصميم المعقد لمعالجة مشاكل محددة. لا، ليست سوى إغلاق حول استخدام متغير التي تأتي من نطاق أعلى من منظور حيث أعلن عن وظيفة (لا تعمل) .

الآن ما يسمح لك أن تفعل يمكن أن يكون أكثر إثارة، انظر إجابات أخرى.




مثال على النقطة الأولى التي dlaliberte:

لم يتم إنشاء إغلاق فقط عند العودة وظيفة الداخلية. في الواقع، ليست بحاجة الى وظيفة أرفق للعودة على الإطلاق. يمكنك بدلا من ذلك قد تعيين وظيفة الداخلية الخاصة بك إلى متغير في نطاق الخارجي، أو تمرير ذلك كحجة لوظيفة أخرى حيث يمكن استخدامها على الفور. لذلك، وإغلاق وظيفة إرفاق ربما موجود بالفعل في الوقت الذي تضم وظيفة ودعا لأن أي وظيفة الداخلية لديه حق الوصول إليها في أقرب وقت كما يطلق عليه.

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);



كنت تواجه النوم مرارا وكنت أدعو دان. كنت أقول دان لتحقيق تحكم إكس بوكس ​​واحد.

دان تدعو بول. دان يسأل بول لتحقيق تحكم واحدة. كم عدد وحدات التحكم نقلوا إلى الحزب؟

function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");



A الإغلاق حيث وظيفة الداخلية لديه حق الوصول إلى المتغيرات في وظيفتها الخارجي. وهذا ربما أبسط تفسير سطر واحد يمكنك الحصول على الإغلاق.




أعرف أن هناك الكثير من الحلول بالفعل، ولكن أعتقد أن هذا السيناريو صغير وبسيط يمكن أن تكون مفيدة للتدليل على مفهوم:

// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined



يمكن وظائف جافا سكريبت الوصول بها:

  1. الحجج
  2. السكان المحليين (وهذا هو، والمتغيرات المحلية والوظائف المحلية)
  3. بيئة، والتي تشمل:
    • غلوبالس، بما في ذلك DOM
    • أي شيء في وظائف الخارجية

إذا وظيفة يصل بيئته، ثم وظيفة هو الإغلاق.

لاحظ أنه ليس مطلوبا من المهام الخارجية، على الرغم من أنها لا تقدم فوائد لا أناقش هنا. عن طريق الوصول إلى البيانات في بيئتها، وتبقي على إغلاق تلك البيانات على قيد الحياة. في subcase وظائف الخارجية / الداخلية، يمكن وظيفة الخارجي خلق البيانات المحلية وفي نهاية المطاف للخروج، وبعد، إن وجدت وظيفة الداخلية (ق) البقاء على قيد الحياة بعد إنهاء الدالة الخارجية، ثم وظيفة الداخلية (ق) الحفاظ على البيانات المحلية وظيفة الخارجية ل على قيد الحياة.

مثال على إغلاق يستخدم البيئة العالمية:

تخيل أن المكدس تجاوز الأحداث زر صوت لأسفل أصوات الهاتفي وتنفذ عمليات الإغلاق، voteUp_click وvoteDown_click، التي يمكنها الوصول إلى المتغيرات الخارجية isVotedUp وisVotedDown، والتي تعرف عالميا. (على سبيل التبسيط، وأنا أشير هنا إلى أزرار سؤال أصوات ستاكوفيرفلوو، وليس مجموعة من أزرار الإجابة أصوات).

عندما يقوم المستخدم بالنقر فوق الزر VoteUp، يتحقق وظيفة voteUp_click سواء isVotedDown == صحيح لتحديد ما إذا كان سيصوت لأعلى أو مجرد إلغاء تصويت لأسفل. وظيفة voteUp_click هو إغلاق لأنه يتم الوصول إلى بيئته.

var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}

كل أربعة من هذه الوظائف الإغلاق كما أنهم جميعا الوصول بيئتهم.




مؤلف الإغلاق وأوضح الإغلاق بشكل جيد، موضحا السبب في أننا في حاجة إليها، وكذلك شرح LexicalEnvironment وهو أمر ضروري لفهم الإغلاق.
هنا هو موجز:

ماذا لو يتم الوصول متغير، ولكنها ليست محلية؟ مثلما هو الحال هنا:

في هذه الحالة، يجد المترجم المتغير الخارجي في LexicalEnvironmentالكائن.

The process consists of two steps:

  1. First, when a function f is created, it is not created in an empty space. There is a current LexicalEnvironment object. In the case above, it's window (a is undefined at the time of function creation).

When a function is created, it gets a hidden property, named [[Scope]], which references the current LexicalEnvironment.

If a variable is read, but can not be found anywhere, an error is generated.

Nested functions

Functions can be nested one inside another, forming a chain of LexicalEnvironments which can also be called a scope chain.

So, function g has access to g, a and f.

Closures

A nested function may continue to live after the outer function has finished:

Marking up LexicalEnvironments:

As we see, this.say is a property in the user object, so it continues to live after User completed.

وإذا كنت تتذكر، عندما this.sayتم إنشاؤه، فإنه (كما في كل وظيفة) يحصل على المرجعية الداخلية this.say.[[Scope]]للLexicalEnvironment الحالية. لذا، فإن LexicalEnvironment إعدام العضو الحالي يبقى في الذاكرة. جميع متغيرات العضو أيضا خصائصه، لذلك هم أيضا الاحتفاظ بعناية، وليس ألغى كما عادة.

بيت القصيد هو التأكد من أن إذا كانت وظيفة الداخلية تريد الوصول إلى المتغير الخارجي في المستقبل، وأنها قادرة على القيام بذلك.

كي تختصر:

  1. وظيفة داخلية تبقي إشارة إلى LexicalEnvironment الخارجي.
  2. وظيفة داخلية قد الوصول متغيرات منه في أي وقت حتى لو تم الانتهاء من وظيفة الخارجية.
  3. المتصفح يحافظ على LexicalEnvironment وجميع خصائصه (المتغيرات) في الذاكرة حتى يكون هناك وظيفة الداخلية التي تشير إليها.

وهذا ما يسمى الإغلاق.

(قد تحتاج أيضا إلى قراءة ما هو الاستخدام العملي لإغلاق في جافا سكريبت؟ )




باعتباره الأب من العمر 6 سنوات، وتعليم الأطفال الصغار حاليا (والمبتدئين النسبي لترميز مع أي تعليم رسمي لذلك سوف تكون هناك حاجة التصويبات)، وأعتقد أن الدرس عصا أفضل من خلال التدريب العملي على اللعب. إذا البالغ من العمر 6 سنوات على استعداد لفهم ما هو الإغلاق، ثم أنها قديمة بما فيه الكفاية لذهاب أنفسهم. أنا أقترح لصق الشفرة في jsfiddle.net، موضحا قليلا، وترك لهم وحدهم لتلفيق أغنية فريدة من نوعها. نص توضيحي أدناه هو على الارجح أكثر ملاءمة ل10 عاما.

function sing(person) {

    var firstPart = "There was " + person + " who swallowed ";

    var fly = function() {
        var creature = "a fly";
        var result = "Perhaps she'll die";
        alert(firstPart + creature + "\n" + result);
    };

    var spider = function() {
        var creature = "a spider";
        var result = "that wiggled and jiggled and tickled inside her";
        alert(firstPart + creature + "\n" + result);
    };

    var bird = function() {
        var creature = "a bird";
        var result = "How absurd!";
        alert(firstPart + creature + "\n" + result);
    };

    var cat = function() {
        var creature = "a cat";
        var result = "Imagine That!";
        alert(firstPart + creature + "\n" + result);
    };

    fly();
    spider();
    bird();
    cat();
}

var person="an old lady";

sing(person);

تعليمات

البيانات: البيانات هي عبارة عن مجموعة من الحقائق. ويمكن أن يكون أرقام، والكلمات، والقياسات والملاحظات أو حتى مجرد أوصاف الأشياء. لا يمكنك لمسها، رائحة لها أو تذوقه. يمكنك كتابة عليه، التحدث بها والاستماع إليه. هل يمكن استخدامها ل خلق رائحة اللمس والذوق باستخدام الكمبيوتر. ويمكن ان يكون مفيدا من قبل جهاز الكمبيوتر باستخدام تعليمات برمجية.

CODE: جميع الكتابة فوق ما يسمى قانون . هو مكتوب في جافا سكريبت.

جافا سكريبت: جافا سكريبت هي لغة. مثل الإنجليزية أو الفرنسية أو الصينية لغات. هناك الكثير من اللغات التي يفهمها الكمبيوتر والمعالجات الإلكترونية الأخرى. للجافا سكريبت ليكون مفهوما من قبل جهاز كمبيوتر فإنه يحتاج إلى مترجم. تخيل لو المعلم الذي لا يتحدث إلا الروسي يأتي لتعليم صفك في المدرسة. عندما يقول المعلم "все садятся"، فإن الطبقة لا يفهم. لكن لحسن الحظ لديك تلميذ الروسي في صفك الذي يقول الجميع وهذا يعني "الجميع الجلوس" - حتى لكم جميعا القيام به. الطبقة هي مثل الكمبيوتر والتلميذ الروسي المترجم. لجافا سكريبت يسمى مترجم الأكثر شيوعا المتصفح.

BROWSER: عند الاتصال بالإنترنت على جهاز كمبيوتر لوحي أو هاتف لزيارة موقع على شبكة الانترنت، يمكنك استخدام المتصفح. أمثلة تعلمون هي إنترنت إكسبلورر، والكروم، فايرفوكس وسفاري. المتصفح يمكن أن نفهم جافا سكريبت ونقول للكمبيوتر ما يتعين عليها القيام به. تعليمات جافا سكريبت هي تسمى الوظائف.

الوظيفة: وظيفة في جافا سكريبت مثل مصنع. قد يكون قليلا المصنع مع جهاز واحد فقط في الداخل. أو أنها قد تحتوي على العديد من المصانع الصغيرة الأخرى، كل مع العديد من الأجهزة والقيام بوظائف مختلفة. في مصنع للملابس واقع الحياة قد يكون لديك رزمة من القماش والبكر من موضوع تسير في والقمصان والجينز الخروج. مصنع جافا سكريبت فقط بمعالجة البيانات، فإنه لا يمكن خياطة، وحفر حفرة أو تذوب المعادن. في مصنع جافا سكريبت يذهب البيانات في والبيانات يخرج.

كل هذه البيانات الاشياء تبدو مملة بعض الشيء، لكنها في الحقيقة بارد جدا. قد يكون لدينا وظيفة أن يقول إنسان ما جعل لتناول العشاء. دعنا نقول أدعو أنت وصديقك إلى منزلي. تريد أرجل الدجاج أفضل، وأنا أحب النقانق، وصديقك دائما يريد ما تريد وصديقي لا يأكل اللحوم.

ليس لدي الوقت للذهاب للتسوق، لذلك يحتاج وظيفة لمعرفة ما لدينا في الثلاجة لاتخاذ القرارات. كل عنصر لديه الوقت للطهي مختلفة، ونحن نريد أن يكون كل شيء يخدم الساخنة بواسطة الروبوت في نفس الوقت. نحن بحاجة إلى توفير وظيفة مع البيانات حول ما نحب، وظيفة يمكن أن 'الكلام' إلى الثلاجة، وظيفة لا يمكن التحكم في الروبوت.

وظيفة لديه عادة اسما أو الأقواس والأقواس. مثله:

function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

لاحظ أن /*...*/و //إيقاف رمز يتم قراءتها من قبل المتصفح.

الاسم: يمكنك استدعاء دالة فقط عن كل ما الكلمة التي تريد. على سبيل المثال "cookMeal" هو نموذجي في الانضمام كلمتين معا، ويعطي ثانية واحدة حرف الكبير في بداية - ولكن هذا ليس ضروريا. لا يمكن أن يكون لها مساحة فيه، وأنه لا يمكن أن يكون عدد من تلقاء نفسها.

بين قوسين: "الأقواس" أو ()هي الرسالة مربع على باب المصنع وظيفة جافا سكريبت أو مربع آخر في الشارع لإرسال حزم المعلومات إلى المصنع. في بعض الأحيان قد تكون وضعت صندوق البريد على سبيل المثال cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime) ، في هذه الحالة كنت تعرف ما هي البيانات التي لديك لإعطائها.

الأقواس: "الحمالات" التي تبدو وكأنها هذه {}هي زجاج معتم من المصنع. من داخل مصنع يمكن أن ترى، ولكن من الخارج لا يمكن أن نرى في.

THE طويل CODE المثال أعلاه

يبدأ رمز لنا مع كلمة وظيفة ، لذلك نحن نعرف أنه واحد! ثم اسم الدالة الغناء - وهذا وصف بلدي ما هي وظيفة عنه. ثم قوسين () . الأقواس هي دائما هناك لوظيفة. في بعض الأحيان أنها فارغة، وأحيانا لديهم شيء في هذا واحد لديه كلمة في: (person). بعد هذا هناك هدفين من هذا القبيل {. هذا يمثل بداية وظيفة الغناء () . لديها شريك الذي يصادف نهاية الغناء () مثل هذا}

function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }

لذلك قد يكون هذه الوظيفة أن تفعل شيئا مع الغناء، وربما تحتاج إلى بعض البيانات حول شخص. لديها تعليمات داخل أن تفعل شيئا مع تلك البيانات.

الآن، وبعد وظيفة الغناء () ، قرب نهاية رمز هو خط

var person="an old lady";

VARIABLE: رسائل فار يقف ل "المتغير". المتغير هو مثل مغلف. في الخارج ويتميز هذا الظرف "شخص". في الداخل كان يحتوي على قصاصة من الورق مع الاحتياجات من المعلومات وظيفتنا، انضم بعض الحروف والمساحات معا مثل قطعة من سلسلة (انه دعا سلسلة) التي تجعل عبارة كتب عليها "سيدة تبلغ من العمر". يمكن أن تحتوي لدينا المغلف أنواع أخرى من أشياء مثل أرقام (وتسمى الأعداد الصحيحة)، تعليمات (تسمى وظائف)، وقوائم (وتسمى المصفوفات ). لأن هذا المتغير هو مكتوب خارج كل الأقواس {}، ولأنك يمكن أن نرى من خلال زجاج معتم عندما كنت داخل الأقواس، هذا المتغير يمكن أن ينظر إليه من أي مكان في التعليمات البرمجية. ونحن نسمي هذا "المتغير العالمي".

متغير عمومي: الشخص هو المتغير العالمي، وهذا يعني أنه إذا قمت بتغيير قيمته من "سيدة تبلغ من العمر" إلى "شاب"، و شخص سوف تبقى كونه شابا حتى تقرر تغييره مرة أخرى، وأن أي وظيفة أخرى في رمز يمكن أن يرى أنه كان شابا. اضغط على F12زر أو إلقاء نظرة على إعدادات خيارات لفتح وحدة تحكم مطور المتصفح واكتب "شخص" لنرى ما هي هذه القيمة. اكتب person="a young man"لتغييره ثم اكتب "شخص" مرة أخرى لنرى أنه قد تغير.

بعد هذا لدينا خط

sing(person);

هذا الخط تدعو وظيفة، كما لو كانوا يدعون كلب

"هيا الغناء ، تعال والحصول على شخص !"

عندما متصفح قام بتحميل شفرة جافا سكريبت لوصل هذا الخط، فإنه سيتم البدء الدالة. أضع خط في نهاية للتأكد من أن المتصفح لديه كل المعلومات التي يحتاجها لتشغيله.

وظائف تحدد الإجراءات - وظيفتها الرئيسية هي عن الغناء. وهو يحتوي على متغير يسمى firstPart الذي ينطبق على الغناء عن الشخص الذي ينطبق على كل من آيات أغنية: "كان هناك" + شخص + "الذي ابتلع". إذا قمت بكتابة firstPart إلى وحدة التحكم، فإنك لن تحصل على الجواب بسبب تأمين متغير حتى في وظيفة - المتصفح لا يمكن أن نرى داخل زجاج معتم من الأقواس.

CLOSURES: لكن الاغلاق وظائف الصغيرة التي توجد داخل كبيرة الغناء () وظيفة. مصانع صغيرة داخل مصنع كبير. أنها تمتلك كل الأقواس الخاصة بهم والتي تعني أن المتغيرات داخلها لا يمكن أن ينظر إليه من الخارج. هذا هو السبب في أسماء المتغيرات ( مخلوق و نتيجة ) يمكن أن يتكرر في عمليات الإغلاق ولكن مع قيم مختلفة. إذا قمت بكتابة هذه أسماء المتغيرات في إطار وحدة التحكم، فإنك لن تحصل على قيمتها لأنها مخبأة من قبل اثنين من طبقات من زجاج معتم.

إغلاق كافة يعرفون ما الغناء () يسمى متغير الدالة firstPart هو، لأنها يمكن رؤية للخروج من واجهاتها ملون.

بعد إغلاق تأتي الخطوط

fly();
spider();
bird();
cat();

فإن وظيفة الغناء () دعوة كل من هذه المهام في النظام التي تقدم لهم. ثم سوف يتم عمل وظيفة الغناء () ل.




حسنا، والحديث مع طفل 6 سنوات من العمر، وأود أن ربما استخدام التالية الجمعيات.

تخيل - كنت تلعب مع الاخوة قليلا وأخواتكم في المنزل بأكمله، وأنت تتحرك حولها مع ألعابك وجلب بعض منهم في غرفة الأخ الأكبر الخاص بك. بعد فترة من الزمن عاد أخيك من المدرسة وذهب إلى غرفته، وانه مؤمن في داخله، وحتى الآن لم تتمكن من الوصول إلى اللعب تركت هناك بعد الآن بطريقة مباشرة. ولكن هل يمكن أن يطرق الباب ويسأل أخيك لذلك لعب الأطفال. وهذا ما يسمى لعبة في إغلاق . جعلت أخيك الامر بالنسبة لك، وهو الآن في الخارجي نطاق .

مقارنة مع الوضع عندما تم تأمين الباب من قبل مشروع ولا أحد داخل (تنفيذ وظيفة عامة)، ثم تحدث بعض الحرائق المحليين وحرق الغرفة (جامع القمامة: D)، ومن ثم كانت غرفة جديدة بناء والآن قد ترك ألعاب أخرى هناك (مثيل وظيفة جديدة)، ولكن لم تحصل على نفس اللعب التي تركت في المقام الأول غرفة.

للحصول على طفل المتقدمة أود أن وضع شيء كما يلي. أنها ليست مثالية، ولكنه يجعلك تشعر حول ما هو عليه:

function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

كما ترون، واللعب ترك في الغرفة لا تزال يمكن الوصول إليها عبر الأخ وبغض النظر عن إذا تم تأمين الغرفة. هنا هو لjsbin للعب مع حولها.




جوابا لطفلة تبلغ من العمر ست سنوات (على افتراض انه يعرف ما هي وظيفة وما هو متغير، وما هي البيانات):

يمكن الدالات بإرجاع البيانات. نوع واحد من البيانات التي يمكن أن تعود من وظيفة غير وظيفة أخرى. عندما يحصل على إرجاع هذا وظيفة جديدة، كل المتغيرات والحجج المستخدمة في الوظيفة التي خلقها لا تذهب بعيدا. بدلا من ذلك، أن وظيفة الأم "يغلق". وبعبارة أخرى، لا شيء يمكن أن ننظر داخل منه ورؤية المتغيرات استخدامه باستثناء الوظيفة التي تم إرجاعها. أن وظيفة جديدة لديها قدرة خاصة أن ننظر إلى الوراء داخل الدالة التي خلقها والاطلاع على البيانات داخل منه.

function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

طريقة أخرى بسيطة حقا أن تفسير ذلك هو من حيث النطاق:

أي وقت قمت بإنشاء نطاق أصغر داخل نطاق أوسع، فإن نطاق أصغر يكون دائما قادرا على رؤية ما هو في نطاق أوسع.




ربما أبعد قليلا جميع ولكن معظم مبكر من سنة من العمر ست سنوات، ولكن عدد قليل من الأمثلة التي ساهمت في جعل مفهوم الإغلاق في جافا سكريبت انقر بالنسبة لي.

والإغلاق هو وظيفة لديه حق الوصول إلى نطاق وظيفة آخر (متغيراته وظائف). أسهل طريقة لإنشاء الإغلاق هي مع وظيفة داخل دالة. والسبب هو أن وظيفة في جافا سكريبت دائما الوصول إلى نطاق يحتوي على وظيفتها و.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

التنبيه: قرد

في المثال أعلاه، ويسمى outerFunction وهذا بدوره يدعو innerFunction. لاحظ كيف outerVar هو متاح innerFunction يدل عليه بشكل صحيح تنبيه قيمة outerVar.

لنتأمل الآن ما يلي:

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

التنبيه: قرد

ومن المقرر referenceToInnerFunction إلى outerFunction ()، والتي ترجع ببساطة إشارة إلى innerFunction. عندما يتم استدعاء referenceToInnerFunction، فإنها ترجع outerVar. مرة أخرى، على النحو الوارد أعلاه، وهذا يدل على أن innerFunction لديه حق الوصول إلى outerVar، متغير outerFunction. وعلاوة على ذلك، فإنه من المثير للاهتمام أن نلاحظ أنه يحتفظ هذا الوصول حتى بعد انتهاء outerFunction المنفذة.

وهنا حيث الامور مثيرة للاهتمام حقا. إذا كان لنا أن نتخلص من outerFunction، ويقول تعيينها فارغة، قد تعتقد أن referenceToInnerFunction أن تفقد قدرتها على الوصول إلى قيمة outerVar. ولكن هذا ليس هو الحال.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());

التنبيه: قرد ALERT: قرد

ولكن كيف يحدث هذا؟ كيف يمكن referenceToInnerFunction تزال تعرف قيمة outerVar الآن أن outerFunction تم ضبط فارغة؟

والسبب في ذلك referenceToInnerFunction لا يزال الوصول إلى قيمة outerVar هو أنه عندما تم إنشاء إغلاق أول من وضع innerFunction داخل outerFunction وأضاف innerFunction إشارة إلى نطاق outerFunction في (متغيراته وظائف) إلى سلسلة نطاقه. ما يعنيه هذا هو أن innerFunction لديه مؤشر أو إشارة إلى كل من المتغيرات outerFunction، بما في ذلك outerVar. وحتى عندما انتهت outerFunction المنفذة، أو حتى إذا تم حذفه أو مجموعة لاغية، والمتغيرات في نطاقها، مثل outerVar، حول عصا في الذاكرة بسبب إشارة بارزة لهم على جزء من innerFunction التي تم إعادتها إلى referenceToInnerFunction. إطلاق سراح حقا outerVar وبقية المتغيرات outerFunction من ذاكرة عملتم للتخلص من هذا المرجع المتميزة لهم،أقول من خلال وضع referenceToInnerFunction فارغة أيضا.

//////////

شيئين أخرى حول إغلاق لاحظ. أولا، سوف إغلاق دائما الوصول إلى القيم الأخيرة من دالة تحتوي على.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();

التنبيه: الغوريلا

ثانيا، عندما يتم إنشاء الإغلاق، فإنه يحتفظ إشارة إلى كل من المتغيرات وظيفة أرفق بها ووظائفها. أنها لا تحصل على انتقاء واختيار. ولكن ذلك، يجب استخدام الإغلاق لماما، أو على الأقل بعناية، لأنها يمكن أن تكون الذاكرة المكثفة. الكثير من المتغيرات يمكن أن تبقى في الذاكرة لفترة طويلة بعد انتهاء وظيفة تحتوي المنفذة.




وظيفة في جافا سكريبت ليست مجرد إشارة إلى مجموعة من التعليمات (كما هو الحال في لغة C)، ولكنه يشمل أيضا بنية بيانات مخفية التي تتألف من إشارات إلى كل المتغيرات غير محلي يستخدم (المتغيرات القبض). وتسمى هذه اثنين من قطعة وظائف الإغلاق. ويمكن اعتبار كل وظيفة في جافا سكريبت إغلاق.

الاغلاق وظائف مع الدولة. وهو مشابه إلى حد ما "هذا" بمعنى أن "هذا" يوفر أيضا دولة لوظيفة ولكن وظيفة و"هذا" هي كائنات منفصلة ( "هذا" هو مجرد معلمة الهوى، والطريقة الوحيدة لربط بشكل دائم ل وظيفة هو خلق إغلاق). في حين أن "هذا" وظيفة يعيش دائما على حدة، وهي وظيفة لا يمكن فصلها عن إغلاق ويوفر لغة أي وسيلة للوصول إلى المتغيرات التي تم التقاطها.

لأن كل هذه المتغيرات الخارجية المشار إليه بواسطة الدالة المتداخلة مفرداتيا هي المتغيرات المحلية الواقع في سلسلة من الوظائف أرفق مفرداتيا لها (المتغيرات العالمية يمكن الافتراض أن المتغيرات المحلية من بعض وظيفة الجذر)، وكل إعدام واحدة من وظيفة يخلق حالات جديدة لل المتغيرات المحلية، فإنه يترتب على ذلك أن كل تنفيذ وظيفة يعودون (أو نقل بها، مثل تسجيل على أنها رد) وظيفة المتداخلة يخلق إغلاق الجديد (مع نفسه مجموعة يحتمل أن تكون فريدة من نوعها من المتغيرات غير محلي المشار إليها والتي تمثل تنفيذه سياق الكلام).

أيضا، يجب أن يكون مفهوما أن يتم إنشاء المتغيرات المحلية في جافا سكريبت ليس على الإطار المكدس، ولكن على كومة وتدميرها إلا عندما لا احد يتم الرجوع إليها. عندما ترجع الدالة، يتم decremented ما يشير إلى المتغيرات المحلية، ولكنها يمكن أن تكون لا تزال غير الصفرية إذا أثناء تنفيذ الحالي أصبحوا جزءا من الإغلاق ولا تزال المشار إليه بواسطة مهامها متداخلة مفرداتيا (الذي يمكن أن يحدث إلا إذا كان يشير إلى أعيدت هذه الوظائف المتداخلة أو نقلها إلا إلى بعض رمز خارجي).

مثال:

function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();



فما استقاموا لكم فاستقيموا لهم مجرد نقطة إلى الصفحة موزيلا الإغلاق . إنها أفضل، الأكثر شرح موجز وبسيط من أساسيات الإغلاق والاستخدام العملي التي وجدتها. ويوصى بشدة لأي شخص جافا سكريبت التعلم.

ونعم، كنت حتى أوصي به يبلغ من العمر 6 - إذا كان 6 سنوات من العمر والتعلم عن الإغلاق، فإنه من المنطقي انهم على استعداد لفهم شرح موجز وبسيط المنصوص عليها في هذه المادة.