成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

前端小知識--從Javascript閉包看let

Kross / 375人閱讀

摘要:閉包會在父函數(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;


    
  • 1
  • 2
  • 3
  • 4

這和我們之前事件委托的例子很像,但是這里我們輸出的不是對應(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è)方法。
用法如下:


    
  • 1
  • 2
  • 3
  • 4

每次到了立即執(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,到底是什么?

3.1 什么是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

相關(guān)文章

  • 前端進(jìn)擊的巨人(三):作用域走進(jìn)閉包

    摘要:進(jìn)擊的巨人第三篇,本篇就作用域作用域鏈閉包等知識點(diǎn),一一擊破。在此我們遵照的方式,暫且稱是閉包。所以,一名合格的前端,除了會用閉包,還要正確的解除閉包引用。 進(jìn)擊的巨人第三篇,本篇就作用域、作用域鏈、閉包等知識點(diǎn),一一擊破。 showImg(https://segmentfault.com/img/bVburWd?w=1280&h=854); 作用域 作用域:負(fù)責(zé)收集并維護(hù)由所有聲明的...

    Vicky 評論0 收藏0
  • javascript知識點(diǎn)

    摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會討論安全的類型檢測惰性載入函數(shù)凍結(jié)對象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...

    Karrdy 評論0 收藏0
  • JavaScript - 收藏集 - 掘金

    摘要:插件開發(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)大而且簡單的方式。插....

    izhuhaodev 評論0 收藏0
  • JavaScript:面試頻繁出現(xiàn)的幾個(gè)易錯(cuò)點(diǎn)

    摘要:針對于面向?qū)ο缶幊痰?。因?yàn)槊嫦驅(qū)ο缶褪轻槍ο罄又械氖睾騺磉M(jìn)行執(zhí)行某些動作。這就是閉包的用途之一延續(xù)變量周期。把變量放在閉包里面和放在全局變量里面,影響是一致的。 1.前言 這段時(shí)間,金三銀四,很多人面試,很多人分享面試題。在前段時(shí)間,我也臨時(shí)擔(dān)任面試官,為了大概了解面試者的水平,我也寫了一份題目,面試了幾個(gè)前端開發(fā)者。在這段時(shí)間里面,我在學(xué),在寫設(shè)計(jì)模式的一些知識,想不到的設(shè)計(jì)模式...

    VincentFF 評論0 收藏0
  • 理解閉包

    摘要:我的理解就是還處于被引用狀態(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...

    fox_soyoung 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<