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

資訊專欄INFORMATION COLUMN

JavaScript:萬惡的 this 拿命來(三)

Cympros / 2274人閱讀

摘要:閉包執(zhí)行上下文決定了變量作用域而閉包,它其實是一種決策,是一種模式,讓我們可以靈活的改變變量作用域。所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。只要咱們弄明白閉包,其中的自然跑不掉。

閉包 this
  

執(zhí)行上下文決定了變量作用域

閉包,它其實是一種決策,是一種模式,讓我們可以靈活的改變變量作用域

按慣例,上栗子

var global = "global";

function outer(){
    var out = "outer";

    function middle(){
        var mid = "middle";

        function inner(){
            var in = "inner";
            console.log("globa : "+global, ",outer : "+out,
                      ",middle : "+mid, ",inner : "+in);
            //globa : global outer : outer middle : middle inner : inner
        }
        inner();
        console.log(in) //undefined
    }
    middle();
}
outer();

console.log(inner);  //undefined 
console.log(middle); //undefined
console.log(outer);  //undefined
console.log(global); //global
作用域

抽象:不同的"函數(shù)調(diào)用"會產(chǎn)生不同的"執(zhí)行上下文",不同的"執(zhí)行上下文"劃分出了不同的"變量作用域"。

具體:咱們應(yīng)該見過婚禮上的蛋糕,圓形的,一圈一圈的同心圓,中間最高,最外圍最低。此處的"最高"和"最低"可以理解為訪問權(quán)限,及里面能訪問外面,而外面訪問不了里面。

變量作用域
  

變量在inner函數(shù)中的作用域 = inner函數(shù)內(nèi)部作用域 + 所有外層的作用域

  

變量在middle函數(shù)中的作用域 = middle函數(shù)內(nèi)部作用域 + 所有外層的作用域 - inner函數(shù)內(nèi)部

  

變量在outer函數(shù)中的作用域 = outer函數(shù)內(nèi)部作用域 + 所有外層的作用域 - middle函數(shù)內(nèi)部作用域

備注:以上前提是基于用var聲明變量,省略var聲明變量會導(dǎo)致變量提升!通過這個栗子可以初看出作用域的端倪

優(yōu)點VS缺點

優(yōu)點:

合理的形成"管轄區(qū)",即"管轄區(qū)"內(nèi)它能被訪問到,"管轄區(qū)"外沒這人

不污染外層作用域

缺點

因為受到了"管轄",導(dǎo)致有時需要訪問它時卻訪問不到

閉包

引自阮一峰老師的博客 -- 學習Javascript閉包(Closure)

  

由于在Javascript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數(shù)內(nèi)部的函數(shù)"。

  

所以,在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。

只要咱們弄明白閉包,其中的this自然跑不掉。

上栗子

function constfuncs() {
    var funcs = [];
    for (var i = 0; i < 10; i++) {
        funcs[i] = function () {
            return i;
        }
    }
    return funcs;
}
var funcs = constfuncs();
alert(funcs[1]());

這是最近的一個問題,對于funcs[1]()是幾大家可以去試試

好吧,如果去試了可能會發(fā)現(xiàn),無論你funcs[1]()中輸入的時1還是9,它的都是10

這個就有意思了,為什么不論怎么輸入,結(jié)果都是10呢?如果你發(fā)出了這個疑問,那么你的潛意識里肯定是弄錯了件事:你認為

funcs[i] = function () {
     return i;
}

funcs[i]中的i會決定這個匿名函數(shù)中返回的i,其實不然。

for循環(huán)的過程中,會不停的創(chuàng)建函數(shù)

funcs[0] = function () {
     return i;
}                            //對象字面量被創(chuàng)建
...
funcs[9] = function () {
     return i;
}                            //對象字面量被創(chuàng)建

被創(chuàng)建的函數(shù)并沒有被立刻執(zhí)行,而是進入了等待隊列,等待你的主動調(diào)用

于此同時,i在等于9后又執(zhí)行了i++操作,現(xiàn)在i等于10

好的,現(xiàn)在咱們調(diào)用了funcs[1](),那么下一步函數(shù)會返回i,也就是10,所以無論你調(diào)用funcs[1]()還是funcs[9](),它都會返回10

現(xiàn)在改用閉包來解決這個問題了!

其實有一個值得玩味事情是:為什么遇到這樣的問題,我們會用閉包解決?
換一種說法是:為什么閉包能解決這個應(yīng)用場景的問題?

讓我們在回顧一下那句話

  

在本質(zhì)上,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來的一座橋梁。

因為我們正好需要一座橋梁,將外部的i和內(nèi)部的i關(guān)聯(lián)起來。

上栗子

function constfuncs() {
    var funcs = [];
    for (var i = 0; i < 10; i++) {
        funcs[i] = (function (i) {   // 標記1
            return function () {      /
                return i;             //   標記2(上下三行)
            };                        /
        })(i)                        //  標記3
    }
    return funcs;
}
var funcs = constfuncs();
console.log(funcs[1]());


