成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Js 中的閉包和this

AlphaWatch / 725人閱讀

摘要:自由變量指的是不在函數(shù)內(nèi)部聲明的變量。作用域鏈就是在所有內(nèi)部環(huán)境中查找變量的鏈?zhǔn)奖?。閉包的形式閉包的過(guò)程寫的不是很嚴(yán)謹(jǐn)。

要弄懂這個(gè)問(wèn)題首先要搞清楚一個(gè)概念, 執(zhí)行上下文。

執(zhí)行上下文

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

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

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

由于JS是單線程的,一次只能發(fā)生一件事情,其他事情會(huì)放在指定上下文棧中排隊(duì)。

js解釋器在初始化執(zhí)行代碼時(shí),會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文到棧中,接著隨著每次函數(shù)的調(diào)用都會(huì)創(chuàng)建并壓入一個(gè)新的執(zhí)行上下文棧。函數(shù)執(zhí)行后,該執(zhí)行上下文被彈出。

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

單線程

同步執(zhí)行

一個(gè)全局上下文

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

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

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

## 創(chuàng)建階段

初始化作用域鏈
創(chuàng)建變量對(duì)象
創(chuàng)建arguments
掃描函數(shù)聲明
掃描變量聲明
求this

執(zhí)行階段

初始化變量和函數(shù)的引用
執(zhí)行代碼
this
在函數(shù)執(zhí)行時(shí),this 總是指向調(diào)用該函數(shù)的對(duì)象。要判斷 this 的指向,其實(shí)就是判斷 this 所在的函數(shù)屬于誰(shuí)。

指向調(diào)用對(duì)象:

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
obj.foo(); // 2
// 指向全局對(duì)象

function foo() {
    console.log( this.a );
}
var a = 2;
foo(); // 2
// 注意

//接上
var bar = foo
a = 3
bar() // 3不是2

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

//再繞一點(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àn)閮?yōu)先讀取foo中設(shè)置的a,類似作用域的原理嗎?

通過(guò)打印foo和doFoo的this,可以知道,他們的this都是指向window的,他們的操作會(huì)修改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é)果可以證實(shí)我們的猜測(cè)。

用new構(gòu)造就指向新對(duì)象:
a = 4
function A() {
    this.a = 3
    this.callA = function() {
        console.log(this.a)
    }
}
A() // 返回undefined, A().callA會(huì)報(bào)錯(cuò)。callA被保存在window上
var a = new A()
a.callA() // 3,callA在new A返回的對(duì)象里
apply/call/bind

大家應(yīng)該都很熟悉,令this指向傳遞的第一個(gè)參數(shù),如果第一個(gè)參數(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返回一個(gè)新的函數(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ù)比較特殊,沒(méi)有自己的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
//對(duì)比一下
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)頁(yè)
// ...

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

變量對(duì)象

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

變量對(duì)象式一個(gè)抽象的概念,在不同的上下文中,表示不同的對(duì)象

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

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

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

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

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

形參

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

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

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

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

舉個(gè)例子

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)

來(lái)分析一下過(guò)程

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

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

執(zhí)行代碼時(shí)
通過(guò)最后的console可以發(fā)現(xiàn),函數(shù)聲明可以被覆蓋

作用域鏈
先了解一下作用域

作用域

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

全局作用域:

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

在最外層定義的變量;

全局對(duì)象的屬性

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

局部作用域:

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

作用域鏈

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

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

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

var s = 3
function a () {
  console.log(s)
}
function b () {
  var s = 6
  a()
}
b() // 3,不是6

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

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

function foo() {
   function bar() {
       ...
   }
}
foo.[[scope]] = [
 globalContext.VO
];
bar.[[scope]] = [
   fooContext.AO,
   globalContext.VO
];

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

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

閉包是什么

閉包按照mdn的定義是可以訪問(wèn)自由變量的函數(shù)。

自由變量前面提到過(guò),指的是不在函數(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"
閉包的過(guò)程

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

運(yùn)行函數(shù)a
創(chuàng)建函數(shù)a的VO,包括變量num和函數(shù)b
定義函數(shù)b的時(shí)候,會(huì)保存a的變量對(duì)象VO和全局變量對(duì)象到[[scope]]中
返回函數(shù)b,保存到c1
運(yùn)行c1
創(chuàng)建c1的作用域鏈,該作用域鏈保存了a的變量對(duì)象VO
創(chuàng)建c1的VO
運(yùn)行c1,這是發(fā)現(xiàn)需要訪問(wèn)變量num,在當(dāng)前VO中不存在,于是通過(guò)作用域鏈進(jìn)行訪問(wèn),找到了保存在a的VO中的num,對(duì)它進(jìn)行操作,num的值被設(shè)置成2
再次運(yùn)行c1,重復(fù)第二步的操作,num的值設(shè)置成3
一些問(wèn)題
通過(guò)上面的運(yùn)行結(jié)果,我們可以觀察到,c2所訪問(wèn)num變量跟c1訪問(wèn)的num變量不是同一個(gè)變量。我們可以修改一下代碼,來(lái)確認(rèn)自己的猜想

function a() {
    var x = {y : 4}
    function b() {
        return x
    }
    return b
}
var c1 = a()
var c2 = a()
c1 === c2()  // false

因此我們可以確定,閉包所訪問(wèn)的變量,是每次運(yùn)行父函數(shù)都重新創(chuàng)建,互相獨(dú)立的。
注意,同一個(gè)函數(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ǔ)充一個(gè)查看作用域鏈和閉包的技巧

打開chrome控制臺(tái)

console.dir(r.b)
f b() {
    [[Scopes]]: [
        {x:0}, 
        {type: "global", name: "", object: Window}
    ]    
}

最后
最后,我們?cè)賮?lái)總結(jié)一下執(zhí)行上下文的過(guò)程,加深下印象

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)建活動(dòng)對(duì)象AO

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

