亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

理解 JavaScript this

zombieda / 627人閱讀

摘要:回調(diào)函數(shù)在回調(diào)函數(shù)中的指向也會(huì)發(fā)生變化。在閉包回調(diào)函數(shù)賦值等場(chǎng)景下我們都可以利用來(lái)改變的指向,以達(dá)到我們的預(yù)期。文章參考系列文章理解閉包理解執(zhí)行棧理解作用域理解數(shù)據(jù)類(lèi)型與變量原文發(fā)布在我的公眾號(hào),點(diǎn)擊查看。

這是本系列的第 5 篇文章。

還記得上一篇文章中的閉包嗎?點(diǎn)擊查看文章 理解 JavaScript 閉包 。

在聊 this 之前,先來(lái)復(fù)習(xí)一下閉包:

var name = "Neil";

var person = {
  name: "Leo",
  sayHi: function() {
    return function () {
      return "Hi! My name is " + this.name;
    }
  }
};

person.sayHi()(); // "Hi! My name is Neil"

上一篇文章說(shuō),我們可以把閉包簡(jiǎn)單地理解為函數(shù)返回函數(shù)。所以這里的閉包結(jié)構(gòu)是:

// ...
function () {
  return "Hi! My name is " + this.name;
}
// ...

但是你有沒(méi)有發(fā)現(xiàn),這個(gè)函數(shù)執(zhí)行的結(jié)果是 “Hi! My name is Neil” 。等等,我不是叫 Leo 嗎?怎么給我改了個(gè)名字?!

我一分析,原來(lái)是 this 在其中作祟,且聽(tīng)我慢慢道來(lái)這“改名的由來(lái)”。

§ this 從何而來(lái)

首先,你得確保你已經(jīng)清楚執(zhí)行棧與執(zhí)行上下文的知識(shí)。點(diǎn)擊查看文章 理解 JavaScript 執(zhí)行棧 。

ECMAScript 5.1 中定義 this 的值為執(zhí)行上下文中的 ThisBinding。而 ThisBinding 簡(jiǎn)單來(lái)說(shuō)就是由 JS 引擎創(chuàng)建并維護(hù),在執(zhí)行時(shí)被設(shè)置為某個(gè)對(duì)象的引用。

在 JS 中有三種情況可以創(chuàng)建上下文:初始化全局環(huán)境、eval() 和執(zhí)行函數(shù)。

§ 全局中的 this
var num = 1;

function getName () {
  return "Leo";
}

this.num; // 1
this.getName(); // Leo

this == window; // true

當(dāng)我們?cè)跒g覽器中運(yùn)行這段代碼,JS 引擎會(huì)將 this 設(shè)置為 window 對(duì)象。而聲明的變量和函數(shù)被作為屬性掛載到 window 對(duì)象上。當(dāng)然,在嚴(yán)格模式下,全局中 this 的值設(shè)置為 undefined。

"use strcit";

var num = 1;
function getName () {
  return "Leo";
}

this.num; // TypeError
this.getName(); // TypeError

this == undefined; // true

開(kāi)啟嚴(yán)格模式后,全局 this 將指向 undefined,所以調(diào)用 this.num 會(huì)報(bào)錯(cuò)。

§ eval() 中的 this

eval() 不被推薦使用,我現(xiàn)在對(duì)其也不太熟悉,這里嘗試著說(shuō)一下。初學(xué)者可以直接跳到下一節(jié)。

結(jié)合所查閱的資料,目前我對(duì) eval() 的理解如下:

eval(...) 直接調(diào)用,被理解為是一個(gè) lvalue,也有說(shuō)是 left unchanged,字面理解為余下不變。什么是“余下不變”?我理解為直接調(diào)用 eval(...),其中代碼的執(zhí)行環(huán)境不變,依舊為當(dāng)前環(huán)境,this 也依舊指向當(dāng)前環(huán)境中的調(diào)用對(duì)象。

而使用類(lèi)似 (1, eval)(...) 的代碼,被稱(chēng)為間接調(diào)用。(1, eval) 是一個(gè)表達(dá)式,你可以這樣認(rèn)為 (true && eval) 或者 (0 : 0 ? eval)。間接調(diào)用的 eval 始終認(rèn)為其中的代碼執(zhí)行在全局環(huán)境,將 this 綁定到全局對(duì)象。

var x = "outer";
(function() {
  var x = "inner";
  // "direct call: inner"
  eval("console.log("direct call: " + x)");
  // "indirect call: outer"
  (1, eval)("console.log("indirect call: " + x)");
})();