- 標記2:我們在原本返回i的地方,返回了一個匿名函數(shù),里面再返回了i
- 標記3:我們傳入了i,架起了連接外部的橋梁
- 標記1:我們將標記3傳入的i作為參數(shù)傳入函數(shù),架起了連接內(nèi)部的橋梁

至此,每當一個for循環(huán)執(zhí)行一次,i也會傳入函數(shù)內(nèi)部被保存/記憶下來。

再來一發(fā)

function constfuncs() {
    var funcs = [];
    for (var i = 0; i < 10; i++) {
        funcs[i] = (function () {
            return i;
        }(i));
    }
    return funcs;
}
var funcs = constfuncs();
console.log(funcs[1]);

在這個栗子中,由于我們改變了寫法,導(dǎo)致最后的調(diào)用方法改變,但依舊是應(yīng)用閉包的特性。

如果這個栗子懂了,那閉包應(yīng)該懂了一大半了,如果還是有點暈,沒關(guān)系,咱們繼續(xù)往下看。

this

現(xiàn)在咱們說說閉包this之間的事

上栗子(瀏覽器/REPL中)

var name = "outer"

function Base(){}

Base.prototype.name = "base";

Base.prototype.log = function () {

    var info = "name is ";

    console.log(this.name);            // name is  base

    function inner(){
        console.log(info,this.name);   // name is  outer
    };
    inner();
};

var base = new Base();
base.log();

我們期望的是通過this訪問原型對象中的name,可是最后卻訪問到全局對象中的name屬性。

所以光有閉包還不夠,我們需要借助點別的技巧,改寫log函數(shù)

var name = "outer"

function Base(){}

Base.prototype.name = "base";

Base.prototype.log = function () {

    var info = "name is ";

    var self = this;         // 保存this

    function inner(){
        console.log(info,self.name);

    };
    inner();
};

var base = new Base();
base.log();

注解:使用self或that變量來保存this是約定俗成

原因:
- 由于inner函數(shù)定義在了log函數(shù)內(nèi)部,形成了閉包,導(dǎo)致內(nèi)部this"泛濫"指向了全局對象,現(xiàn)在做的就是在this還沒有"泛濫"的時候,保存它。

更常見的,是這樣的改寫log函數(shù)

var name = "outer"

function Base(){}

Base.prototype.name = "base";

Base.prototype.log = function () {

    var info = "name is ";

    var self = this;
    (function inner(){
        console.log(info,self.name);

    })(self);
};

var base = new Base();
base.log();

用一個"立即執(zhí)行的函數(shù)表達式"代替函數(shù)創(chuàng)建和調(diào)用。

再來一枚經(jīng)典栗子

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

相信大家對函數(shù)中的函數(shù)應(yīng)該有一定的警惕性了,this.scope的值是誰大家應(yīng)該也心中有值了,大家可以自己動手改一改,實踐才是王道!

立即執(zhí)行的函數(shù)表達式

最常見的版本大概是長這個樣子:

var name = "outer";

(function () {

    var name = "inner";
    console.log(name);          // inner
    console.log(this.name);     // outer
})();

相信大家看過上文后,應(yīng)該都明白了為什么this.name會輸出outer,下面來說說什么是立即執(zhí)行的函數(shù)表達式

咱們分兩步說:
 - 立即執(zhí)行
 - 函數(shù)表達式

常見的創(chuàng)建函數(shù)有這兩種

function Thing(){
    console.log("thing");
}                            //直接函數(shù)聲明
Thing();                     //函數(shù)調(diào)用


var thing = function () {
    console.log("thing");
};                           //函數(shù)字面量
thing();                     //函數(shù)調(diào)用

不妨試試這樣

thing
()

你會發(fā)現(xiàn)函數(shù)神奇的執(zhí)行了,也就是說函數(shù)名后面跟上一對小括號(),可以立刻調(diào)用函數(shù)。

那多帶帶的那一行thing是什么呢?它是函數(shù)的名字,是一個指針,但是在這里被解析成了表達式,多帶帶占了一行。

也就說我們通常執(zhí)行函數(shù)都是這么搞的,那么萬一這函數(shù)沒有名字呢?我們可以這樣

(function(){
    console.log("no name");
})();

(function(){
    console.log("no name")
}());

-function(){
    console.log("no name");
}();

+function(){
    console.log("no name");
}();

~function(){
    console.log("no name");
}();

!function(){
    console.log("no name");
}();

除了最上面兩個較常見外,其他的都挺怪異!但是他們都可以立即執(zhí)行!

  

注意函數(shù)的前面都有一個符號,"+" , "-" , "~" , "!" , "()"這些符號告訴解析器強制把這些函數(shù)聲明解析成函數(shù)表達式,最后的一對小括號()又讓這函數(shù)表達式立即執(zhí)行。

注意
如果要使用就請使用前兩個,用小括號()的方式是最正規(guī)也是慣例,其他的方式容易導(dǎo)致自己或者他人誤解,而且不符合編碼規(guī)范,強烈不推薦使用,自己在練習的時候可以玩一玩,體會體會。
應(yīng)用場景