第三步是將活動(dòng)對(duì)象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
];

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/95903.html

相關(guān)文章

  • JS核心知識(shí)點(diǎn)梳理——上下文、作用域、閉包、this(中)

    摘要:引言滿滿的干貨,面試必系列,參考大量資料,并集合自己的理解以及相關(guān)的面試題,對(duì)核心知識(shí)點(diǎn)中的作用域閉包上下文進(jìn)行了梳理。本篇重點(diǎn)介紹閉包和。所以,有另一種說(shuō)法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 滿滿的干貨,面試必bei系列,參考大量資料,并集...

    rottengeek 評(píng)論0 收藏0
  • JS腳丫系列】重溫閉包

    摘要:內(nèi)部的稱為內(nèi)部函數(shù)或閉包函數(shù)。過(guò)度使用閉包會(huì)導(dǎo)致性能下降。,閉包函數(shù)分為定義時(shí),和運(yùn)行時(shí)。循環(huán)會(huì)先運(yùn)行完畢,此時(shí),閉包函數(shù)并沒(méi)有運(yùn)行。閉包只能取得外部函數(shù)中的最后一個(gè)值。事件綁定種的匿名函數(shù)也是閉包函數(shù)。而對(duì)象中的閉包函數(shù),指向。 閉包概念解釋: 閉包(也叫詞法閉包或者函數(shù)閉包)。 在一個(gè)函數(shù)parent內(nèi)聲明另一個(gè)函數(shù)child,形成了嵌套。函數(shù)child使用了函數(shù)parent的參數(shù)...

    MartinDai 評(píng)論0 收藏0
  • js 閉包的使用技巧

    摘要:閉包的學(xué)術(shù)定義先來(lái)參考下各大權(quán)威對(duì)閉包的學(xué)術(shù)定義百科閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。所以,有另一種說(shuō)法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 前言 上一章講解了閉包的底層實(shí)現(xiàn)細(xì)節(jié),我想大家對(duì)閉包的概念應(yīng)該也有了個(gè)大概印象,但是真要用簡(jiǎn)短的幾句話來(lái)說(shuō)清楚,這還真不是件容易的事。這里我們就來(lái)總結(jié)提煉下閉包的概念,以應(yīng)付那些非專人士的心血來(lái)潮。 閉包的學(xué)術(shù)...

    dendoink 評(píng)論0 收藏0
  • js閉包的理解

    摘要:一般來(lái)講,函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷毀,內(nèi)存中僅保存全局作用域,但是閉包的情況有所不同理解閉包的前提先理解另外兩個(gè)內(nèi)容作用域鏈垃圾回收作用域鏈當(dāng)代碼在執(zhí)行過(guò)程中,會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈。 閉包是javascript語(yǔ)言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包來(lái)實(shí)現(xiàn)。個(gè)人的理解是:函數(shù)中嵌套函數(shù)。 閉包的定義及其優(yōu)缺點(diǎn) 閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的...

    EasonTyler 評(píng)論0 收藏0
  • JS基礎(chǔ)-閉包

    摘要:如果不用的話,你實(shí)際上聲明了一個(gè)全局變量閉包有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域的變量,常見的創(chuàng)建方式就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù),通過(guò)另一個(gè)函數(shù)訪問(wèn)這個(gè)函數(shù)的局部變量。 變量作用域 變量的作用域無(wú)非就是兩種:全局變量和局部變量。 函數(shù)內(nèi)部聲明變量的時(shí)候,一定要使用var命令。如果不用的話,你實(shí)際上聲明了一個(gè)全局變量! 閉包 有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域的變量,常見的創(chuàng)建方式就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建...

    Scliang 評(píng)論0 收藏0
  • JS進(jìn)擊之路:閉包

    摘要:常見問(wèn)題說(shuō)到閉包相關(guān)的問(wèn)題,最典型的就是變量和指向這兩類問(wèn)題。如果有錯(cuò)誤或不嚴(yán)謹(jǐn)?shù)牡胤?,歡迎批評(píng)指正,如果喜歡,歡迎點(diǎn)贊。 引言 閉包這個(gè)詞對(duì)很多前端開發(fā)人員來(lái)說(shuō)既熟悉又陌生,熟悉是因?yàn)楹芏嗳硕加眠^(guò)閉包,但是用的時(shí)候不知道閉包,陌生是因?yàn)椴⒉焕斫忾]包,接下來(lái)這篇文章將會(huì)從多方面介紹閉包 定義 閉包是怎么定義的呢?當(dāng)函數(shù)可以記住并訪問(wèn)所在的詞法作用域時(shí),就產(chǎn)生了閉包,即使函數(shù)在當(dāng)前詞法作...

    tolerious 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<