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

資訊專欄INFORMATION COLUMN

一次搞定this和閉包

Airy / 914人閱讀

摘要:他的組成如下對的就是你關(guān)注的那個變量對象作用域鏈跟閉包相關(guān)由于是單線程的,一次只能發(fā)生一件事情,其他事情會放在指定上下文棧中排隊。

閉包和this,是兩個相當(dāng)高頻的考點(diǎn),然而你有沒有想過,實際上他們兩個都跟同一個知識點(diǎn)相關(guān)?

有請我們的這篇文章的主角,執(zhí)行上下文

執(zhí)行上下文
執(zhí)行上下文是什么

可以簡單理解執(zhí)行上下文是js代碼執(zhí)行的環(huán)境,當(dāng)js執(zhí)行一段可執(zhí)行代碼時,會創(chuàng)建對應(yīng)的執(zhí)行上下文。他的組成如下

executionContextObj = {
    this: 對的就是你關(guān)注的那個this,
    VO:變量對象,
    scopeChain: 作用域鏈,跟閉包相關(guān)
}

由于JS是單線程的,一次只能發(fā)生一件事情,其他事情會放在指定上下文棧中排隊。js解釋器在初始化執(zhí)行代碼時,會創(chuàng)建一個全局執(zhí)行上下文到棧中,接著隨著每次函數(shù)的調(diào)用都會創(chuàng)建并壓入一個新的執(zhí)行上下文棧。函數(shù)執(zhí)行后,該執(zhí)行上下文被彈出。

五個關(guān)鍵點(diǎn):

單線程

同步執(zhí)行

一個全局上下文

無限制函數(shù)上下文

每次函數(shù)調(diào)用創(chuàng)建新的上下文,包括調(diào)用自己

執(zhí)行上下文建立的步奏

創(chuàng)建階段

初始化作用域鏈

創(chuàng)建變量對象

創(chuàng)建arguments

掃描函數(shù)聲明

掃描變量聲明

求this

執(zhí)行階段

初始化變量和函數(shù)的引用

執(zhí)行代碼

this

在函數(shù)執(zhí)行時,this 總是指向調(diào)用該函數(shù)的對象。要判斷 this 的指向,其實就是判斷 this 所在的函數(shù)屬于誰。

指向調(diào)用對象
function foo() {
    console.log( this.a );
}

var obj = {
    a: 2,
    foo: foo
};

obj.foo(); // 2
指向全局對象
function foo() {
    console.log( this.a );
}

var a = 2;

foo(); // 2

注意

//接上

var bar = foo

a = 3
bar() // 3不是2

通過這個例子可以更加了解this是函數(shù)調(diào)用時才確定的

再繞一點(diǎn)

function foo() {
    console.log( this.a );
}

function doFoo(fn) {
    this.a = 4
    fn();
}

var obj = {
    a: 2,
    foo: foo
};

var a =3

doFoo( obj.foo ); // 4

function foo() {
    this.a = 1
    console.log( this.a );
}

function doFoo(fn) {
    this.a = 4
    fn();
}

var obj = {
    a: 2,
    foo: foo
};

var a =3

doFoo( obj.foo ); // 1

這是為什么呢?是因為優(yōu)先讀取foo中設(shè)置的a,類似作用域的原理嗎?

通過打印foo和doFoo的this,可以知道,他們的this都是指向window的,他們的操作會修改window中的a的值。并不是優(yōu)先讀取foo中設(shè)置的a

因此如果把代碼改成

function foo() {
    setTimeout(() => this.a = 1,0)
    console.log( this.a );
}

function doFoo(fn) {
    this.a = 4
    fn();
}

var obj = {
    a: 2,
    foo: foo
};

var a =3

doFoo( obj.foo ); // 4
setTimeout(obj.foo,0) // 1

上面的代碼結(jié)果可以證實我們的猜測。

用new構(gòu)造就指向新對象
a = 4
function A() {

    this.a = 3
    this.callA = function() {
        console.log(this.a)
    }
}

A() // 返回undefined, A().callA會報錯。callA被保存在window上

var a = new A()

a.callA() // 3,callA在new A返回的對象里
apply/call/bind

大家應(yīng)該都很熟悉,令this指向傳遞的第一個參數(shù),如果第一個參數(shù)為null,undefined或是不傳,則指向全局變量

a = 3
function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

foo.call( obj ); // 2

foo.call( null ); // 3

foo.call( undefined ); // 3

foo.call(  ); // 3

var obj2 = {
    a: 5,
    foo
}

obj2.foo.call() // 3,不是5!

//bind返回一個新的函數(shù)
function foo(something) {
    console.log( this.a, something );
    return this.a + something;
}

var obj =
    a: 2
};

var bar = foo.bind( obj );

var b = bar( 3 ); // 2 3
console.log( b ); // 5
箭頭函數(shù)

箭頭函數(shù)比較特殊,沒有自己的this,它使用封閉執(zhí)行上下文(函數(shù)或是global)的 this 值。

var x=11;
var obj={
 x:22,
 say:()=>{
   console.log(this.x); //this指向window
 }
}
obj.say();// 11
obj.say.call({x:13}) // 11
x = 14
obj.say() // 14

//對比一下

var obj2={
 x:22,
 say() {
   console.log(this.x); //this指向obj2
 }
}
obj2.say();// 22
obj2.say.call({x:13}) // 13

事件監(jiān)聽函數(shù)

指向被綁定的dom元素

document.body.addEventListener("click",function(){
    console.log(this)
}
)

// 點(diǎn)擊網(wǎng)頁

// ...
HTML

HTML標(biāo)簽的屬性中是可能寫JS的,這種情況下this指代該HTML元素。

變量對象

變量對象是與執(zhí)行上下文相關(guān)的數(shù)據(jù)作用域,存儲了上下文中定義的變量和函數(shù)聲明。

變量對象式一個抽象的概念,在不同的上下文中,表示不同的對象

全局執(zhí)行上下文的變量對象

全局執(zhí)行上下文中,變量對象就是全局對象。
在頂層js代碼中,this指向全局對象,全局變量會作為該對象的屬性來被查詢。在瀏覽器中,window就是全局對象。

var a = 1
console.log(window.a) // 1
console.log(this.a) // 1
函數(shù)執(zhí)行上下文的變量對象

函數(shù)上下文中,變量對象VO就是活動對象AO。

初始化時,帶有arguments屬性。
函數(shù)代碼分成兩個階段執(zhí)行

進(jìn)入執(zhí)行上下文時
此時變量對象包括

形參

函數(shù)聲明,會替換已有變量對象

變量聲明,不會替換形參和函數(shù)

函數(shù)執(zhí)行

根據(jù)代碼修改變量對象的值

舉個例子

function test (a,c) {
  console.log(a, b, c, d) // 5 undefined [Function: c] undefined
  var b = 3;
  a = 4
  function c () {

  }
  var d = function () {

  }
  console.log(a, b, c, d) // 4 3 [Function: c] [Function: d]

  var c = 5
  console.log(a, b, c, d) // 4 3 5 [Function: d]

}

test(5,6)

來分析一下過程

1.創(chuàng)建執(zhí)行上下文時

VO = {

arguments: {0:5},
a: 5,
b: undefined,
c: [Function], //函數(shù)C覆蓋了參數(shù)c,但是變量聲明c無法覆蓋函數(shù)c的聲明
d: undefined, // 函數(shù)表達(dá)式?jīng)]有提升,在執(zhí)行到對應(yīng)語句之前為undefined

}

執(zhí)行代碼時

通過最后的console可以發(fā)現(xiàn),函數(shù)聲明可以被覆蓋

作用域鏈

先了解一下作用域

作用域

變量與函數(shù)的可訪問范圍,控制著變量及函數(shù)的可見性與生命周期。分為全局作用域和局部作用域。

全局作用域:

在代碼中任何地方都能訪問到的對象擁有全局作用域,有以下幾種:

在最外層定義的變量;

全局對象的屬性

任何地方隱式定義的變量(未定義直接賦值的變量),在任何地方隱式定義的變量都會定義在全局作用域中,即不通過 var 聲明直接賦值的變量。

局部作用域:

JavaScript的作用域是通過函數(shù)來定義的,在一個函數(shù)中定義的變量只對這個函數(shù)內(nèi)部可見,稱為函數(shù)(局部)作用域

作用域鏈

作用域鏈?zhǔn)且粋€對象列表,用以檢索上下文代碼中出現(xiàn)的標(biāo)識符。
標(biāo)識符可以理解為變量名稱,參數(shù),函數(shù)聲明。

函數(shù)在定義的時候會把父級的變量對象AO/VO的集合保存在內(nèi)部屬性[[scope]]中,該集合稱為作用域鏈。
自由變量指的是不在函數(shù)內(nèi)部聲明的變量。
當(dāng)函數(shù)需要訪問自由變量時,會順著作用域鏈來查找數(shù)據(jù)。子對象會一級一級的向上查找父對象的變量,父對象的變量對子對象是可見的,反之不成立。
作用域鏈就是在所有內(nèi)部環(huán)境中查找變量的鏈?zhǔn)奖怼?/p>

可以直接的說,JS采用了詞法作用域(靜態(tài)作用域),JS的函數(shù)運(yùn)行在他們被定義的作用域中,而不是他們被執(zhí)行的作用域。可以舉一個例子說明:

var s = 3
function a () {
  console.log(s)
}

