Rumah  >  Artikel  >  hujung hadapan web  >  Teruskan mempelajari kemahiran closure_javascript javascript

Teruskan mempelajari kemahiran closure_javascript javascript

WBOY
WBOYasal
2016-05-16 15:27:55936semak imbas

1. Apakah itu penutupan?

Penjelasan rasmi ialah: penutupan ialah ungkapan (biasanya fungsi) yang mempunyai banyak pembolehubah dan persekitaran yang terikat pada pembolehubah ini, jadi pembolehubah ini juga merupakan sebahagian daripada ungkapan
Saya percaya tidak ramai yang dapat memahami secara langsung ayat ini kerana penerangannya terlalu akademik. Malah, ayat ini dalam istilah orang awam bermaksud: semua fungsi dalam JavaScript adalah penutupan. Tetapi secara amnya, penutupan yang dijana oleh fungsi bersarang adalah lebih berkuasa, dan ia adalah apa yang kita panggil "penutupan" pada kebanyakan masa. Lihat kod berikut:

function a() { 
 var i = 0; 
 function b() { alert(++i); } 
 return b;
}
var c = a();
c();

Kod ini mempunyai dua ciri:

1. Fungsi b bersarang di dalam fungsi a;

2. Fungsi a mengembalikan fungsi b.

Hubungan rujukan adalah seperti yang ditunjukkan dalam rajah:

Dengan cara ini, selepas melaksanakan var c=a(), pembolehubah c sebenarnya menunjuk ke fungsi b Selepas melaksanakan c() sekali lagi, tetingkap akan muncul untuk memaparkan nilai i (kali pertama ialah 1). Kod ini sebenarnya membuat penutupan Mengapa? Kerana pembolehubah c fungsi luar a merujuk kepada fungsi b dalam fungsi a, iaitu:

 

Apabila fungsi dalaman b fungsi a dirujuk oleh pembolehubah di luar fungsi a, penutupan dibuat.

Mari kita lebih teliti. Apa yang dipanggil "penutupan" adalah untuk menentukan fungsi lain dalam badan pembina sebagai fungsi kaedah objek sasaran, dan fungsi kaedah objek ini pula merujuk kepada pembolehubah sementara dalam badan fungsi luar. Ini membolehkan nilai pembolehubah sementara yang digunakan oleh badan pembina asal dikekalkan secara tidak langsung selagi objek sasaran sentiasa boleh mengekalkan kaedahnya sepanjang hayatnya. Walaupun panggilan pembina awal telah tamat dan nama pembolehubah sementara telah hilang, nilai pembolehubah sentiasa boleh dirujuk dalam kaedah objek sasaran, dan nilai hanya boleh diakses melalui kaedah ini. Walaupun pembina yang sama dipanggil semula, hanya objek dan kaedah baharu akan dijana, dan pembolehubah sementara baharu hanya sepadan dengan nilai baharu, yang bebas daripada panggilan terakhir.

2. Apakah fungsi penutupan?

Ringkasnya, fungsi penutupan ialah selepas a dilaksanakan dan dikembalikan, penutupan menghalang mekanisme pengumpulan sampah Javascript GC daripada menuntut semula sumber yang diduduki oleh a, kerana pelaksanaan fungsi dalaman a b perlu bergantung pada a. pembolehubah dalam. Ini adalah huraian yang sangat jelas tentang peranan penutupan. Ia tidak profesional atau ketat, tetapi maksud umum ini adalah ini memerlukan proses langkah demi langkah.

Dalam contoh di atas, disebabkan kewujudan penutupan, i in a akan sentiasa wujud selepas fungsi a kembali dengan cara ini, setiap kali c() dilaksanakan, i akan menjadi nilai i yang dimaklumkan selepas menambah. 1.

Kemudian mari bayangkan situasi lain Jika a mengembalikan sesuatu selain daripada fungsi b, keadaannya berbeza sama sekali. Kerana selepas a dilaksanakan, b tidak dikembalikan ke dunia luar a, tetapi hanya dirujuk oleh a Pada masa ini, a hanya akan dirujuk oleh b Oleh itu, fungsi a dan b merujuk kepada satu sama lain tetapi tidak terganggu oleh dunia luar (dirujuk oleh dunia luar , fungsi a dan b akan dikitar semula oleh GC. (Mekanisme pengumpulan sampah Javascript akan diperkenalkan secara terperinci kemudian)

3. Dunia mikroskopik dalam penutupan

Jika kita ingin mempunyai pemahaman yang lebih mendalam tentang penutupan dan hubungan antara fungsi a dan fungsi bersarang b, kita perlu memperkenalkan beberapa konsep lain: konteks pelaksanaan fungsi (konteks pelaksanaan), objek aktif (objek panggilan), skop (skop ) ), rantaian skop. Ambil proses fungsi a daripada definisi kepada pelaksanaan sebagai contoh untuk menggambarkan konsep ini.

    Apabila mentakrifkan fungsi a, jurubahasa js akan menetapkan
  • rantai skop (rantai skop) fungsi a kepada "persekitaran" di mana a adalah apabila mentakrifkan a fungsi global, maka hanya terdapat objek tetingkap dalam rantai skop.
  • Apabila melaksanakan fungsi a, a akan memasuki konteks pelaksanaan
  • yang sepadan.
  • Dalam proses mencipta persekitaran pelaksanaan, atribut skop akan ditambahkan pada a, iaitu, skop
  • dan nilai ialah rantai skop dalam langkah 1. Iaitu, rantaian skop a.scope=a.
  • Kemudian persekitaran pelaksanaan akan mencipta objek panggilan
  • . Objek aktif juga merupakan objek yang mempunyai sifat, tetapi ia tidak mempunyai prototaip dan tidak boleh diakses secara langsung melalui kod JavaScript. Selepas mencipta objek aktif, tambahkan objek aktif ke bahagian atas rantai skop a. Pada masa ini, rantai skop a mengandungi dua objek: objek aktif a dan objek tetingkap.
  • Langkah seterusnya ialah menambah atribut argumen pada objek aktif, yang menyimpan parameter yang diluluskan semasa memanggil fungsi a.
  • Akhir sekali, tambahkan semua parameter formal fungsi a dan rujukan kepada fungsi dalaman b pada objek aktif a. Dalam langkah ini, takrifan fungsi b selesai, jadi sama seperti langkah 3, rantaian skop fungsi b ditetapkan kepada persekitaran di mana b ditakrifkan, iaitu skop a.
Pada ketika ini, langkah-langkah dari definisi hingga pelaksanaan keseluruhan fungsi a telah selesai. Pada masa ini, a mengembalikan rujukan fungsi b kepada c, dan rantai skop fungsi b mengandungi rujukan kepada objek aktif fungsi a, yang bermaksud bahawa b boleh mengakses semua pembolehubah dan fungsi yang ditakrifkan dalam a. Fungsi b dirujuk oleh c, dan fungsi b bergantung pada fungsi a, jadi fungsi a tidak akan dikitar semula oleh GC selepas dikembalikan.

Apabila fungsi b dilaksanakan, ia akan sama seperti langkah di atas. Oleh itu, rantai skop b semasa pelaksanaan mengandungi 3 objek: objek aktif b, objek aktif a dan objek tetingkap, seperti yang ditunjukkan dalam rajah berikut:

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

  • 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
  • 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
  • 如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) { 
 var g = function () { return x; }
 return g;
}
var h = f(1);
alert(h()); 

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。
假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。
如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

四、闭包的应用场景
保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
私有属性和方法在Constructor外是无法被访问的

function Constructor(...) { 
 var that = this; 
 var membername = value; 
 function membername(...) {...}
}

以上3点是闭包最基本的应用场景,很多经典案例都源于此。

五、Javascript的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。

六、结语

理解了aScript的闭包的解释和运行机制才能写出更为安全和优雅的代码,希望对大家的学习有所帮助。

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn