国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入javascript——作用域和閉包

oogh / 2589人閱讀

摘要:注意由于閉包會額外的附帶函數的作用域內部匿名函數攜帶外部函數的作用域,因此,閉包會比其它函數多占用些內存空間,過度的使用可能會導致內存占用的增加。

  

作用域和作用域鏈是javascript中非常重要的特性,對于他們的理解直接關系到對于整個javascript體系的理解,而閉包又是對作用域的延伸,也是在實際開發(fā)中經常使用的一個特性,實際上,不僅僅是javascript,在很多語言中都提供了閉包的特性。

作用域

作用域是一個變量和函數的作用范圍,javascript中函數內聲明的所有變量在函數體內始終是可見的,在javascript中有全局作用域和局部作用域,但是沒有塊級作用域,局部變量的優(yōu)先級高于全局變量,通過幾個示例來了解下javascript中作用域的那些“潛規(guī)則”(這些也是在前端面試中經常問到的問題)。
1. 變量聲明提前
示例1:

var scope="global";
function scopeTest(){
    console.log(scope);
    var scope="local"  
}
scopeTest(); //undefined

此處的輸出是undefined,并沒有報錯,這是因為在前面我們提到的函數內的聲明在函數體內始終可見,上面的函數等效于:

var scope="global";
function scopeTest(){
    var scope;
    console.log(scope);
    scope="local"  
}
scopeTest(); //local

注意,如果忘記var,那么變量就被聲明為全局變量了。
2. 沒有塊級作用域
和其他我們常用的語言不同,在Javascript中沒有塊級作用域:

function scopeTest() {
    var scope = {};
    if (scope instanceof Object) {
        var j = 1;
        for (var i = 0; i < 10; i++) {
            //console.log(i);
        }
        console.log(i); //輸出10
    }
    console.log(j);//輸出1

}

在javascript中變量的作用范圍是函數級的,即在函數中所有的變量在整個函數中都有定義,這也帶來了一些我們稍不注意就會碰到的“潛規(guī)則”:

var scope = "hello";
function scopeTest() {
    console.log(scope);//①
    var scope = "no";
    console.log(scope);//②
}

在①處輸出的值竟然是undefined,簡直喪心病狂啊,我們已經定義了全局變量的值啊,這地方不應該為hello嗎?其實,上面的代碼等效于:

var scope = "hello";
function scopeTest() {
    var scope;
    console.log(scope);//①
    scope = "no";
    console.log(scope);//②
}

聲明提前、全局變量優(yōu)先級低于局部變量,根據這兩條規(guī)則就不難理解為什么輸出undefined了。

作用域鏈

在javascript中,每個函數都有自己的執(zhí)行上下文環(huán)境,當代碼在這個環(huán)境中執(zhí)行時,會創(chuàng)建變量對象的作用域鏈,作用域鏈是一個對象列表或對象鏈,它保證了變量對象的有序訪問。
作用域鏈的前端是當前代碼執(zhí)行環(huán)境的變量對象,常被稱之為“活躍對象”,變量的查找會從第一個鏈的對象開始,如果對象中包含變量屬性,那么就停止查找,如果沒有就會繼續(xù)向上級作用域鏈查找,直到找到全局對象中:

作用域鏈的逐級查找,也會影響到程序的性能,變量作用域鏈越長對性能影響越大,這也是我們盡量避免使用全局變量的一個主要原因。

閉包

基礎概念

作用域是理解閉包的一個前提,閉包是指在當前作用域內總是能訪問外部作用域中的變量。

function createClosure(){
    var name = "jack";
    return {
        setStr:function(){
            name = "rose";
        },
        getStr:function(){
            return name + ":hello";
        }
    }
}
var builder = new createClosure();
builder.setStr();
console.log(builder.getStr()); //rose:hello

上面的示例在函數中返回了兩個閉包,這兩個閉包都維持著對外部作用域的引用,因此不管在哪調用總是能夠訪問外部函數中的變量。在一個函數內部定義的函數,會將外部函數的活躍對象添加到自己的作用域鏈中,因此上面實例中通過內部函數能夠訪問外部函數的屬性,這也是javascript模擬私有變量的一種方式。

注意:由于閉包會額外的附帶函數的作用域(內部匿名函數攜帶外部函數的作用域),因此,閉包會比其它函數多占用些內存空間,過度的使用可能會導致內存占用的增加。

閉包中的變量
在使用閉包時,由于作用域鏈機制的影響,閉包只能取得內部函數的最后一個值,這引起的一個副作用就是如果內部函數在一個循環(huán)中,那么變量的值始終為最后一個值。

    //該實例不太合理,有一定延遲因素,此處主要為了說明閉包循環(huán)中存在的問題
    function timeManage() {
        for (var i = 0; i < 5; i++) {
            setTimeout(function() {
                console.log(i);
            },1000)
        };
    }

上面的程序并沒有按照我們預期的輸入1-5的數字,而是5次全部輸出了5。再來看一個示例:

function createClosure(){
    var result = [];
    for (var i = 0; i < 5; i++) {
        result[i] = function(){
            return i;
        }
    }
    return result;
}

調用createClosure()[0]()返回的是5,createClosure()[4]()返回值仍然是5。通過以上兩個例子可以看出閉包在帶有循環(huán)的內部函數使用時存在的問題:因為每個函數的作用域鏈中都保存著對外部函數(timeManage、createClosure)的活躍對象,因此,他們都引用著同一變量i,當外部函數返回時,此時的i值為5,所以內部的每個函數i的值也為5。
那么如何解決這個問題呢?我們可以通過匿名包裹器(匿名自執(zhí)行函數表達式)來強制返回預期的結果:

function timeManage() {
    for (var i = 0; i < 5; i++) {
        (function(num) {
            setTimeout(function() {
                console.log(num);
            }, 1000);
        })(i);
    }
}

或者在閉包匿名函數中再返回一個匿名函數賦值:

function timeManage() {
    for (var i = 0; i < 10; i++) {
        setTimeout((function(e) {
            return function() {
                console.log(e);
            }
        })(i), 1000)
    }
}
//timeManager();輸出1,2,3,4,5
function createClosure() {
    var result = [];
    for (var i = 0; i < 5; i++) {
        result[i] = function(num) {
            return function() {
                console.log(num);
            }
        }(i);
    }
    return result;
}
//createClosure()[1]()輸出1;createClosure()[2]()輸出2

無論是匿名包裹器還是通過嵌套匿名函數的方式,原理上都是由于函數是按值傳遞,因此會將變量i的值復制給實參num,在匿名函數的內部又創(chuàng)建了一個用于返回num的匿名函數,這樣每個函數都有了一個num的副本,互不影響了。

閉包中的this

在閉包中使用this時要特別注意,稍微不慎可能會引起問題。通常我們理解this對象是運行時基于函數綁定的,全局函數中this對象就是window對象,而當函數作為對象中的一個方法調用時,this等于這個對象(TODO 關于this做一次整理)。由于匿名函數的作用域是全局性的,因此閉包的this通常指向全局對象window:

var scope = "global";
var object = {
    scope:"local",
    getScope:function(){
        return function(){
            return this.scope;
        }
    }
}

調用object.getScope()()返回值為global而不是我們預期的local,前面我們說過閉包中內部匿名函數會攜帶外部函數的作用域,那為什么沒有取得外部函數的this呢?每個函數在被調用時,都會自動創(chuàng)建thisarguments,內部匿名函數在查找時,搜索到活躍對象中存在我們想要的變量,因此停止向外部函數中的查找,也就永遠不可能直接訪問外部函數中的變量了。總之,在閉包中函數作為某個對象的方法調用時,要特別注意,該方法內部匿名函數的this指向的是全局變量。
幸運的是我們可以很簡單的解決這個問題,只需要把外部函數作用域的this存放到一個閉包能訪問的變量里面即可:

var scope = "global";
var object = {
    scope:"local",
    getScope:function(){
        var that = this;
        return function(){
            return that.scope;
        }
    }
}

