Artikel ini membawakan anda nota kajian tentang penutupan JavaScript, termasuk penutupan, susunan kaedah dan peranan penutupan saya harap ia akan membantu anda.
Secara definisi, ia adalah bahasa skrip dan bahasa skrip yang agak mudah dipelajari. Tanpa banyak kepakaran, anda juga boleh menggunakan kod js (singkatan untuk JavaScript) pada tahap tertentu.
Sudah tentu, jika anda telah mempelajari pengetahuan bahagian hadapan, anda seharusnya dapat memahami peranan alat ini Ini adalah alat yang sangat mudah untuk memaparkan jarak antara elemen halaman. Anda lihat, anda baru sahaja melakukan beberapa operasi penyemak imbas yang mudah, dan anda tidak dapat memahami kandungan kod di atas, tetapi anda baru sahaja membenamkan sekeping kod js dalam halaman yang anda gunakan (jelas ia tidak berbahaya, Sila berasa bebas untuk menggunakannya) Terima kasih kepada video kursus minimum CodingStartup master atas [Dengannya, jadikan halaman web kelihatan seperti lukisan reka bentuk] dan jawapan master ArcRain di bawah video
Tujuan nota kajian ini adalah untuk merakam saya Saya mempunyai beberapa pandangan dan pengalaman tentang perjalanan pembelajaran js, serta beberapa petua yang saya fikir bukan untuk pengajaran, jadi saya tidak akan memberikan jawapan kepada prinsip beberapa kandungan Mungkin saya tidak dapat menerangkannya dengan tepat . , mungkin saya belum memahaminya lagi, dan tahap saya agak terhad Jika terdapat sebarang kesilapan dalam teks, anda dialu-alukan untuk mengkritik.
Secara formal belajar JavaScript adalah dalam kelas latihan Ya, saya keluar dari kelas latihan, boleh dikatakan saya sangat akar umbi. Semasa saya belajar, standard ES6 belum lagi popular, dan var yang sangat tradisional masih digunakan untuk penamaan pembolehubah Sekeping kod pertama yang saya pelajari ialah console.log('Hello, world!'), yang mana cetakan kursus pada konsol keluar.
Sudah tentu, kandungan JavaScript dalam institusi latihan adalah sangat mudah, dengan hanya definisi pembolehubah yang paling asas dan penamaan, pengisytiharan fungsi, fungsi panggil balik, ajax dan operasi dom yang paling asas. Jelas sekali kandungan ini tidak mencukupi untuk kerja itu.
Peluang untuk melanjutkan pelajaran dalam js datang dari kerja saya Di tempat kerja, saya belajar tentang nod buat kali pertama, dan juga mengetahui bahawa js pun boleh digunakan sebagai backend (saya bekerja dalam Latihan JAVA). dan secara beransur-ansur bersentuhan dengan beberapa piawaian ES6. Sudah tentu, ini semua perkara untuk kemudian. Halangan terbesar yang saya hadapi pada mulanya adalah produk ini.
Ah, saya hanya mempunyai sedikit pengetahuan tentangnya, dan saya tidak dapat memahami kod jsonp yang dirangkumkan oleh syarikat kami sama sekali.
var jsonp = (function(){ var JSONP; return function(url){ if (JSONP) { document.getElementsByTagName("head")[0].removeChild(JSONP); } JSONP = document.createElement("script"); JSONP.type = "text/javascript"; JSONP.src = url; document.getElementsByTagName("head")[0].appendChild(JSONP); } }())
Sudah tentu, kaedah ini tidak lagi boleh digunakan secara langsung melalui konsol pada penyemak imbas Untuk mengelakkan serangan XSS, penyemak imbas telah melarang suntikan kod dengan cara ini, tetapi ia masih boleh digunakan pada pelayan Sudah tentu, Tiada satu pun perkara ini.
Intinya di sini
if (JSONP) { //dosome }
Jika anda seperti saya dan tidak tahu apa itu penutupan atau hanya mempunyai pemahaman terhad tentang penutupan, maka anda juga harus mempunyai soalan tentang perkara ini . Idea ini kira-kira Ini adalah seperti ini
Baris 2 mentakrifkan JSONP tetapi tidak menetapkan nilai Sekarang baris ketiga mengembalikan kaedah . Jika ia tidak kosong, lakukan beberapa perkara, okey, anda tidak perlu membacanya kemudian, ini jika ditulis dengan sia-sia, ia adalah 100% mustahil untuk masuk!
Lihat, tiada tugasan sebelum ini, dan kemudian ia dinilai secara langsung, maka ia jelas batal. Tetapi apabila anda benar-benar menggunakannya, anda akan mendapati bahawa panggilan pertama ke tempat ini memang tidak akan memasuki cawangan ini, tetapi selagi anda memanggilnya kali kedua, ia akan 100% memasuki cawangan ini.
// 这个是一个可以在控制台输出的闭包版本,你可以自己试一下 var closedhull = (function() { let name = null; // 这里直接赋值为null return function(msg){ if(name) { console.log('name:', name) return name += msg; } return name = msg; } }()) closedhull('我是第一句。') //我是第一句。 closedhull('我是第二句。') //我是第一句。我是第二句。
Selepas menjalankan contoh di atas, tidak sukar untuk melihat dari console.log() atau nilai pulangan bahawa ia sememangnya telah memasuki cawangan if(name) Ini ialah prestasi penutupan . Berikut ialah definisi penutupan
Penutupan ialah fungsi yang boleh membaca pembolehubah dalaman fungsi lain.
Tidak!
Ciri yang membezakannya ialah fungsi dalam fungsi!
Perhatikan kaedah berikut
Empat ini adalah penutupan, dan semuanya mempunyai ciri kaedah dalam kaedah./*第一个案例*/ function test1(){ // a应该在方法运行结束后销毁 let a = 1; return { add: function(){ return ++a; } } } let a = test1(); a.add()//2 a.add()//3 /*第二个案例*/ (function(){ // b应该在方法运行结束后销毁 let b = 1, timer = setInterval(()=>{ console.log(++b) }, 2000) setTimeout(()=>{ clearInterval(timer) }, 10000) })()// 2 3 4 5 6 /*第三个案例*/ function showMaker(obj){ // obj应该在方法运行结束后销毁 return function(){ console.log(JSON.stringify(obj)) } } let shower = showMaker({a:1}) // 显然这里你还能看到他 shower(); // {"a":1} /*第四个案例*/ let outObj = (function(){ let c = 'hello', obj = {}; Object.defineProperty(obj, 'out', { get(){ return c; }, set(v){ c = v; } }); return obj })() outObj.out // 可以读取并设置c的值
4. Penutupan dan susunan kaedah (anda boleh melangkaunya jika anda tidak berminat dengan prinsip)
Memandangkan konsep gelung peristiwa terlibat dalam 3, kita akan membincangkannya kemudian di sini kita membincangkan takrifan dua yang pertama kaedah.
Jika anda tahu apa itu tindanan kaedah, anda boleh melangkaunya
局部作用域:在ES6之前,一般指一个方法内部(从参数列表开始,到方法体的括号结束为止),ES6中增加let关键字后,在使用let的情况下是指在一个{}中的范围内(显然,你不能在隐式的{}中使用let,编译器会禁止你做出这种行为的,因为没有{}就没有块级作用域),咱们这里为了简化讨论内容,暂且不把let的块级作用域算作闭包的范畴(其实应该算,不过意义不大,毕竟,你可以在外层块声明它。天啊,JS的命名还没拥挤到需要在一个方法内再去防止污染的程度。)
局部变量:区别于全局变量,全局变量会在某些时候被意外额创造和使用,这令人非常的...恼火和无助。局部变量就是在局部作用域下使用变量声明关键字声明出来的变量,应该很好理解。
局部变量的生命周期:好了,你在一个局部作用域中通过关键字(var const let等)声明了一个变量,然后给它赋值,这个局部变量在这个局部作用域中冒险就开始了,它会被使用,被重新赋值(除了傲娇的const小姐外),被调用(如果它是个方法),这个局部变量的本质是一个真实的值,区别在于如果它是个对象(对象,数组,方法都是对象)那么,它其实本质是一个地址的指针。如果它一个基础类型,那么它就是那个真实的值。它之所以存活是因为它有个住所。内存。
局部作用域与内存:每当出现一个局部作用域,一个方法栈就被申请了出来,在这个方法栈大概长这样子
| data5 | | data4 | | data3 | | data2 | |__data1_|
当然,它是能够套娃的,长这个样子
| | d2 | | | |_d1_| | | data3 | | data2 | |__data1___|
如果上面的东西是在太过于抽象,那么,我可以用实际案例展示一下
function stack1(){ var data1, data2, data3, data4, data5 } function stack2(){ var data1, data2, data3; function stackInner(){ var d1, d2; } }
如果方法栈能够直观的感受的话,大约就是这个样子,咱们重点来分析stack2的这种情况,同时写一点实际内容进去
function stack2(){ var data1 = '1', data2 = {x: '2'}, data3 = '3'; function stackInner(){ var d1 = '4', d2 = {y: '5'}; } stackInner() } stack2()
显然其中data1,data3,d1持有的是基本类型(string),data2,d2持有的是引用类型(object),反应到图上
运行时的方法栈的样子
|------>{y: '5'} | |->{x: '2'} | | d2-| || | | |_d1='4'_|| | | data3='3' | | | data2 ----| | |__data1='1'___|
画有点抽象...就这样吧。具体对象在哪呢?他们在一个叫堆的地方,不是这次的重点,还是先看方法栈内的这些变量,运行结束后,按照先进后出的原则,把栈内的局部变量一个一个的销毁,同时堆里的两个对象,由于引用被销毁,没了继续存在的意义,等待被垃圾回收。
接下来咱们要做两件事情:
d1不再等于4了,而是引用data1
return stackInner 而不是直接调用
这样闭包就完成了
function stack2(){ var data1 = {msg: 'hello'}, data2 = {x: '2'}, data3 = '3'; function stackInner(){ var d1 = data1, d2 = {y: '5'}; } return stackInner } var out = stack2()
这里有一个要点,d2赋值给data1一定是在stackInner中完成的,原因?因为再stackInner方法中d2才被声明出来,如果你在stack2中d1 = data1那么恭喜你,你隐式的声明了一个叫d1的全局变量,而且在stackInner由于变量屏蔽的原因,你也看不到全局上的d2,原本计划的闭包完全泡汤。
变量屏蔽:不同作用域中相同名称的变量就会触发变量屏蔽。
看看栈现在的样子
运行时的方法栈的样子
|------>{y: '5'} out<---| | |----| | | | d2-| | | | | | |--|_d1---|_| | | | data3='3' | | | data2(略) | | |_____data1<------|__|
好了,这个图可以和我们永别了,如果有可能,我后面会用画图工具替代,这么画图实在是太过邪典了。
这里涉及到了方法栈的一个特性,就是变量的穿透性,外部变量可以在内部的任意位置使用,因为再内部执行结束前,外部变量会一直存在。
由于stackInner被外部的out引用,导致这个对象不会随着方法栈的结束而销毁,接下来,最神奇的事情来了,由于stackInner这对象没有销毁,它内部d1依然保有data1所对应数据的引用,d1,d2一定会活下来,因为他们的爸爸stackInner活下来了,data1也以某种形式活了下来。
为什么说是某种形式,因为,本质上来说data1还是被销毁了。没错,只不过,data1所引用的那个对象的地址链接没有被销毁,这个才是本质。栈在调用结束后一定是会销毁的。但是调用本体(方法对象)只要存在,那么内部所引用的链接就不会断。
这个就是闭包的成因和本质。
OK,我猜测上一个章节估计很多人都直接跳过了,其实,跳过影响也不多,这个部分描述一下结论性的东西,闭包的作用。
它的最大作用就是给你的变量一个命名空间,防止命名冲突。要知道,你的框架,你export的东西,你import进来的东西,在编译的时候都会变成闭包,为的就是减少你变量对全局变量的污染,一个不依赖与import export的模块的代码大概长这个样子
(function(Constr, global){ let xxx = new Constr(env1, env2, env3) global.NameSpace = xxx; })(function(parm1, parm2, parm3) { //dosomeing reutrn { a: 'some1', b: 'some2', funcC(){ //dosome }, funcD(){ //dosome } } }, window)
当然这种封装代码的风格有多种多样的,但是大家都尽量把一套体系的内容都放到一个命名空间下,避免与其他框架产生冲突
相关推荐:javascript学习教程
Atas ialah kandungan terperinci Perkara yang anda perlu tahu tentang penutupan JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!