1.你可以用立即執(zhí)行的函數(shù)表達式暴露公開的成員方法

var cal = (function () {

    return {

        add: function (a,b) {
            return a + b;
        },
        sub: function (a,b) {
            return a - b;
        }
    }
})();

cal.add(5,2)   // 7
cal.sub(4,1)   // 3

或者

var cal = (function () {

    var way = {};

    way.add = function (a,b) {
        return a + b;
    };
    way.sub = function (a,b) {
        return a - b;
    };

    return way;
})();

cal.add(3,6)   // 9
cal.sub(8,5)   // 3

2.續(xù)寫子模塊

cal.controller = (function () {

    var way = {};

    var result;
    way.set = function (args) {
        result = args;
    }

    way.get = function () {
        return result;
    }

    return way;
})();

cal.controller.set(123);   
cal.controller.get();   //  123
總結(jié)

細說變量作用域,比較其優(yōu)缺點

舉例說明閉包的概念,作用

舉例吐槽了閉包this之間的劇情,原因及解決方案

細說了立即執(zhí)行的函數(shù)表達式的概念及原理

列舉了其應(yīng)用場景

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

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/85645.html

相關(guān)文章

  • JavaScript:萬惡this命來(一)

    摘要:在腳本中,默認指向一個空對象,并不是指向,也不是指向。舉個栗子,在函數(shù)執(zhí)行后,覆蓋原先的值我們在外部定義了一個名為的全局變量,它會被默認添加到全局的屬性上。總結(jié)在不同的執(zhí)行環(huán)境中的默認指代通過省略聲明變量導(dǎo)致變量提升現(xiàn)象的發(fā)生及預(yù)防 侃侃JavaScript中的this this為何如此多變? this總是跟它的執(zhí)行上下文有關(guān),而在JavaScript總會有開辟新的執(zhí)行上...

    loostudy 評論0 收藏0
  • JavaScript:萬惡this命來(二)

    摘要:構(gòu)造函數(shù)對于被實例化的,我們稱之為構(gòu)造函數(shù),及使用關(guān)鍵字調(diào)用的,對于它們來說,會被改變,指向?qū)嵗I侠踝尤仲x上屬性通過關(guān)鍵字創(chuàng)建實例,改變函數(shù)內(nèi)部指向注解通過這個栗子,我們可以看出,通過創(chuàng)建構(gòu)造函數(shù)的實例,使得的指向改變,指向了實例本身。 用栗子說this Bug年年有,今年特別多 對于JavaScript這么靈活的語言來說,少了this怎么活! function ...

    fox_soyoung 評論0 收藏0
  • 設(shè)計模式之狀態(tài)模式

    摘要:為了實現(xiàn)這個正義偷笑又合理的訴求,你得先學會今天要介紹的設(shè)計模式,因為你們公司的這個流程可能就是用今天這個模式設(shè)計的。狀態(tài)模式對開閉原則的支持并不太好,新增狀態(tài)時,不僅得增加狀態(tài)類,還得修改原來已經(jīng)有的狀態(tài),讓之前的狀態(tài)切換到新增的狀態(tài)。一、定義你是否經(jīng)常請(偷)假(懶)?是不是對公司萬惡的請假申請流程深惡痛絕。有沒有想過偷偷改造這個萬惡的系統(tǒng),從 申請->項目經(jīng)理審批->部門審批->老板審...

    zhangke3016 評論0 收藏0
  • 不用 Spring Security 可否?試試這個小而美安全框架

    摘要:寫在前面在一款應(yīng)用的整個生命周期,我們都會談及該應(yīng)用的數(shù)據(jù)安全問題。用戶的合法性與數(shù)據(jù)的可見性是數(shù)據(jù)安全中非常重要的一部分。 寫在前面 在一款應(yīng)用的整個生命周期,我們都會談及該應(yīng)用的數(shù)據(jù)安全問題。用戶的合法性與數(shù)據(jù)的可見性是數(shù)據(jù)安全中非常重要的一部分。但是,一方面,不同的應(yīng)用對于數(shù)據(jù)的合法性和可見性要求的維度與粒度都有所區(qū)別;另一方面,以當前微服務(wù)、多服務(wù)的架構(gòu)方式,如何共享Sessi...

    toddmark 評論0 收藏0
  • 異步流程控制:7 行代碼學會 co 模塊

    摘要:而在中是迭代器生成器,被創(chuàng)造性的拿來做異步流程控制了。當執(zhí)行的時候,并不執(zhí)行函數(shù)體,而是返回一個迭代器。行代碼再看看文章開頭的行代碼首先生成一個迭代器,然后執(zhí)行一遍,得到的是一個對象,里面再執(zhí)行。 廣告招人:阿里巴巴招前端,在這里你可以享受大公司的福利和技術(shù)體系,也有小團隊的挑戰(zhàn)和成長空間。聯(lián)系: qingguang.meiqg at alibaba-inc.com 首先請原諒我的標題...

    tinna 評論0 收藏0

發(fā)表評論

0條評論

Cympros

|高級講師

TA的文章

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