"This" is For JavaScript

"This" is For JavaScript

What is this?

先提早說明一個觀點,實際上,this既不指向函數自身,也不止像函數的詞法做用域 ,此外還有一個重點:this跟函數在哪裏定義無關,決定this的是函數在哪裏調用,它是一種在函數調用時發生的綁定。javascript

this的綁定規則

this的四種綁定規則分別爲:默認綁定、隱式綁定、顯式綁定、new綁定。java

他們中的優先級爲 new綁定 > 顯式綁定 > 陰式綁定 > 默認綁定app

默認綁定

即當沒有任何其餘綁定規則時默認會生效的規則。最常使用的是當函數被單獨定義以及調用時,它默認綁定全局變量函數

function fun() {
    console.log( this.x );
}
var x = '我是一個全局變量';
fun(); //‘我是一個全局變量’ this此時指向window
複製代碼

但咱們須要注意,當咱們在嚴格模式下時,全局對象是沒法用默認綁定的,會拋出錯誤post

function fun() {
 "use strict";
    console.log( this.x );
}
var x = '我是嚴格模式下的全局變量';

fun(); //Uncaught TypeError:Cannot read property 'x' of underfined
複製代碼

隱式綁定

若是函數的調用是在某個對象上觸發,那麼這個調用位置存在一個上下文對象來綁定this。ui

function fun() {
    console.log( this.a );
}
var a = '我是一個全局變量';

var obj = {
    a: '我是obj對象的a屬性',
    fun: fun
};

obj.fun(); //'我是obj對象的a屬性'
複製代碼

這裏obj對象內的fun函數被當作引用屬性,當調用它時實際上的流程爲:this

經過obj對象會去到fun 屬性 --> 根據引用關係找到 fun 函數 --> 調用fun函數spa

因此在此時調用fun函數是,this 被隱式綁定到了obj上下文上,此時的 this.a 也就被解析爲了 obj.a。code

多層調用

有時咱們調用一個函數時會使用多層調用的狀況,此時咱們經過分析一個實際的例子來演示對象

function fun() {
    console.log( this.a );
}

var a = '我是全局變量!';
var obj1 = {
    a: '我是obj1的屬性a',
    fun: fun
};
var obj2 = {
    a: '我是obj2的屬性a',
    obj1:obj1
}

obj2.obj1.fun(); // '我是obj1的屬性a'
複製代碼

此時這個函數的調用過程爲:

訪問obj2.obj1 --> 經過引用獲取 obj1對象 --> 訪問obj1.fun --> 引用獲取fun函數 --> 調用fun函數

此時隱式綁定實際上只是獲取最後一層調用的上下文對象,把this綁定上去。

隱式丟失

函數別名

咱們可能會在平常開發中有這樣的場景

function fun() {
    console.log( this.a );
}

var a = '我是全局變量';

var obj = {
    a: '我是obj對象的a屬性',
    fun: fun
};
//此時再賦予這個函數一個別名
var bar = obj.fun;
bar(); //'我是全局變量'
複製代碼

結果並非'我是obj對象的a屬性',這是由於obj.fun 本質上是引用屬性,因此下面兩行本質上是沒有區別的

var bar = obj.fun;

var bar = fun

因此調用時本質上是 bar 找到了 fun函數自己,進行調用時是在全局環境下調用的,因此不會輸出obj內定義的屬性,因此此時就是默認綁定

回調函數

當咱們使用回調函數時也會存在隱式綁定丟失的狀況

function fun() {
    console.log( this.a );
}

var a = '我是全局變量';

var obj = {
    a: '我是obj的屬性a',
    fun: fun
};

setTimeout( obj.fun, 1000); //我是全局變量
複製代碼

發證這個結果的緣由也是一樣的道理,傳入 setTimeout的obj.fun 是引用屬性, 因此調用順序爲:

setTimeout --> 獲取obj.fun屬性 --> 經過引用屬性的到fun函數 --> 調用執行fun函數。

因此在調用執行的時候並無在obj的上下文中,因此仍爲默認綁定

顯式綁定

相較於隱式綁定而言,咱們有時須要手動來爲函數調用指明函數執行的上下文,此時據須要使用顯式綁定

顯式綁定主要是使用三個方法 call,apply以及bind方法來實現,這部份內容我在另外一篇文章中有詳細解析

Call,Apply與Bind的兄弟密談

new 綁定

當咱們使用new 來修飾並調用函數時,他會先查看這個函數是否有其餘的返回對象,若是沒有就自動返回本身做爲一個新對象。

function fun(a) {
    this.a = a;
    console.log( this.a );
}
var a = '我是全局變量';

var fun1 = new fun('我是fun1的參數');
var fun2 = new fun('我是fun2的參數');

console.log(fun1.a,fun2.a); // '我是fun1的參數','我是fun2的參數'
複製代碼

由於當咱們使用new時他會生成一個全新的對象來綁定this。

箭頭函數

箭頭函數做爲ES6提出的新概念,它是一種對以前this複雜操做的一種極好的解決方案,他的this綁定取決於外層(函數或者全局)做用域。

注意,是取決於外層做用域,也就是咱們對箭頭函數使用顯示綁定來強制修改上下文綁定也是無效的。

參考

《你不知道的JavaScript(上卷)》——Kyle Simpson 著

《ES6標準入門(第三版)》 —— 阮一峯 著

相關文章
相關標籤/搜索