梳理 this 關(guān)鍵字的指向

一、this 指向梳理圖

this 的指向梳理圖

二、逐個(gè)學(xué)習(xí)

先說(shuō)明上下文(context)和作用域(scope)的區(qū)別:

  • 作用域(scope): 指變量、函數(shù)、對(duì)象的可訪問(wèn)性
  • 上下文(context):this 在同一作用域中的值
為什么設(shè)計(jì)出 this ?

函數(shù)可以在不同的運(yùn)行環(huán)境執(zhí)行,所以需要有一種機(jī)制,能夠在函數(shù)體內(nèi)部獲得當(dāng)前的運(yùn)行環(huán)境(context)。this 的設(shè)計(jì)目的就是在函數(shù)體內(nèi)部,指代函數(shù)當(dāng)前的運(yùn)行環(huán)境。

2.1 全局上下文

說(shuō)明:全局上下文、全局環(huán)境其實(shí)都是全局上下文環(huán)境 的簡(jiǎn)稱,好多文章兩種簡(jiǎn)稱都在用。

函數(shù)在瀏覽器全局上下文環(huán)境中被調(diào)用時(shí):

  • 非嚴(yán)格模式:this 指向全局對(duì)象 window
  • 嚴(yán)格模式:this 指向 undefined
demo 2-1
2.2 構(gòu)造函數(shù)
2.2.1 通過(guò) new 調(diào)用構(gòu)造函數(shù)

需要提前了解 new 關(guān)鍵字的底層原理。可稍后看文章底部。

構(gòu)造函數(shù)中的 this 根據(jù)構(gòu)造函數(shù)返回值的不同,指向也不同

  • 構(gòu)造函數(shù)顯式 return 一個(gè)對(duì)象類型(包括:數(shù)組、函數(shù)等):this 指向返回的對(duì)象
demo 2-2.1-1
  • 構(gòu)造函數(shù)顯式 return 一個(gè)非對(duì)象類型(基礎(chǔ)類型):this 指向?qū)嵗龑?duì)象
demo 2-2.1-2
  • 未顯示指定 return, 構(gòu)造函數(shù)會(huì)默認(rèn)返回實(shí)例對(duì)象,而且 this 指向這個(gè)實(shí)例對(duì)象


    demo 2-2.1-2
2.2.2 直接調(diào)用構(gòu)造函數(shù),當(dāng)做普通函數(shù)

this 指向全局對(duì)象 window。

demo 2-2.2
2.3 對(duì)象方法中的 this

當(dāng)以對(duì)象方法調(diào)用函數(shù)時(shí),this 指向調(diào)用當(dāng)前函數(shù)的對(duì)象。

function say() {
  console.log(this.name);
}
const person = {
  name: 'lee',
  say
}
person.say();
demo 2-3.1

先在外部定義函數(shù),并未影響 this 的綁定,關(guān)鍵還是看誰(shuí)調(diào)用了這個(gè)函數(shù)。


demo 2-3.2
2.4 getter 與 setter 中的 this

this 指向調(diào)用 getter、setter 的對(duì)象。

從下面例子中的 temp 對(duì)象能夠看出,通過(guò) temp.prop = person.prop; ,將 getter 與 setter 賦予 temp 對(duì)象,之后調(diào)用 temp.prop 時(shí),this 指向的是 temp 對(duì)象,所以不會(huì)對(duì) person 產(chǎn)生影響。

class Person {
  constructor() {
    this.name = 'lee';
  }
  get prop() {
    return this.name;
  }

  set prop(value) {
    this.name = value;
  }
}


const person = new Person();
console.log("===============person 實(shí)例對(duì)象===============");
console.log('修改前 person.name:',person.prop);
person.prop = 'nacy';
console.log('修改后 person.name:',person.prop);

console.log("=============== temp 對(duì)象===============");
const temp = {
  name: 'temp'
}
temp.prop = person.prop;
console.log('修改前 temp.name:',temp.prop);
temp.prop = 'andy';
console.log('修改后 temp.name:',temp.prop);

console.log('對(duì)person沒(méi)影響:',person.prop);

運(yùn)行結(jié)果:

demo 2-4
2.5 原型鏈中的 this

this 指向調(diào)函數(shù)的對(duì)象。

從下面的例子可以看出,child 實(shí)例對(duì)象的 say 方法是通過(guò)原型鏈在 Parent 的原型上找到的,但是因?yàn)檎{(diào)用 say 的是 child 實(shí)例對(duì)象,所以此時(shí) this 指向 child,輸出就是 child。

function Parent() {
  this.name = 'father';
}
Parent.prototype.say = function () {
  console.log(this.name);
}

function Child() {
  this.name = 'child';
}

Child.prototype = new Parent();

const child = new Child();
child.say();

運(yùn)行結(jié)果:

demo 2-5
2.6 箭頭函數(shù)中的 this

箭頭函數(shù)中沒(méi)有 this 綁定,this 的值由外層最近的非箭頭函數(shù)的作用域決定。

2.7 回調(diào)函數(shù)中的 this

setTimeout()、forEach()、dom 中的事件等,表現(xiàn)是類似的,這里采用《深入淺出 es6 》 中的例子:

var PageHandler = {

    id: "123456",

    init: function() {
        document.addEventListener("click", function(event) {
            this.doSomething(event.type);     // 錯(cuò)誤
        }, false);
    },

    doSomething: function(type) {
        console.log("Handling " + type  + " for " + this.id);
    }
};

此時(shí) this 指向的是 document ,容易被忽略掉。改用箭頭函數(shù)之后,this 的值通過(guò)查找作用域鏈來(lái)確定,外層最近的函數(shù)就是 init, 此時(shí) this 就指向了 PageHandler, 將能夠正確調(diào)用 doSomething 。

2.8 bind、call、apply 改變 this 的指向

bind 和 call 、apply 的區(qū)別:
bind 返回的是一個(gè)新的函數(shù),并未立即執(zhí)行原函數(shù)。
call、apply 會(huì)立即執(zhí)行原函數(shù)。

call 和 apply 的區(qū)別:
call 的參數(shù)需要一個(gè)個(gè)寫上
apply 的參數(shù)是一個(gè)數(shù)組。

2.9 this 優(yōu)先級(jí)

顯式綁定:call、apply、bind、new。
隱式綁定:根據(jù)調(diào)用關(guān)系確定 this 指向。

  • new 綁定的優(yōu)先級(jí)比 bind 綁定高。
    調(diào)用 bind 返回的新綁定函數(shù),如果被當(dāng)做構(gòu)造函數(shù),使用 new 關(guān)鍵字調(diào)用,bind 對(duì) this 的綁定將被忽略。

  • 箭頭函數(shù)的優(yōu)先級(jí)比 call、apply 高

var a = 10;
const say = () => {
  // this 原始指向 window
  console.log(this.a);
}

const obj = {
  a: 3
}

// 試圖用 call 改變 this 的指向
say.call(obj);

// 試圖用 bind 改變 this 的指向
const bindSay = say.bind(obj);
bindSay();

使用 letconst 聲明的變量不會(huì)掛載到 window 全局對(duì)象上,所以為了能正常打印出 10,這里使用了 var a = 10;

一句話總結(jié)

候策的陳述是:this 的指向,是在調(diào)用函數(shù)時(shí)根據(jù)執(zhí)行上下文所動(dòng)態(tài)確定的。

  1. 直觀的看,這句話對(duì)于對(duì)象方法和箭頭函數(shù)是容易理解的。
  2. call、apply、bind 這里要繞個(gè)彎子想,call、apply、bind 的內(nèi)部實(shí)現(xiàn)原理還是通過(guò)指定的對(duì)象來(lái)調(diào)用原函數(shù),所以也符合。
  3. 通過(guò) new 關(guān)鍵字調(diào)用構(gòu)造函數(shù),這里是一樣的思考方式,new 關(guān)鍵字的底層原理:
  • 創(chuàng)建一個(gè)空對(duì)象,作為將要返回的對(duì)象實(shí)例
  • 將這個(gè)空對(duì)象的原型,指向構(gòu)造函數(shù)的 prototype 屬性
  • 將這個(gè)空對(duì)象賦值給 this
  • 開(kāi)始執(zhí)行構(gòu)造函數(shù)內(nèi)部的代碼,返回這個(gè)新的對(duì)象。

所以這里 this 指向的是實(shí)例對(duì)象。

參考

阮一峰 this 關(guān)鍵字
JavaScript中call,apply,bind以及this的理解
候策: 一網(wǎng)打盡 this,對(duì)執(zhí)行上下文說(shuō) Yes

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.概念 在JavaScript中,this 是指當(dāng)前函數(shù)中正在執(zhí)行的上下文環(huán)境,因?yàn)檫@門語(yǔ)言擁有四種不同的函數(shù)調(diào)...
    BluesCurry閱讀 1,239評(píng)論 0 2
  • 轉(zhuǎn)載自:https://wangdoc.com/javascript/oop/this.html 1,涵義 thi...
    團(tuán)子家族_方糖咖啡閱讀 571評(píng)論 0 1
  • 前言 面試官出很多考題,基本都會(huì)變著方式來(lái)考察this指向,看候選人對(duì)JS基礎(chǔ)知識(shí)是否扎實(shí)。讀者可以先拉到底部看總...
    若川i閱讀 1,179評(píng)論 0 10
  • 1. this之謎 在JavaScript中,this是當(dāng)前執(zhí)行函數(shù)的上下文。因?yàn)镴avaScript有4種不同的...
    百里少龍閱讀 1,103評(píng)論 0 3
  • 夜幕中的東起路大橋 寂靜無(wú)聲的風(fēng)中 我聽(tīng)到冬蟲(chóng)在鳴叫 也聽(tīng)到 風(fēng)中有人在輕聲細(xì)語(yǔ) 浪漫,心中有追求,但虛也,那季飛...
    毛辣丁閱讀 266評(píng)論 3 7

友情鏈接更多精彩內(nèi)容