function b () {
  var s = 6
  a()
}

b() // 3,不是6

如果js采用動態(tài)作用域,打印出來的應(yīng)該是6而不是3,這個例子說明了js是靜態(tài)作用域。

函數(shù)作用域鏈的偽代碼:

 
function foo() {
    function bar() {
        ...
    }
}

foo.[[scope]] = [
  globalContext.VO
];

bar.[[scope]] = [
    fooContext.AO,
    globalContext.VO
];

函數(shù)在運(yùn)行激活的時候,會先復(fù)制[[scope]]屬性創(chuàng)建作用域鏈,然后創(chuàng)建變量對象VO,然后將其加入到作用域鏈。

executionContextObj: {
    VO:{},
    scopeChain: [VO, [[scope]]]
}
閉包
閉包是什么

閉包按照mdn的定義是可以訪問自由變量的函數(shù)。自由變量前面提到過,指的是不在函數(shù)內(nèi)部聲明的變量。

閉包的形式
function a() {
    var num = 1
    function b() {
        console.log(num++)
    }
    return b
}

var c1 = a()
c1() // "1"
c1() // "2"

var c2 = a()
c2() // "1"
c2() // "2"
閉包的過程

寫的不是很嚴(yán)謹(jǐn)。可能省略了一些過程

運(yùn)行函數(shù)a

創(chuàng)建函數(shù)a的VO,包括變量num和函數(shù)b

定義函數(shù)b的時候,會保存a的變量對象VO和全局變量對象到[[scope]]中

返回函數(shù)b,保存到c1

運(yùn)行c1

創(chuàng)建c1的作用域鏈,該作用域鏈保存了a的變量對象VO

創(chuàng)建c1的VO

運(yùn)行c1,這是發(fā)現(xiàn)需要訪問變量num,在當(dāng)前VO中不存在,于是通過作用域鏈進(jìn)行訪問,找到了保存在a的VO中的num,對它進(jìn)行操作,num的值被設(shè)置成2

再次運(yùn)行c1,重復(fù)第二步的操作,num的值設(shè)置成3

一些問題

通過上面的運(yùn)行結(jié)果,我們可以觀察到,c2所訪問num變量跟c1訪問的num變量不是同一個變量。我們可以修改一下代碼,來確認(rèn)自己的猜想

function a() {
    var x = {y : 4}
    function b() {
        return x
    }
    return b
}

var c1 = a()


var c2 = a()
c1 === c2()  // false

因此我們可以確定,閉包所訪問的變量,是每次運(yùn)行父函數(shù)都重新創(chuàng)建,互相獨(dú)立的。
注意,同一個函數(shù)中創(chuàng)建的自由變量是可以在不同的閉包共享的

function a() {
    var x = 0
    function b() {
        console.log(x++)
    }
    function c() {
        console.log(x++)
    }
    
    return {
        b,
        c
    }
}

var r =  a()
r.b() // 0
r.c() // 1

補(bǔ)充一個查看作用域鏈和閉包的技巧
打開chrome控制臺

console.dir(r.b)

f b() {
    [[Scopes]]: [
        {x:0}, 
        {type: "global", name: "", object: Window}
    ]    
}
最后

最后,我們再來總結(jié)一下執(zhí)行上下文的過程,加深下印象

var scope = "global scope";
function checkscope(a){
    var scope2 = "local scope";
}

checkscope(5);
創(chuàng)建全局上下文執(zhí)行棧

創(chuàng)建全局變量globalContext.VO.

創(chuàng)建checkscope函數(shù)

將全局變量VO保存為作用域鏈,設(shè)置到函數(shù)的內(nèi)部屬性[[scope]]

checkscope.[[scope]] = [
    globalContext.VO
];
執(zhí)行checkscope函數(shù)

創(chuàng)建函數(shù)執(zhí)行上下文,將checkscope函數(shù)執(zhí)行上下文壓入執(zhí)行上下文棧

ECStack = [
    checkscopeContext,
    globalContext
];
函數(shù)執(zhí)行上下文創(chuàng)建階段

第一步是復(fù)制[[scope]],創(chuàng)建作用域鏈

checkscopeContext = {
    Scope: checkscope.[[scope]],
}

第二步是創(chuàng)建活動對象AO

checkscopeContext = {
    AO: {
        arguments: {
            0: 5
            length: 1
        },
        a: 5
        scope2: undefined
    },
    Scope: checkscope.[[scope]],
}

第三步是將活動對象AO放入作用域鏈頂端

checkscopeContext = {
    AO: {
        arguments: {
            0: 5
            length: 1
        },
        a: 5
        scope2: undefined
    },
    Scope:  [AO, checkscope.[[scope]]],
}

第四步,求出this,上下文創(chuàng)建階段結(jié)束

這里的this等于window