關(guān)于 eval(),現(xiàn)在不敢確定,如有錯(cuò)誤,歡迎指正。

§ 函數(shù)中的 this ◆ 一般情況

首先,我們需要明確的是,在 JS 中函數(shù)也屬于對(duì)象,它可以擁有屬性,this 就是函數(shù)在執(zhí)行時(shí)獲得的屬性。一般情況下,在全局環(huán)境中直接調(diào)用函數(shù),函數(shù)中的 this 會(huì)在調(diào)用時(shí)被 JS 引擎設(shè)置為全局對(duì)象 window(同樣在嚴(yán)格模式下為 undefined)。

var name = "Leo";

function getName() {
  var name = "Neil";
  console.log(this); // [object Window]
  return this.name;
}

getName(); // Leo
◆ 作為對(duì)象的方法

函數(shù)可以作為對(duì)象的方法被該對(duì)象調(diào)用,那么這種情況 this 會(huì)被設(shè)置為該對(duì)象。

var name = "Neil";

var person = {
  name: "Leo",
  sayHi: function() {
    console.log(this); // person
    return "Hi! My name is " + this.name;
  }
};

person.sayHi(); // "Hi! My name is Leo"

當(dāng) person 對(duì)象調(diào)用 sayHi() 方法時(shí),this 被指向 person。

◆ 特殊的內(nèi)置函數(shù)

JS 還提供了一種供開(kāi)發(fā)者自定義 this 的方式,它提供了 3 種方式。

Function.prototype.call(thisArg, argArray)

Function.prototype.apply(thisArg [, arg1 [, args2, ...]])

Function.prototype.bind(thisArg [, arg1 [, args2, ...]])

我們可以通過(guò)設(shè)置 thisArg 的值,來(lái)自定義函數(shù)中 this 的指向。

var leo = {
  name: "Leo",
  sayHi: function () {
    return "Hi! My name is " + this.name;
  }
}

var neil = {
  name: "Neil"
};

leo.sayHi(); // "Hi! My name is Leo"
?leo.sayHi.call(neil); // "Hi! My name is Neil"

這里,我們通過(guò) call() 將 sayHi() 中 this 的指向綁定為 neil 對(duì)象,從而取代了默認(rèn) 的 this 指向 leo 對(duì)象。

關(guān)于函數(shù)的 call(), apply(), bind() 我將在后面另寫(xiě)一篇文章,敬請(qǐng)期待。

§ this 引起的令人費(fèi)解的現(xiàn)象 ◆ 閉包

通過(guò)前面的介紹,我想你對(duì) this 已經(jīng)有了初步的印象。那么,回到文章開(kāi)頭的問(wèn)題,this 是怎么改變了我的名字?換句話說(shuō),this 在閉包的影響下指向發(fā)生了怎樣的變動(dòng)?

再看一下代碼:

var name = "Neil";

var person = {
  name: "Leo",
  sayHi: function() {
    return function () {
      return "Hi! My name is " + this.name;
    }
  }
};

person.sayHi()(); // "Hi! My name is Neil"

通過(guò)上一篇文章 理解 JavaScript 閉包,函數(shù)返回函數(shù)會(huì)形成閉包。在這種情況下,閉包往往所執(zhí)行的環(huán)境與所定義的環(huán)境不一致,而 this 的值卻是在執(zhí)行時(shí)決定的。所以,當(dāng)上面代碼中的閉包在執(zhí)行時(shí),它所在的執(zhí)行上下文是全局環(huán)境,this 將被設(shè)置為 window(嚴(yán)格模式下為 undefined)。

怎么解決?我們可以利用 call / apply / bind 來(lái)修改 this 的指向。

var name = "Neil";

var person = {
  name: "Leo",
  sayHi: function() {
    return function () {
      return "Hi! My name is " + this.name;
    }
  }
};

person.sayHi().call(person); // "Hi! My name is Leo"

這里利用 call() 將 this 指向 person。OK,我的名字回來(lái)了,“Hi! My name is Leo” ^^

當(dāng)然,我們還有第二種解決方法,閉包的問(wèn)題就讓閉包自己解決。

var name = "Neil";

var person = {
  name: "Leo",
  sayHi: function() {
    var that = this; // 定義一個(gè)局部變量 that
    return function () {
      return "Hi! My name is " + that.name; // 在閉包中使用 that
    }
  }
};

person.sayHi()(); // "Hi! My name is Leo"

