摘要:閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。立即執(zhí)行函數(shù)立即執(zhí)行函數(shù),顧名思義,立即會執(zhí)行的函數(shù),即當(dāng)讀取到該函數(shù),會立即執(zhí)行。特性使用語句聲明一個(gè)變量,該變量的范圍限于聲明它的塊中。使用聲明的變量,在聲明前無法使用,否則將會導(dǎo)致錯(cuò)誤。
let和閉包
之前一直模模糊糊記得,let解決了某個(gè)閉包問題,想用時(shí)又不敢肯定,今天終于遇到這個(gè)問題了,那我們就一起來分析一下,什么是let,let有什么作用,以及,他是如何解決閉包的,當(dāng)然,也順便好好聊聊閉包。1、閉包 1.1 閉包的定義
閉包的定義是這樣的:內(nèi)部函數(shù)被保存到了外部,即為閉包
先來看一個(gè)簡單的例子:
//我們聲明一個(gè)函數(shù)test(),這個(gè)函數(shù)返回了一個(gè)function function test(){ var i = 0; return function(){ console.log(i++) } }; //把test()的返回值賦給a和b變量,所以其實(shí)這時(shí)候的a/b=function(){console.log(i++);} var a = test(); var b = test(); //依次執(zhí)行a,a,b,控制臺會輸出什么呢? a();a();b();
先思考一下,然后去瀏覽器驗(yàn)證一下
答案是:0,1,0
這是因?yàn)?,a/b=test()時(shí),a/b各自保留了test的AO,所以各自上面均有一個(gè)i=0;
1.2 實(shí)現(xiàn)共有變量這樣其實(shí)是實(shí)現(xiàn)了一個(gè)共有變量,比如我們把上面的代碼稍稍調(diào)整一下,就實(shí)現(xiàn)了一個(gè)累加計(jì)數(shù)器;
//累加器 function add(){ var count = 0; function demo(){ count++; console.log(count); } return demo; } var counter = add(); counter();
這也是閉包的第一個(gè)功能,實(shí)現(xiàn)共有變量;
1.3 可以做緩存閉包的第二個(gè)功能是可以用作緩存,比如下面這個(gè)例子,我們用push把準(zhǔn)備用到的東西放進(jìn)去,當(dāng)eat調(diào)用時(shí)使用:
//隱式緩存應(yīng)用 function eater(){ var food = ""; var obj = { eat: function(){ console.log("I"m eating " + food); food = ""; }, push: function(myFood){ food = myFood; } } return obj; } var eater1 = eater(); eater1.push("banana"); eater1.eat();1.4 可以實(shí)現(xiàn)封裝,屬性私有化
這個(gè)典型的例子是圣杯繼承實(shí)現(xiàn)的雅虎的寫法
雅虎寫法 var inherit = (function(){ var F = function(){}; return function(Target,Origin){ F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target; //超類 Target.prototype.uber = Origin.prototype; } }()) //理解,return時(shí)保留了F變量,閉包私有化變量1.5 模塊化開發(fā),防止污染全局變量
利用閉包變量私有化,避免命名空間的問題 var name = "heh"; var init = (function(){ var name = "zhangsan"; function callName(){ console.log(name); } return function (){ callName(); } }()) init();1.6 閉包的危害
以上四點(diǎn),其實(shí)都是閉包的好處,善加利用是能夠幫助到我們的,所以大家不要先入為主覺得閉包是不好的,閉包其實(shí)是我們解決很多問題的一種思路,但當(dāng)然,閉包確實(shí)有它危害的方面:
閉包會導(dǎo)致原有作用域鏈不釋放,造成內(nèi)存泄漏,即占用導(dǎo)致剩下內(nèi)存變少
1.如何清除閉包:閉包函數(shù)=null; 如第一個(gè)例子:a = null;
否則閉包會一直占用內(nèi)存,直到瀏覽器進(jìn)程結(jié)束。
2.閉包會在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。如1.3的例子,我們修改了內(nèi)部的food值,所以,對于閉包,一定要小心使用。
3.還有一個(gè)最常見的情況是for循環(huán)中的閉包:我們寫一個(gè)ul列表,當(dāng)點(diǎn)擊時(shí)輸出對應(yīng)的i;
這和我們之前事件委托的例子很像,但是這里我們輸出的不是對應(yīng)的this對象,而是函數(shù)所在作用域的i值,可以看到,我們輸出的都是4,而我們的i應(yīng)該是從0到1、2、3,加到4的時(shí)候已經(jīng)不滿足條件了,不會進(jìn)入循環(huán)。
這到底是怎么回事呢?
這同樣形成了一個(gè)閉包,內(nèi)部的函數(shù)console.log(i)被保存到了外部的items[i].onclick()之中,所以我們有一個(gè)外部的AO,里面保存了一個(gè)i,但是這個(gè)i是for循環(huán)執(zhí)行完之后的i,當(dāng)我們執(zhí)行點(diǎn)擊函數(shù)時(shí),始終用到的就是這個(gè)i,但這明顯和我們要的不一樣,我們希望每一個(gè)執(zhí)行點(diǎn)擊時(shí)輸出的都是for循環(huán)時(shí)對應(yīng)的那個(gè)i;
這時(shí)候的閉包,是存在一定問題的,利用立即執(zhí)行函數(shù)可以解決這個(gè)問題。
2、立即執(zhí)行函數(shù)立即執(zhí)行函數(shù),顧名思義,立即會執(zhí)行的函數(shù)(Immediately-Invoked Function Expression),即當(dāng)js讀取到該函數(shù),會立即執(zhí)行。
我們1.4、1.5對應(yīng)的例子中就用到了這個(gè)方法。
用法如下:
每次到了立即執(zhí)行函數(shù)時(shí),都會把當(dāng)前的i賦值給index保存起來,并返回帶有這個(gè)值的函數(shù)。
2.1 立即執(zhí)行函數(shù)的寫法官方的兩種寫法 (function (){}());//w3c建議第一種 (function (){})();2.2 立即執(zhí)行函數(shù)用于初始化
var num = ( function (b) { var a = 123; console.log(a,b); d = a + b; return d; }(2) )2.3 常見寫法的注意事項(xiàng)
//1.只有表達(dá)式才能被執(zhí)行符號執(zhí)行,會忽略表達(dá)式的名字 //2.我們正常函數(shù)執(zhí)行的寫法如下 function test(){ }; test(); // 但是直接在函數(shù)聲明后接執(zhí)行符號,是不可以的,會報(bào)語法錯(cuò)誤 function test(){ }(); //3.凡是能變成表達(dá)式就能被執(zhí)行 var test = function () { }(); // 執(zhí)行一次后被永久銷毀 // 表達(dá)式部分 = function(){ // }() //4.最先識別哪個(gè)括號 (--這種寫法最外面先 function test(){}() --); (--這種寫法最前面先 function test(){} --) (); //可以沒有test名稱 //5.當(dāng)有參數(shù)時(shí),不報(bào)錯(cuò),但也不執(zhí)行 function test(a,b,c,d){ console.log(a+b+c+d); }(1,2,3,4); 實(shí)際分成了兩個(gè)部分 function test(a,b,c,d){ console.log(a+b+c+d);} 和 (1,2,3,4);//4,輸出個(gè)數(shù)2.4 作用
通過定義一個(gè)匿名函數(shù),創(chuàng)建了一個(gè)新的函數(shù)作用域,相當(dāng)于創(chuàng)建了一個(gè)“私有”的命名空間,該命名空間的變量和方法,不會破壞污染全局的命名空間。此時(shí)若是想訪問外部對象,將外部對象以參數(shù)形式傳進(jìn)去即可。
3、let還是上面那個(gè)問題,我們看看下面的代碼
// let for (let i = 0; i < len; i++) { items[i].onclick = function () { console.log(i); } }
和我們第一個(gè)版本一樣,只是把var聲明的i換成了let的聲明方式,但是結(jié)果已經(jīng)沒有任何問題了。
為什么僅僅改動了這一點(diǎn),就解決了我們之前的問題呢?
其實(shí)這個(gè)問題的本質(zhì)原因,還是var帶來的作用域的問題,接下來,我們來看一看,let,到底是什么?
let語句,聲明一個(gè)塊范圍變量。
let是ES6中新增關(guān)鍵字。它的作用類似于var,用來聲明變量,用法也類似,但是let是存在塊級作用域的。
let variable1 = value1;3.2 特性
1.使用 let 語句聲明一個(gè)變量,該變量的范圍限于聲明它的塊中。不能在外部訪問該變量,可以在聲明變量時(shí)為變量賦值,也可以稍后在腳本中給變量賦值。
var l = 10; {//注意這里僅僅是一個(gè)塊級作用域 let l = 2; console.log(l);// 這里 l = 2. let m = 4; console.log(m);// 這里 m = 4. } console.log(l);// 這里 l = 10. console.log(m);//報(bào)錯(cuò)
相反,對于var,最小級別是函數(shù)作用域的。
所以在for循環(huán)中,var聲明的i是所在函數(shù)的作用域,而let則是for循環(huán)及循環(huán)體內(nèi)的作用域,所以里面的語句可以訪問到對應(yīng)的i。
2.使用 let 聲明的變量,在聲明前無法使用,否則將會導(dǎo)致錯(cuò)誤。(不存在變量提升了)
console.log(index); let index;
3.如果未在 let 語句中初始化您的變量,則將自動為其分配 JavaScript 值 undefined。
let index; console.log(index);3.3 解讀for循環(huán)中的let
for (let i = 0; i < len; i++) { items[i].onclick = function () { console.log(i); } }
對于var來說,形成的閉包始終獲取到的都是循環(huán)完成后被改變的最終的i
而對于let,每一個(gè)i都是獨(dú)立在當(dāng)前塊級作用域的,當(dāng)前的i只在本輪循環(huán)有效,所以每一次循環(huán)的i其實(shí)都是一個(gè)新的變量。而JavaScript 引擎內(nèi)部會記住上一輪循環(huán)的值,初始化本輪的變量i時(shí),就在上一輪循環(huán)的基礎(chǔ)上進(jìn)行計(jì)算。
其實(shí),所謂的for循環(huán)帶來的閉包問題,其實(shí)就是變量作用域的問題,解決方式很多種,基本上可以用立即執(zhí)行函數(shù)和let變量聲明來解決,其次,具體情具體分析。
推薦閱讀http://web.jobbole.com/82520/
https://www.sogou.com/link?ur...
http://hao.jser.com/archive/5...
https://msdn.microsoft.com/li...
http://www.jb51.net/article/2...
https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/93491.html
摘要:進(jìn)擊的巨人第三篇,本篇就作用域作用域鏈閉包等知識點(diǎn),一一擊破。在此我們遵照的方式,暫且稱是閉包。所以,一名合格的前端,除了會用閉包,還要正確的解除閉包引用。 進(jìn)擊的巨人第三篇,本篇就作用域、作用域鏈、閉包等知識點(diǎn),一一擊破。 showImg(https://segmentfault.com/img/bVburWd?w=1280&h=854); 作用域 作用域:負(fù)責(zé)收集并維護(hù)由所有聲明的...
摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會討論安全的類型檢測惰性載入函數(shù)凍結(jié)對象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...
摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡單的方式。插....
摘要:針對于面向?qū)ο缶幊痰?。因?yàn)槊嫦驅(qū)ο缶褪轻槍ο罄又械氖睾騺磉M(jìn)行執(zhí)行某些動作。這就是閉包的用途之一延續(xù)變量周期。把變量放在閉包里面和放在全局變量里面,影響是一致的。 1.前言 這段時(shí)間,金三銀四,很多人面試,很多人分享面試題。在前段時(shí)間,我也臨時(shí)擔(dān)任面試官,為了大概了解面試者的水平,我也寫了一份題目,面試了幾個(gè)前端開發(fā)者。在這段時(shí)間里面,我在學(xué),在寫設(shè)計(jì)模式的一些知識,想不到的設(shè)計(jì)模式...
摘要:我的理解就是還處于被引用狀態(tài)。內(nèi)存機(jī)制的內(nèi)存空間分為棧堆其中棧存放變量,堆存放復(fù)雜對象。對堆內(nèi)數(shù)據(jù)進(jìn)行復(fù)制修改時(shí)理解閉包有了前面的鋪墊,我們再來看看閉包是怎么回事。這種反常的現(xiàn)象我們就叫它,中文名閉包。這就是閉包形成的原因了。 知識小儲備 ECMAScript 的數(shù)據(jù)有兩種類型:基本類型值和引用類型值,基本類型指的是簡單的數(shù)據(jù)段,引用類型指的是可能由多個(gè)值構(gòu)成的對象。Undefined...
閱讀 9056·2021-11-18 10:02
閱讀 2602·2019-08-30 15:43
閱讀 2663·2019-08-30 13:50
閱讀 1382·2019-08-30 11:20
閱讀 2712·2019-08-29 15:03
閱讀 3633·2019-08-29 12:36
閱讀 933·2019-08-23 17:04
閱讀 624·2019-08-23 14:18