狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

無人不識又無人不迷糊的this

freeflydom
2024年3月28日 17:20 本文熱度 596

本文分享自華為云社區《3月閱讀周·你不知道的JavaScript | 無人不識又無人不迷糊的this》,作者: 葉一一。

關于this

this關鍵字是JavaScript中最復雜的機制之一。它是一個很特別的關鍵字,被自動定義在所有函數的作用域中。

為什么要用this

隨著開發者的使用模式越來越復雜,顯式傳遞上下文對象會讓代碼變得越來越混亂,使用this則不會這樣。

比如下面的例子:

function identify() {

  return this.name.toUpperCase();

}


function speak() {

  var greeting = "Hello, I'm " + identify.call(this);

  console.log(greeting);

}


var me = {

  name: 'Kyle',

};


var you = {

  name: 'Reader',

};


console.log(identify.call(me)); 

console.log(identify.call(you)); 


speak.call(me); 

speak.call(you);

打印一下結果:

 

上面的代碼可以在不同的上下文對象(me和you)中重復使用函數identify()和speak(),不用針對每個對象編寫不同版本的函數。如果不使用this,那就需要給identify()和speak()顯式傳入一個上下文對象。

誤解

有兩種常見的對于this的解釋,但是它們都是錯誤的。

1、指向自身

人們很容易把this理解成指向函數自身。

那么為什么需要從函數內部引用函數自身呢?常見的原因是遞歸(從函數內部調用這個函數)或者可以寫一個在第一次被調用后自己解除綁定的事件處理器。

看下面這段代碼,思考foo會被調用了多少次?

function foo(num) {

  console.log('foo: ' + num);


  // 記錄foo被調用的次數

  this.count++;

}


foo.count = 0;


var i;

for (i = 0; i < 10; i++) {

  if (i > 5) {

    foo(i);

  }

}


// foo被調用了多少次?

console.log(foo.count);

打印結果:

 

console.log語句產生了4條輸出,證明foo(..)確實被調用了4次,但是foo.count仍然是0。顯然從字面意思來理解this是錯誤的。

執行foo.count = 0時,的確向函數對象foo添加了一個屬性count。但是函數內部代碼this.count中的this并不是指向那個函數對象。

2、它的作用域

第二種常見的誤解是,this指向函數的作用域。這個問題有點復雜,因為在某種情況下它是正確的,但是在其他情況下它卻是錯誤的。

this在任何情況下都不指向函數的詞法作用域。

function foo() {

  var a = 2;

  this.bar();

}


function bar() {

  console.log(this.a);

}


foo();

直接打印上面的代碼會得到一個報錯:

 

這段代碼試圖通過this.bar()來引用bar()函數。這是不可能實現的,使用this不可能在詞法作用域中查到什么。

每當開發者想要把this和詞法作用域的查找混合使用時,一定要提醒自己,這是無法實現的。

this到底是什么

this的綁定和函數聲明的位置沒有任何關系,只取決于函數的調用方式。

當一個函數被調用時,會創建一個活動記錄(有時候也稱為執行上下文)。這個記錄會包含函數在哪里被調用(調用棧)、函數的調用方式、傳入的參數等信息。this就是這個記錄的一個屬性,會在函數執行的過程中用到。

this全面解析

調用位置

在理解this的綁定過程之前,首先要理解調用位置:調用位置就是函數在代碼中被調用的位置(而不是聲明的位置)。

尋找調用位置就是尋找“函數被調用的位置”。最重要的是要分析調用棧(就是為了到達當前執行位置所調用的所有函數)。調用位置就在當前正在執行的函數的前一個調用中。

通過下面的代碼來看什么是調用棧和調用位置:

function baz() {

  // 當前調用棧是:baz

  // 因此,當前調用位置是全局作用域


  console.log('baz');

  bar(); // <-- bar的調用位置

}


function bar() {

  // 當前調用棧是baz -> bar

  // 因此,當前調用位置在baz中


  console.log('bar');

  foo(); // <-- foo的調用位置

}


function foo() {

  // 當前調用棧是baz -> bar -> foo

  // 因此,當前調用位置在bar中


  console.log('foo');

}


baz(); // <-- baz的調用位置

打印的結果如下:

 

綁定規則

來看看在函數的執行過程中調用位置如何決定this的綁定對象。

首先必須找到調用位置,然后判斷需要應用下面四條規則中的哪一條。

充分理解四條規則之后,再理解多條規則都可用時它們的優先級如何排列。

1、默認綁定

首先要介紹的是最常用的函數調用類型:獨立函數調用。可以把這條規則看作是無法應用其他規則時的默認規則。

var a = 2;


function foo() {

  console.log(this.a);

}


foo(); // 2

打印結果是2。也就是當調用foo()時,this.a被解析成了全局變量a。函數調用時應用了this的默認綁定,因此this指向全局對象。

2、隱式綁定

另一條需要考慮的規則是調用位置是否有上下文對象,或者說是否被某個對象擁有或者包含,不過這種說法可能會造成一些誤導。

思考下面的代碼:

function foo() {

  console.log(this.a);

}


var obj = {

  a: 2,

  foo: foo,

};


obj.foo(); // 2

當foo()被調用時,它的前面確實加上了對obj的引用。當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象。因為調用foo()時this被綁定到obj,因此this.a和obj.a是一樣的。

3、顯式綁定