在 sayHi() 方法中定義一個(gè)局部變量,閉包可以將這個(gè)局部變量保存在內(nèi)存中,從而解決問(wèn)題。

◆ 回調(diào)函數(shù)

在回調(diào)函數(shù)中 this 的指向也會(huì)發(fā)生變化。

var name = "Neil";

var person = {
  name: "Leo",
  sayHi: function() {
    return "Hi! My name is " + this.name;
  }
};

var btn = document.querySelector("#btn");

btn.addEventListener("click", person.sayHi);
// "Hi! My name is undefined"

這里 this 既不指向 person,也不指向 window。那它指向什么?

btn 對(duì)象,它是一個(gè) DOM 對(duì)象,有一個(gè) onclick 方法,在這里定義為 person.sayHi。

{
  // ...
  onclick: person.sayHi
  // ...
}

所以,當(dāng)我們執(zhí)行上面的代碼,this.name 的值為 undefined,因?yàn)?btn 對(duì)象上沒(méi)有定義 name 屬性。我們給 btn 對(duì)象自定義一個(gè) name 屬性來(lái)驗(yàn)證一下。

var btn = document.querySelector("#btn");

btn.name = "Jackson";

btn.addEventListener("click", person.sayHi);
// "Hi! My name is Jackson"

原因說(shuō)清楚了,解決方案同樣可用過(guò) call / apply / bind 來(lái)改變 this 的指向,使其綁定到 person 對(duì)象。

btn.addEventListener("click", person.sayHi.bind(person));
// "Hi! My name is Leo"
◆ 賦值
var name = "Neil";

var person = {
  name: "Leo",
  sayHi: function() {
    return "Hi! My name is " + this.name;
  }
};

person.sayHi(); // "Hi! My name is Leo"

var foo = person.sayHi;

foo(); // "Hi! My name is Neil"

當(dāng)把 person.sayHi() 賦值給一個(gè)變量,這個(gè)時(shí)候 this 的指向又發(fā)生了變化。因?yàn)?foo 執(zhí)行時(shí)是在全局環(huán)境中,所以 this 指向 window(嚴(yán)格模式下指向 undefined)。

同樣,我們可以通過(guò) call / apply / bind 來(lái)解決,這里就不貼代碼了。

§ 別忘了 new

在 JS 中,我們聲明一個(gè)類(lèi),然后 new 一個(gè)實(shí)例。

function Person(name) {
  this.name = name;
}

var her = Person("Angelia");
console.log(her.name); // TypeError

var me = new Person("Leo");
console.log(me.name); // "Leo"

如果我們直接把調(diào)用這個(gè)函數(shù),this 將指向全局對(duì)象,Person 在這里就是一個(gè)普通函數(shù),沒(méi)有返回值,默認(rèn) undefined,而嘗試訪問(wèn) undefined 的屬性就會(huì)報(bào)錯(cuò)。

如果我們使用 new 操作符,那么 new 其實(shí)會(huì)生成一個(gè)新的對(duì)象,并將 this 指向這個(gè)新的對(duì)象,然后將其返回,所以 me.name 能打印出 “Leo”。

關(guān)于 new 的原理,我會(huì)在后面的文章分享,敬請(qǐng)期待。

§ 小結(jié)

你看,this 是不是千變?nèi)f化。但是我們得以不變應(yīng)萬(wàn)變。

在這么多場(chǎng)景下,this 的指向萬(wàn)變不離其宗:它一定是在執(zhí)行時(shí)決定的,指向調(diào)用函數(shù)的對(duì)象。在閉包、回調(diào)函數(shù)、賦值等場(chǎng)景下我們都可以利用 call / apply / bind 來(lái)改變 this 的指向,以達(dá)到我們的預(yù)期。

接下來(lái),請(qǐng)期待文章《理解 JavaScript call/apply/bind》。

◆ 文章參考

Understand JavaScript"s "this" With Clarity, and Matser It | Richard Bovell

How does the "this" keyword work | Stack Overflow

ECMAScript Language Specification - $11.1.1 The This Keyword

(1, eval)("this") vs eval("this") in JavaScript | Stack Overflow

§ JavaScript 系列文章

理解 JavaScript 閉包

理解 JavaScript 執(zhí)行棧

理解 JavaScript 作用域

理解 JavaScript 數(shù)據(jù)類(lèi)型與變量

Be Good. Sleep Well. And Enjoy.

原文發(fā)布在我的公眾號(hào) camerae,點(diǎn)擊查看。

前端技術(shù) | 個(gè)人成長(zhǎng)

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/100207.html