進(jìn)入函數(shù)執(zhí)行階段

隨著函數(shù)執(zhí)行,修改AO的值

    AO: {
        arguments: {
            0: 5
            length: 1
        },
        a: 5
        scope2: "local scope"
    },
函數(shù)執(zhí)行完畢

函數(shù)上下文從執(zhí)行上下文棧彈出

ECStack = [
    globalContext
];

文章寫的比較長,涉及的范圍也比較廣,可能有不少的錯誤,希望大家可以指正。

本文章為前端進(jìn)階系列的一部分,
歡迎關(guān)注和star本博客或是關(guān)注我的github

參考

深入理解ES6箭頭函數(shù)中的this

你不知道的JS上卷

JavaScript深入之執(zhí)行上下文棧

理解JavaScript的作用域鏈

JavaScript深入之變量對象

深入理解JavaScript系列(12):變量對象(Variable Object)

了解JavaScript的執(zhí)行上下文

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

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

相關(guān)文章

  • 查漏補(bǔ)缺 - 收藏集 - 掘金

    摘要:醞釀許久之后,筆者準(zhǔn)備接下來撰寫前端面試題系列文章,內(nèi)容涵蓋瀏覽器框架分鐘搞定常用基礎(chǔ)知識前端掘金基礎(chǔ)智商劃重點(diǎn)在實際開發(fā)中,已經(jīng)非常普及了。 這道題--致敬各位10年阿里的前端開發(fā) - 掘金很巧合,我在認(rèn)識了兩位同是10年工作經(jīng)驗的阿里前端開發(fā)小伙伴,不但要向前輩學(xué)習(xí),我有時候還會選擇另一種方法逗逗他們,拿了網(wǎng)上一道經(jīng)典面試題,可能我連去阿里面試的機(jī)會都沒有,但是我感受到了一次面試1...

    YuboonaZhang 評論0 收藏0
  • Javascript currying柯里化詳解

    摘要:面試題實現(xiàn)結(jié)果,題的核心就是問的的柯里化先說說什么是柯里化,看過許多關(guān)于柯里化的文章,始終搞不太清楚,例如柯里化是把接受多個參數(shù)的函數(shù)變換成接受一個單一參數(shù)最初函數(shù)的第一個參數(shù)的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。 面試題:實現(xiàn)add(1)(2)(3) //結(jié)果 = 6,題的核心就是問的js的柯里化 先說說什么是柯里化,看過許多關(guān)于柯里化的文章,始終搞不太清楚,例如...

    mengbo 評論0 收藏0
  • 前端基礎(chǔ)進(jìn)階(八):深入詳解函數(shù)的柯里化

    摘要:函數(shù)被轉(zhuǎn)化之后得到柯里化函數(shù),能夠處理的所有剩余參數(shù)。因此柯里化也被稱為部分求值。那么函數(shù)的柯里化函數(shù)則可以如下因此下面的運(yùn)算方式是等價的。而這里對于函數(shù)參數(shù)的自由處理,正是柯里化的核心所在。額外知識補(bǔ)充無限參數(shù)的柯里化。 showImg(https://segmentfault.com/img/remote/1460000008493346); 柯里化是函數(shù)的一個比較高級的應(yīng)用,想要...

    kk_miles 評論0 收藏0
  • Java與groovy混編 —— 一種兼顧接口清晰實現(xiàn)敏捷的開發(fā)方式

    摘要:原文鏈接有大量平均水平左右的工人可被選擇參與進(jìn)來這意味著好招人有成熟的大量的程序庫可供選擇這意味著大多數(shù)項目都是既有程序庫的拼裝,標(biāo)準(zhǔn)化程度高而定制化場景少開發(fā)工具測試工具問題排查工具完善,成熟基本上沒有團(tuán)隊愿意在時間緊任務(wù)重的項目情況 原文鏈接:http://pfmiles.github.io/blog/java-groovy-mixed/ 有大量平均水平左右的工人可被選擇、參與...

    LittleLiByte 評論0 收藏0
  • javascript 閉包 ---- js進(jìn)化之路

    摘要:閉包利用的,其實就是作用域嵌套情況下,內(nèi)部作用域可以訪問外部作用域這一特性。之所以要將閉包和垃圾回收策略聯(lián)系在一起,是因為這涉及到閉包的一些重要特性,如變量暫時存儲和內(nèi)存泄漏。因為匿名函數(shù)回調(diào)的閉包實際引用的是變量,而非變量的值。 本文旨在總結(jié)在js學(xué)習(xí)過程中,對閉包的思考,理解,以及一些反思,如有不足,還請大家指教 閉包---closure 閉包是js中比較特殊的一個概念,其特殊之處...

    HtmlCssJs 評論0 收藏0

發(fā)表評論

0條評論

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