真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

深入淺出 JavaScript 中的 this

筆者最近在看 你不知道的JavaScript上卷,里面關(guān)于 this 的講解個(gè)人覺得非常精彩。JavaScript 中的 this 算是一個(gè)核心的概念,有一些同學(xué)會(huì)對(duì)其有點(diǎn)模糊和小恐懼,究其原因,現(xiàn)在對(duì) this 討論的文章很多,讓我們覺得 this 無規(guī)律可尋,就像一個(gè)幽靈一樣

創(chuàng)新互聯(lián)公司主營(yíng)昆明網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,成都App制作,昆明h5微信平臺(tái)小程序開發(fā)搭建,昆明網(wǎng)站營(yíng)銷推廣歡迎昆明等地區(qū)企業(yè)咨詢

如果你還沒弄懂 this,或者對(duì)它比較模糊,這篇文章就是專門為你準(zhǔn)備的,如果你相對(duì)比較熟悉了,那你也可以當(dāng)做復(fù)習(xí)鞏固你的知識(shí)點(diǎn)

本篇文章,算是一篇讀書筆記,當(dāng)然也加上了很多我的個(gè)人理解,我覺得肯定對(duì)大家有所幫助

執(zhí)行上下文

在理解 this 之前,我們先來看下什么是執(zhí)行上下文

簡(jiǎn)而言之,執(zhí)行上下文是評(píng)估和執(zhí)行 JavaScript 代碼的環(huán)境的抽象概念。每當(dāng) Javascript 代碼在運(yùn)行的時(shí)候,它都是在執(zhí)行上下文中運(yùn)行

JavaScript 中有三種執(zhí)行上下文類型

  • 全局執(zhí)行上下文 — 這是默認(rèn)或者說基礎(chǔ)的上下文,任何不在函數(shù)內(nèi)部的代碼都在全局上下文中。它會(huì)執(zhí)行兩件事:創(chuàng)建一個(gè)全局的 window 對(duì)象(瀏覽器的情況下),并且設(shè)置 this 的值等于這個(gè)全局對(duì)象。一個(gè)程序中只會(huì)有一個(gè)全局執(zhí)行上下文
  • 函數(shù)執(zhí)行上下文 — 每當(dāng)一個(gè)函數(shù)被調(diào)用時(shí), 都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的上下文。每個(gè)函數(shù)都有它自己的執(zhí)行上下文,不過是在函數(shù)被調(diào)用時(shí)創(chuàng)建的。函數(shù)上下文可以有任意多個(gè)
  • eval 函數(shù)執(zhí)行上下文 — 執(zhí)行在 eval 函數(shù)內(nèi)部的代碼也會(huì)有它屬于自己的執(zhí)行上下文,但由于 JavaScript 開發(fā)者并不經(jīng)常使用 eval,所以在這里我不會(huì)討論它

這里我們先得出一個(gè)結(jié)論,非嚴(yán)格模式和嚴(yán)格模式中 this 都是指向頂層對(duì)象(瀏覽器中是window)

console.log(this === window); // true
'use strict'
console.log(this === window); // true
this.name = 'vnues';
console.log(this.name); // vnues

后面我們的討論更多的是針對(duì)函數(shù)執(zhí)行上下文

this 到底是什么?為什么要用 this

this 是在運(yùn)行時(shí)進(jìn)行綁定的,并不是在編寫時(shí)綁定,它的上下文取決于函數(shù)調(diào) 用時(shí)的各種條件

牢記:this 的綁定和函數(shù)聲明的位置沒有任何關(guān)系,只取決于函數(shù)的調(diào)用方式

當(dāng)一個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)活動(dòng)記錄(有時(shí)候也稱為執(zhí)行上下文)。這個(gè)記錄會(huì)包 含函數(shù)在哪里被調(diào)用(調(diào)用棧)、函數(shù)的調(diào)用方法、傳入的參數(shù)等信息。this 就是記錄的 其中一個(gè)屬性,會(huì)在函數(shù)執(zhí)行的過程中用到

看個(gè)實(shí)例,理解為什么要用 this,有時(shí)候,我們需要實(shí)現(xiàn)類似如下的代碼:

function identify(context) {
  return context.name.toUpperCase();
}
function speak(context) {
  var greeting = "Hello, I'm " + identify(context);
  console.log(greeting);
}
var me = {
  name: "Kyle"
};
speak(me); //hello, 我是 KYLE

這段代碼的問題,在于需要顯示傳遞上下文對(duì)象,如果代碼越來越復(fù)雜,這種方式會(huì)讓你的代碼看起來很混亂,用 this 則更加的優(yōu)雅

var me = {
  name: "Kyle"
};