相關(guān)文章

  • 我對(duì)JavaScriptthis的一些理解

    摘要:匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此它的對(duì)象通常指向。如果對(duì)此有疑惑,可以看知乎上的答案知乎匿名函數(shù)的指向?yàn)槭裁词亲鳛閷?duì)象方法的調(diào)用,指向該對(duì)象當(dāng)函數(shù)作為某個(gè)對(duì)象的方法調(diào)用時(shí),就指這個(gè)函數(shù)所在的對(duì)象。 因?yàn)槿粘9ぷ髦薪?jīng)常使用到this,而且在JavaScript中this的指向問(wèn)題也很容易讓人混淆一部分知識(shí)。 這段時(shí)間翻閱了一些書(shū)籍也查閱了網(wǎng)上一些資料然后結(jié)合自己的經(jīng)驗(yàn),為了能讓自...

    focusj 評(píng)論0 收藏0
  • javascript技術(shù)難點(diǎn)(三)之this、new、apply和call詳解

    摘要:第四點(diǎn)也要著重講下,記住構(gòu)造函數(shù)被操作,要讓正常作用最好不能在構(gòu)造函數(shù)里 4) this、new、call和apply的相關(guān)問(wèn)題 講解this指針的原理是個(gè)很復(fù)雜的問(wèn)題,如果我們從javascript里this的實(shí)現(xiàn)機(jī)制來(lái)說(shuō)明this,很多朋友可能會(huì)越來(lái)越糊涂,因此本篇打算換一個(gè)思路從應(yīng)用的角度來(lái)講解this指針,從這個(gè)角度理解this指針更加有現(xiàn)實(shí)意義。 下面我們看看在ja...

    ghnor 評(píng)論0 收藏0
  • 加深對(duì) JavaScript This理解

    摘要:使用來(lái)調(diào)用函數(shù),會(huì)自動(dòng)執(zhí)行下面操作創(chuàng)建一個(gè)全新的對(duì)象。所以如果是一個(gè)函數(shù)的話,會(huì)是這樣子的創(chuàng)建一個(gè)新對(duì)象連接新對(duì)象與函數(shù)的原型執(zhí)行函數(shù),改變指向新的對(duì)象所以,在使用來(lái)調(diào)用函數(shù)時(shí)候,我們會(huì)構(gòu)造一個(gè)新對(duì)象并把它綁定到函數(shù)調(diào)用中的上。 歡迎來(lái)我的博客閱讀:《加深對(duì) JavaScript This 的理解》 我相信你已經(jīng)看過(guò)很多關(guān)于 JavaScript 的 this 的談?wù)摿?,既然你點(diǎn)進(jìn)來(lái)...

    PiscesYE 評(píng)論0 收藏0
  • 理解 JavaScript call()/apply()/bind()

    摘要:理解文章中已經(jīng)比較全面的分析了在中的指向問(wèn)題,用一句話來(lái)總結(jié)就是的指向一定是在執(zhí)行時(shí)決定的,指向被調(diào)用函數(shù)的對(duì)象。與和直接執(zhí)行原函數(shù)不同的是,返回的是一個(gè)新函數(shù)。這個(gè)新函數(shù)包裹了原函數(shù),并且綁定了的指向?yàn)閭魅氲摹? 理解 JavaScript this 文章中已經(jīng)比較全面的分析了 this 在 JavaScript 中的指向問(wèn)題,用一句話來(lái)總結(jié)就是:this 的指向一定是在執(zhí)行時(shí)決定的,...

    duan199226 評(píng)論0 收藏0
  • javascriptthis理解

    摘要:的關(guān)鍵字總是讓人捉摸不透,關(guān)鍵字代表函數(shù)運(yùn)行時(shí),自動(dòng)生成的一個(gè)內(nèi)部對(duì)象,只能在函數(shù)內(nèi)部使用,因?yàn)楹瘮?shù)的調(diào)用場(chǎng)景不同,的指向也不同。其實(shí)只要理解語(yǔ)言的特性就很好理解。個(gè)人對(duì)中的關(guān)鍵字的理解如上,如有不正,望指正,謝謝。 javascript的this關(guān)鍵字總是讓人捉摸不透,this關(guān)鍵字代表函數(shù)運(yùn)行時(shí),自動(dòng)生成的一個(gè)內(nèi)部對(duì)象,只能在函數(shù)內(nèi)部使用,因?yàn)楹瘮?shù)的調(diào)用場(chǎng)景不同,this的指向也不...

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

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

0條評(píng)論

閱讀需要支付1元查看
<