JavaScript提供的絕大多數函數以及你自己創建的所有函數都可以使用call(..)和apply(..)方法。

它們的第一個參數是一個對象,是給this準備的,接著在調用函數時將其綁定到this。

因為可以直接指定this的綁定對象,因此我們稱之為顯式綁定。

思考下面的代碼:

function foo() {

  console.log(this.a);

}


var obj = {

  a: 2,

};


foo.call(obj); // 2

通過foo.call(..),我們可以在調用foo時強制把它的this綁定到obj上。

4、new綁定

在傳統的面向類的語言中,“構造函數”是類中的一些特殊方法,使用new初始化類時會調用類中的構造函數。通常的形式是這樣的:

something = new MyClass(..);

在JavaScript中,構造函數只是一些使用new操作符時被調用的函數。它們并不會屬于某個類,也不會實例化一個類。實際上,它們甚至都不能說是一種特殊的函數類型,它們只是被new操作符調用的普通函數而已。

優先級

1、四條規則的優先級

new綁定 > 顯式綁定 > 隱式綁定 > 默認綁定。

2、判斷this

可以根據優先級來判斷函數在某個調用位置應用的是哪條規則。可以按照下面的順序來進行判斷:

(1)函數是否在new中調用(new綁定)?如果是的話this綁定的是新創建的對象。

var bar = new foo();

(2)函數是否通過call、apply(顯式綁定)或者硬綁定調用?如果是的話,this綁定的是指定的對象。

var bar = foo.call(obj2);

(3)函數是否在某個上下文對象中調用(隱式綁定)?如果是的話,this綁定的是那個上下文對象。

var bar = obj1.foo();

(4)如果都不是的話,使用默認綁定。如果在嚴格模式下,就綁定到undefined,否則綁定到全局對象。

var bar = foo();

綁定例外

在某些場景下this的綁定行為會出乎意料,你認為應當應用其他綁定規則時,實際上應用的可能是默認綁定規則。

被忽略的this

如果你把null或者undefined作為this的綁定對象傳入call、apply或者bind,這些值在調用時會被忽略,實際應用的是默認綁定規則:

function foo() {

  console.log(this.a);

}


var a = 2;


foo.call(null); // 2

那么什么情況下會傳入null呢?

一種非常常見的做法是使用apply(..)來“展開”一個數組,并當作參數傳入一個函數。類似地,bind(..)可以對參數進行柯里化(預先設置一些參數),這種方法有時非常有用。

間接引用

另一個需要注意的是,你有可能(有意或者無意地)創建一個函數的“間接引用”,在這種情況下,調用這個函數會應用默認綁定規則。

間接引用最容易在賦值時發生:

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

賦值表達式p.foo = o.foo的返回值是目標函數的引用,因此調用位置是foo()而不是p.foo()或者o.foo()。根據我們之前說過的,這里會應用默認綁定。

軟綁定

如果可以給默認綁定指定一個全局對象和undefined以外的值,那就可以實現和硬綁定相同的效果,同時保留隱式綁定或者顯式綁定修改this的能力。

function foo() {

  console.log('name: ' + this.name);

}


var obj = { name: 'obj' },

  obj2 = { name: 'obj2' },

  obj3 = { name: 'obj3' };


var fooOBJ = foo.softBind(obj);


fooOBJ(); // name: obj


obj2.foo = foo.softBind(obj);

obj2.foo(); // name: obj2 <---- 看!! !


fooOBJ.call(obj3); // name: obj3 <---- 看!


setTimeout(obj2.foo, 10);

// name: obj   <---- 應用了軟綁定

可以看到,軟綁定版本的foo()可以手動將this綁定到obj2或者obj3上,但如果應用默認綁定,則會將this綁定到obj。

this詞法

ES6中介紹了一種無法使用這些規則的特殊函數類型:箭頭函數。箭頭函數并不是使用function關鍵字定義的,而是使用被稱為“胖箭頭”的操作符=>定義的。箭頭函數不使用this的四種標準規則,而是根據外層(函數或者全局)作用域來決定this。

箭頭函數的詞法作用域:

function foo() {

  // 返回一個箭頭函數

  return a => {

    //this繼承自foo()

    console.log(this.a);

  };

}


var obj1 = {

  a: 2,

};


var obj2 = {

  a: 3,

};


var bar = foo.call(obj1);

bar.call(obj2); // 2, 不是3!

foo()內部創建的箭頭函數會捕獲調用時foo()的this。由于foo()的this綁定到obj1,bar(引用箭頭函數)的this也會綁定到obj1,箭頭函數的綁定無法被修改。

總結

我們來總結一下本篇的主要內容:

  • this實際上是在函數被調用時發生的綁定,它指向什么完全取決于函數在哪里被調用。

  • 如果要判斷一個運行中函數的this綁定,就需要找到這個函數的直接調用位置。找到之后就可以順序應用下面這四條規則來判斷this的綁定對象。

  • ES6中的箭頭函數并不會使用四條標準的綁定規則,而是根據當前的詞法作用域來決定this,具體來說,箭頭函數會繼承外層函數調用的this綁定(無論this綁定到什么)。這其實和ES6之前代碼中的self = this機制一樣。

 

轉自博客園,作者華為云開發聯盟https://www.cnblogs.com/huaweiyun/p/18096240


該文章在 2024/3/28 17:20:08 編輯過
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved