Penutupan perkongsian ringkasan JavaScript

WBOY
Lepaskan: 2022-11-07 16:31:08
ke hadapan
1251 orang telah melayarinya

Artikel ini membawa anda pengetahuan yang berkaitan tentang JavaScript terutamanya memperkenalkan isu berkaitan tentang penutupan, termasuk apakah penutupan, mengapa ia direka dengan cara ini dan bagaimana ia boleh digunakan kandungan yang berkaitan di bawah saya harap ia akan membantu semua orang.

Penutupan perkongsian ringkasan JavaScript

[Cadangan berkaitan: Tutorial video JavaScript, bahagian hadapan web]

Apakah itu penutupan ?

Untuk titik pengetahuan, saya sentiasa percaya bahawa tidak kira dari mana anda bermula, anda perlu memahami tiga soalan dengan teliti sebelum anda benar-benar memahami perkara pengetahuan ini, dan kemudian mempraktikkannya dalam amalan untuk menjadi layak Untuk dikuasai. Tiga soalan ini ialah: Apakah

  • ?
  • Kenapa reka bentuk? Di manakah
  • boleh digunakan?

Jawab dahulu soalan tentang penutupan. Sepatutnya kebanyakan orang telah membaca banyak artikel berkaitan, dan ramai juga yang telah memberikan penjelasan mereka sendiri, jadi saya akan memberikan penjelasan yang saya faham dahulu, iaitu: Terdapat dua konsep berimbuhan:

  • Penutupan telah ditentukan semasa analisis leksikal, jadi ia akan dikaitkan dengan skop leksikal.

  • Prasyarat untuk kewujudan penutupan ialah bahasa pengaturcaraan perlu menyokong fungsi sebagai warga kelas pertama, jadi ia akan berkaitan dengan fungsi.

Jadi kesimpulan akhir ialah:

  • 闭包首先是一个结构体,这个结构体的组成部分为  一个函数 该函数所处的词法作用域
  • Iaitu, 闭包是由一个函数并且该函数能够记住声明自己的词法作用域所产生的结构体.
  • difahami dalam ingatan sebagai, apabila fungsi dipanggil, 它所产生的函数执行上下文里的作用域链保存有其父词法作用域,所以父变量对象由于存在被引用而不会销毁,驻留在内存中供其使用. Keadaan ini dipanggil penutupan.

Penjelasan di atas sepatutnya jelas kepada orang yang telah memahami penutupan, tetapi sebenarnya, jika mereka tidak mengetahui sepenuhnya tentang penutupan, mereka mungkin tidak dapat difahami sepenuhnya. Apatah lagi, ramai yang sebenarnya hanya ingat takrifan ini, tetapi tidak begitu memahami konotasinya.

Jadi, saya ingin menggunakan analogi yang tidak semestinya tepat untuk membantu memahami maksud penutupan. Bayangkan anda menulis artikel dan meletakkannya pada pelayan anda sendiri, dan memetik 3 artikel anda sendiri sebagai rujukan. Jadi pada masa ini, persekitaran pelayan artikel adalah serupa dengan penutupan.

Selepas diterbitkan di Internet, ia dicetak semula pada platform lain, dan pembaca di platform lain klik pada artikel anda untuk membaca dan ingin terus membaca artikel yang anda petik, dan ia diubah hala dengan tepat artikel pada pelayan anda.

Dalam contoh ini, artikel itu menyimpan rujukan kepada persekitaran pelayan di mana ia ditulis. Oleh itu, tidak kira di mana anda membaca artikel tersebut, rujukan artikel rujukan yang diingati dalam artikel akan sentiasa menunjuk ke alamat dalam pelayan. Keadaan ini dipanggil menggunakan ciri penutupan.

Mungkin contoh itu masih tidak mudah untuk difahami, lagipun, ia tidak begitu tepat konsep penutupnya agak abstrak, dan saya tidak memikirkan sebarang contoh konkrit yang boleh digunakan sebagai metafora. Jika sesiapa boleh memikirkan analogi yang lebih baik untuk ditunjukkan, saya akan memberi anotasi dan menerangkannya.

Mengapa kita perlu mereka bentuk penutupan?

Mengapa ia direka, pemahaman cetek saya adalah kerana bahasa JavaScript是异步单线程. Masalah terbesar dengan pengaturcaraan tak segerak ialah apabila anda menulis fungsi, masa ia sebenarnya dipanggil mungkin pada bila-bila masa kemudian.

Ini adalah masalah besar untuk 内存管理 Untuk kod yang dijalankan secara serentak, data yang diperlukan apabila fungsi diisytiharkan dan dipanggil masih dalam ingatan dan boleh diperoleh tanpa sebarang halangan. Kod tak segerak sering mengisytiharkan bahawa konteks fungsi mungkin telah dimusnahkan Pada masa ia dipanggil, jika beberapa data luaran yang diperlukan telah dikosongkan dalam ingatan, ini adalah masalah besar.

Jadi penyelesaian JavaScript ialah 让函数能够记得自己之前所能获取数据的范围,统统都保存在内存里,只要该函数没有被内存回收,它自身以及所能记住的范围都不会被销毁.

Skop yang tidak dapat dilupakan di sini merujuk kepada skop leksikal, yang perlu diingat kerana ia statik.

Ini sekali lagi disebabkan oleh skop reka bentuk statik JavaScript. Jika ia adalah skop dinamik, apabila fungsi dipanggil, ia hanya memerlukan persekitaran apabila ia dipanggil, dan tidak perlu mengingati skopnya sendiri.

Jadi untuk meringkaskan:

  • penutupan dijana untuk 解决词法作用域引发的问题 dan 内存不好管理异步编程里数据获取.

Soalan klasik

Asalnya idea saya adalah untuk menerangkan situasi penutupan dari peringkat terendah Kemudian, apabila saya menyemak pelbagai artikel, saya mendapati bahawa satu artikel sudah ditulis dengan baik. Itulah mekanisme operasi asas penutupan JavaScript saya rasa anda boleh membaca penjelasan ini dahulu dan kemudian membaca apa yang saya tulis kemudian.

Memandangkan terdapat banyak artikel bermula daripada soalan temu bual yang sangat klasik berikut, tetapi tiada siapa yang benar-benar menjelaskannya dari peringkat bawah, jadi saya bercadang untuk menyelesaikan keseluruhan proses untuk memahaminya .

for (var i = 0; i < 3; i++) {  setTimeout(function cb() {    console.log(i);
  }, 1000);
}
Salin selepas log masuk

Pada asasnya semua orang yang mempunyai kemahiran asas boleh memberitahu sepintas lalu bahawa output adalah tiga 3.

Kemudian ubah suainya kepada output mengikut urutan Biasanya anda hanya perlu mengubah suai var untuk membiarkan:

for (let i = 0; i < 3; i++) {  setTimeout(function cb() {    console.log(i);
  }, 1000);
}
Salin selepas log masuk

Dengan cara ini, output ialah 0, 1, 2. Dan ia adalah output. pada masa yang sama, dan Ia tidak dikeluarkan setiap satu saat.

Maka persoalannya, kenapa?

Anda boleh menulis penjelasan anda sendiri terlebih dahulu tanpa membaca yang berikut untuk melihat sama ada ia sama dengan apa yang saya tulis.

1 Mari kita bincangkan situasi di mana pembolehubah i ialah var.

Apabila kod mula dilaksanakan, situasi dalam tindanan konteks pelaksanaan dan ingatan adalah seperti berikut: 变量i dalam objek global dan 变量i dalam persekitaran pembolehubah dalam konteks pelaksanaan global adalah pembolehubah yang sama.

Kemudian gelung bermula Apabila i = 0, pemasa pertama dilemparkan ke dalam baris gilir tugasan makro tergolong dalam kategori gelung peristiwa. buat sementara Hanya faham bahawa setTimeout akan dibuang ke dalam baris gilir dan dilaksanakan kemudian. Pada masa ini, fungsi panggil balik cb akan dibuat dalam ingatan timbunan, dan [[skop]] akan dibuat apabila fungsi itu dicipta Dalam peraturan ECMA sebenar, [[skop]] akan menunjuk kepada skop induk fungsi, iaitu semasa Objek global (skop ialah perkara konsep, dan manifestasi sebenar dalam ingatan ialah struktur yang menyimpan data, yang mungkin objek atau sesuatu yang lain). Walau bagaimanapun, dalam pelaksanaan enjin V8, ia sebenarnya tidak menunjuk kepada objek global Sebaliknya, ia menganalisis pembolehubah dalam skop induk yang digunakan oleh fungsi, menyimpan pembolehubah ini dalam Penutupan, dan kemudian menunjuk ke skop. Setiap fungsi mempunyai tepat satu objek Penutupan.


Berikut ialah maklumat tentang tempat objek Penutupan boleh dilihat dalam Chrome: Seperti yang anda lihat, apabila fungsi bar dicipta, ia hanya merujuk pembolehubah nama skop induk, jadi hanya nama pembolehubah disimpan dalam objek penutupan, tetapi umur pembolehubah tidak wujud.


Begitu juga, i = 1, dan i = 2 adalah sama, dan keputusan akhir akan menjadi:

Akhirnya, i = 3 kerana i, gelung berakhir dan kod global dilaksanakan. Hasilnya pada masa ini ialah:

dan kemudian memulakan proses melaksanakan fungsi panggil balik pemasa. Mula melaksanakan fungsi panggil balik dalam pemasa pertama, tolaknya ke dalam tindanan konteks pelaksanaan, dan laksanakan output i Walau bagaimanapun, pembolehubah i tidak boleh ditemui dalam persekitaran leksikal dan persekitaran berubah, jadi kita pergi ke [[skop]nya sendiri. ] dan cari i didapati dalam objek Penutupan dan bersamaan dengan 3, dan hasil keluaran ialah 3.

Begitu juga, proses yang sama digunakan untuk dua pemasa seterusnya, dan sebenarnya, masa apabila pemasa dimulakan dilaksanakan serta-merta dalam gelung, menghasilkan yang sebenar tiga Masa bagi setiap fungsi adalah konsisten selama 1 saat, dan hasil keluaran akhir ialah tiga 3 adalah keluaran hampir serentak. Daripada mengeluarkan 3 selepas setiap selang 1 saat, sudah tentu ini adalah pengetahuan berkaitan pemasa.

2. Kemudian bincangkan perkara yang sebenarnya berubah selepas menukar var kepada membiarkan

Apabila ia mula-mula dibuat, situasi yang ditunjukkan ialah:

Selepas memasuki badan gelung, apabila i = 0:

Selepas memasuki situasi apabila i = 1:

Akhirnya memasuki situasi i = 2, yang pada asasnya serupa dengan i = 1:

Akhirnya i menjadi nilai i 3 , kitaran tamat. Mulakan pemasa:

当执行第一个定时器的回调函数时,创建了函数执行上下文,此时执行输出语句i时,会先从自己的词法环境里寻找变量i的值,也就是在 record环境记录里搜索,但是不存在。因而通过自己外部环境引用outer找到原先创建的块级作用域里 i = 0的情况, 输出了i值为0的结果。

对于之后的定时器也都是一样的情况,原先的块级作用域由于被回调函数所引用到了,因而就产生了闭包的情况,不会在内存中被销毁,而是一直留着。

等到它们都执行完毕后,最终内存回收会将之全部都销毁。

其实以上画的图并不是很严谨,与实际在内存中的表现肯定是有差异的,但是对于理解闭包在内存里的情况还是不影响的。

闭包能用在哪?

首先需要先明确一点,那就是在JavaScript中,只要创建了函数,其实就产生了闭包。这是广义上的闭包,因为在全局作用域下声明的函数,也会记着全局作用域。而不是只有在函数内部声明的函数才叫做闭包。

通常意义上所讨论的闭包,是使用了闭包的特性

1. 函数作为返回值

let a = 1function outer() {  let a = 2

  function inside() {
    a += 1
    console.log(a)
  }  return inside
}const foo = outer()foo()
Salin selepas log masuk

此处outer函数调用完时,返回了一个inside函数,在执行上下文栈中表示的既是outer函数执行上下文被销毁,但有一个返回值是一个函数。 该函数在内存中创建了一个空间,其[[scope]]指向着outer函数的作用域。因而outer函数的环境不会被销毁。

当foo函数开始调用时,调用的就是inside函数,所以它在执行时,先询问自身作用域是否存在变量a, 不存在则向上询问自己的父作用域outer,存在变量a且值为2,最终输出3。

2. 函数作为参数

var name = &#39;xavier&#39;function foo() {  var name = &#39;parker&#39;
  function bar() {    console.log(name)
  } console.log(name)  return bar
}function baz(fn) {  var name = &#39;coin&#39;
  fn()
}baz(foo())baz(foo)
Salin selepas log masuk

对于第一个baz函数调用,输出的结果为两个'parker'。 对于第二个baz函数的调用,输出为一个'parker'。

具体的理解其实跟上面一致,只要函数被其他函数调用,都会存在闭包。

3. 私有属性

闭包可以实现对于一些属性的隐藏,外部只能获取到属性,但是无法对属性进行操作。

function foo(name) {  let _name = name  return {    get: function() {      return _name
    }
  }
}let obj = foo(&#39;xavier&#39;)
obj.get()
Salin selepas log masuk

4. 高阶函数,科里化,节流防抖等

对于一些需要存在状态的函数,都是使用到了闭包的特性。

// 节流function throttle(fn, timeout) {  let timer = null
  return function (...arg) {    if(timer) return
    timer = setTimeout(() => {
    fn.apply(this, arg)
    timer = null
    }, timeout)
  }
}// 防抖function debounce(fn, timeout){  let timer = null
  return function(...arg){    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, arg)
    }, timeout)
  }
}
Salin selepas log masuk

5. 模块化

在没有模块之前,对于不同地方声明的变量,可能会产生冲突。而闭包能够创造出一个封闭的私有空间,为模块化提供了可能性。 可以使用IIFE+闭包实现模块。

var moduleA = (function (global, doc) {  var methodA = function() {};  var dataA = {};  return {    methodA: methodA,    dataA: dataA
  };
})(this, document);
Salin selepas log masuk

【相关推荐:JavaScript视频教程web前端

Atas ialah kandungan terperinci Penutupan perkongsian ringkasan JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:juejin.im
Kenyataan Laman Web ini
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
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!