function identify() {
  return this.name.toUpperCase();
}
function speak() {
  var greeting = "Hello, I'm " + identify.call(this);
  console.log(greeting);
}
speak.call(me); // Hello, 我是 KYLE

this 的四種綁定規(guī)則

下面我們來看在函數(shù)上下文中的綁定規(guī)則,有以下四種

  • 默認(rèn)綁定
  • 隱式綁定
  • 顯式綁定
  • new 綁定

默認(rèn)綁定

最常用的函數(shù)調(diào)用類型:獨(dú)立函數(shù)調(diào)用,這個(gè)也是優(yōu)先級(jí)最低的一個(gè),此事 this 指向全局對(duì)象。注意:如果使用嚴(yán)格模式(strict mode),那么全局對(duì)象將無法使用默認(rèn)綁定,因此 this 會(huì)綁定 到 undefined,如下所示

var a = 2;  //  變量聲明到全局對(duì)象中
function foo() {
  console.log(this.a);   // 輸出 a
}

function bar() {
  'use strict';
  console.log(this); // undefined
}
foo();
bar();

隱式綁定

還可以我們開頭說的:this 的綁定和函數(shù)聲明的位置沒有任何關(guān)系,只取決于函數(shù)的調(diào)用方式

先來看一個(gè)例子:

  function foo() {
    console.log(this.a);
  }
  var obj = {
    a: 2,
    foo: foo
  };
  obj.foo(); // 2

當(dāng)調(diào)用 obj.foo() 的時(shí)候,this 指向 obj 對(duì)象。當(dāng)函數(shù)引用有上下文對(duì)象時(shí),隱式綁定規(guī)則會(huì)把函數(shù)調(diào)用中的 this 綁定到這個(gè)上下文對(duì)象。因?yàn)檎{(diào) 用 foo()時(shí) this 被綁定到 obj,因此 this.a 和 obj.a 是一樣的

記?。?strong>對(duì)象屬性引用鏈中只有最頂層或者說最后一層會(huì)影響調(diào)用位置

    function foo() {
      console.log(this.a);
    }
    var obj2 = {
      a: 42,
      foo: foo
    };
    var obj1 = {
      a: 2,
      obj2: obj2
    };
    obj1.obj2.foo(); // 42

間接引用

另一個(gè)需要注意的是,你有可能(有意或者無意地)創(chuàng)建一個(gè)函數(shù)的“間接引用”,在這 種情況下,調(diào)用這個(gè)函數(shù)會(huì)應(yīng)用默認(rèn)綁定規(guī)則

function foo() {
  console.log(this.a);
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2

另一個(gè)需要注意的是,你有可能(有意或者無意地)創(chuàng)建一個(gè)函數(shù)的“間接引用”,在這 種情況下,調(diào)用這個(gè)函數(shù)會(huì)應(yīng)用默認(rèn)綁定規(guī)則

賦值表達(dá)式 p.foo = o.foo 的返回值是目標(biāo)函數(shù)的引用,因此調(diào)用位置是 foo()而不是 p.foo() 或者 o.foo()。根據(jù)我們之前說過的,這里會(huì)應(yīng)用默認(rèn)綁定

顯示綁定

在分析隱式綁定時(shí),我們必須在一個(gè)對(duì)象內(nèi)部包含一個(gè)指向函數(shù)的屬性,并通過這個(gè)屬性間接引用函數(shù),從而把 this 間接(隱式)綁定到這個(gè)對(duì)象上。 那么如果我們不想在對(duì)象內(nèi)部包含函數(shù)引用,而想在某個(gè)對(duì)象上強(qiáng)制調(diào)用函數(shù),該怎么
做呢?

Javascript 中提供了 apply 、callbind 方法可以讓我們實(shí)現(xiàn)

不同之處在于,call()apply() 是立即執(zhí)行函數(shù),并且接受的參數(shù)的形式不同:

  • call(this, arg1, arg2, ...)
  • apply(this, [arg1, arg2, ...])

bind() 則是創(chuàng)建一個(gè)新的包裝函數(shù),并且返回,而不是立刻執(zhí)行

  • bind(this, arg1, arg2, ...)

看如下的例子:

  function foo(b) {
    console.log(this.a + '' + b);
  }
  var obj = {
    a: 2,
    foo: foo
  };
  var a = 1;
  foo('Gopal'); // 1Gopal
  obj.foo('Gopal'); // 2Gopal
  foo.call(obj, 'Gopal'); // 2Gopal
  foo.apply(obj, ['Gopal']); // 2Gopal
  let bar = foo.bind(obj, 'Gopal');
  bar(); // 2Gopal

被忽略的 this

如果你把 null 或者 undefined 作為 this 的綁定對(duì)象傳入 call、apply 或者 bind,這些值在調(diào)用時(shí)會(huì)被忽略,實(shí)際應(yīng)用的是默認(rèn)綁定規(guī)則

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

利用這個(gè)用法使用 apply(..) 來“展開”一個(gè)數(shù)組,并當(dāng)作參數(shù)傳入一個(gè)函數(shù)。
類似地,bind(..) 可以對(duì)參數(shù)進(jìn)行柯里化(預(yù)先設(shè)置一些參數(shù))

  function foo(a, b) {
    console.log("a:" + a + ", b:" + b);
  }
  // 把數(shù)組“展開”成參數(shù)
  foo.apply(null, [2, 3]); // a:2, b:3
  // 使用 bind(..) 進(jìn)行柯里化
  var bar = foo.bind(null, 2);
  bar(3); // a:2, b:3

new綁定

當(dāng)我們使用構(gòu)造函數(shù) new 一個(gè)實(shí)例的時(shí)候,這個(gè)實(shí)例的 this 指向是什么呢?

我們先來看下使用 new 來調(diào)用函數(shù),或者說發(fā)生構(gòu)造函數(shù)調(diào)用時(shí),會(huì)執(zhí)行什么操作,如下:

  • 創(chuàng)建(或者說構(gòu)造)一個(gè)全新的對(duì)象
  • 這個(gè)新對(duì)象會(huì)被執(zhí)行[[原型]]連接,將對(duì)象(實(shí)例)的 __proto__ 和構(gòu)造函數(shù)的 prototype 綁定
  • 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的 this
  • 如果函數(shù)沒有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象

原理實(shí)現(xiàn)類似如下:

function create (ctr) {
    // 創(chuàng)建一個(gè)空對(duì)象
    let obj = new Object()
    // 鏈接到構(gòu)造函數(shù)的原型對(duì)象中
    let Con = [].shift.call(arguments)
    obj.__proto__ = Con.prototype
    // 綁定this
    let result = Con.apply(obj, arguments);
    // 如果返回是一個(gè)對(duì)象,則直接返回這個(gè)對(duì)象,否則返回實(shí)例
    return typeof result === 'object'? result : obj;
}

注意:let result = Con.apply(obj, arguments); 實(shí)際上就是指的是新對(duì)象會(huì)綁定到函數(shù)調(diào)用的 this

  function Foo(a) {
    this.a = a;
  }
  var bar = new Foo(2);
  console.log(bar.a); // 2

特殊情況——箭頭函數(shù)

我們之前介紹的四條規(guī)則已經(jīng)可以包含所有正常的函數(shù)。但是 ES6 中介紹了一種無法使用 這些規(guī)則的特殊函數(shù)類型:箭頭函數(shù)

箭頭函數(shù)不使用 this 的四種標(biāo)準(zhǔn)規(guī)則,而是根據(jù)定義時(shí)候的外層(函數(shù)或者全局)作用域來決 定 this。也就是說箭頭函數(shù)不會(huì)創(chuàng)建自己的 this,它只會(huì)從自己的作用域鏈的上一層繼承 this

function foo() {
  // 返回一個(gè)箭頭函數(shù)
  // this 繼承自 foo()
  return (a) => {
    console.log(this.a);
  }
};

var obj1 = {
  a: 2
};
var obj2 = {
  a: 3
};
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是 3 !

foo() 內(nèi)部創(chuàng)建的箭頭函數(shù)會(huì)捕獲調(diào)用時(shí) foo()this。由于 foo()this 綁定到 obj1, bar(引用箭頭函數(shù))的 this 也會(huì)綁定到 obj1,箭頭函數(shù)的綁定無法被修改。(new 也不 行!)

總結(jié)——this 優(yōu)先級(jí)

判斷是否為箭頭函數(shù),是則按照箭頭函數(shù)的規(guī)則

否則如果要判斷一個(gè)運(yùn)行中函數(shù)的 this 綁定,就需要找到這個(gè)函數(shù)的直接調(diào)用位置。找到之后就可以順序應(yīng)用下面這四條規(guī)則來判斷 this 的綁定對(duì)象

  1. new 調(diào)用?綁定到新創(chuàng)建的對(duì)象
  2. call 或者 apply(或者 bind)調(diào)用?綁定到指定的對(duì)象
  3. 由上下文對(duì)象調(diào)用?綁定到那個(gè)上下文對(duì)象
  4. 默認(rèn):在嚴(yán)格模式下綁定到 undefined,否則綁定到全局對(duì)象

如下圖所示:

參考

  • [譯] 理解 JavaScript 中的執(zhí)行上下文和執(zhí)行棧

  • 你不知道的JavaScript上卷


文章名稱:深入淺出 JavaScript 中的 this
當(dāng)前路徑:http://weahome.cn/article/dsojgso.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部