object.getScope()()返回值為local

內存與性能
由于閉包中包含與函數運行期上下文相同的作用域鏈引用,因此,會產生一定的負面作用,當函數中活躍對象和運行期上下文銷毀時,由于必要仍存在對活躍對象的引用,導致活躍對象無法銷毀,這意味著閉包比普通函數占用更多的內存空間,在IE瀏覽器下還可能會導致內存泄漏的問題,如下:

 function bindEvent(){
    var target = document.getElementById("elem");
    target.onclick = function(){
        console.log(target.name);
    }
 }

上面例子中匿名函數對外部對象target產生一個引用,只要是匿名函數存在,這個引用就不會消失,外部函數的target對象也不會被銷毀,這就產生了一個循環(huán)引用。解決方案是通過創(chuàng)建target.name副本減少對外部變量的循環(huán)引用以及手動重置對象:

  function bindEvent(){
    var target = document.getElementById("elem");
    var name = target.name;
    target.onclick = function(){
        console.log(name);
    }
    target = null;
 }

閉包中如果存在對外部變量的訪問,無疑增加了標識符的查找路徑,在一定的情況下,這也會造成性能方面的損失。解決此類問題的辦法我們前面也曾提到過:盡量將外部變量存入到局部變量中,減少作用域鏈的查找長度。

總結:閉包不是javascript獨有的特性,但是在javascript中有其獨特的表現形式,使用閉包我們可以在javascript中定義一些私有變量,甚至模仿出塊級作用域,但閉包在使用過程中,存在的問題我們也需要了解,這樣才能避免不必要問題的出現。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85279.html

相關文章

  • 【進階2-3期】JavaScript深入閉包面試題解

    摘要:閉包面試題解由于作用域鏈機制的影響,閉包只能取得內部函數的最后一個值,這引起的一個副作用就是如果內部函數在一個循環(huán)中,那么變量的值始終為最后一個值。 (關注福利,關注本公眾號回復[資料]領取優(yōu)質前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第8天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了...

    alanoddsoff 評論0 收藏0
  • JavaScript深入閉包

    摘要:深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。定義對閉包的定義為閉包是指那些能夠訪問自由變量的函數。 JavaScript深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。 定義 MDN 對閉包的定義為: 閉包是指那些能夠訪問自由變量的函數。 那什么是自由變量呢? 自由變量是指在函數中使用的,但既不是函數參數也...

    caige 評論0 收藏0
  • 【進階2-2期】JavaScript深入之從作用域鏈理解閉包

    摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數,也不是局部變量,所以是自由變量。 (關注福利,關注本公眾號回復[資料]領取優(yōu)質前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計...

    simpleapples 評論0 收藏0
  • 20170917 前端開發(fā)周報:JavaScript函數式編程、作用域和閉包

    摘要:用函數式編程對進行斷舍離當從業(yè)的老司機學會函數式編程時,他扔掉了的特性,也不用面向對象了,最后發(fā)現了真愛啊作用域和閉包作用域和閉包在里非常重要。旨在幫助非函數式編程的同學,能快速切入到函數式編程的理念。 1、用函數式編程對JavaScript進行斷舍離 當從業(yè)20的JavaScript老司機學會函數式編程時,他扔掉了90%的特性,也不用面向對象了,最后發(fā)現了真愛啊!!! https:/...

    tomener 評論0 收藏0
  • 20170917 前端開發(fā)周報:JavaScript函數式編程、作用域和閉包

    摘要:用函數式編程對進行斷舍離當從業(yè)的老司機學會函數式編程時,他扔掉了的特性,也不用面向對象了,最后發(fā)現了真愛啊作用域和閉包作用域和閉包在里非常重要。旨在幫助非函數式編程的同學,能快速切入到函數式編程的理念。 1、用函數式編程對JavaScript進行斷舍離 當從業(yè)20的JavaScript老司機學會函數式編程時,他扔掉了90%的特性,也不用面向對象了,最后發(fā)現了真愛啊!!! https:/...

    cyixlq 評論0 收藏0

發(fā)表評論

0條評論

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