JavaScript 中 this 的指向問題

首發我的博客瀏覽器

JavaScript 中的 this ,你們都用過。可是它到底指向哪裏呢?今天在閱讀 《你不知道的JavaScript (上卷)》再結合本身平時看的博客,對它又有了新的認識,在此來作個小結,再碰到 this ,就不再用擔憂不知道它指向哪裏了。

與調用位置有關,而與定義位置無關

如下示例(在瀏覽器端運行)app

function sayHi(){
    var hi = 1;
    console.log(this.hi);
}

var hi = 2;

var obj = {
    hi : 3,
    sayHi : sayHi
}

// output
sayHi(); // 2
obj.sayHi(); // 3

從上述代碼的執行結果咱們能夠看出,直接調用 sayHi() 函數,它輸出的 this.hi 不指向函數體內本身定義的 hi 變量,也就是說,this 的指向與詞法做用域無關,這也是剛接觸 JavaScript的同窗常犯的一個錯誤(包括我本身),認爲 this.xx 就指向函數體裏面定義的變量 xxsayHi函數的只定義了一次,但obj.sayHi()sayHi() 的輸出結果不同,也偏偏證實了 函數體內this的指向與函數定義的位置無關,而與函數被調用的位置有關,至於爲何輸出結果不同,在下文會講到。函數

this 的綁定規則

默認綁定

默認綁定比較常見,表現形式就是在全局做用域中獨立調用,如上文中的 sayHi(),這種直接調用方式函數體內的 this 就應用了默認綁定規則,默認綁定有如下兩種狀況。oop

  1. 在嚴格模式下(指在函數聲明的過程當中,處在嚴格模式,而不是函數調用處於嚴格模式),函數體內 this 綁定到 undefined
  2. 在非嚴格模式下,函數體內 this 指向全局對象 window
/***** 如下例子爲處在嚴格模式下 *****/
function fn1(){
    'use strict'
    console.log(this);
}

fn1(); // undefined

/***** 如下例子處於非嚴格模式下被調用 *****/
function fn2(){
    console.log(this);
}

fn2(); // window

function fn3(){
    console.log(this);
}

/*
 * 這也是在非嚴格模式下被調用
 * 由於在函數定義時沒有用嚴格模式
 */
'use strict'
fn3(); // window

隱式綁定

隱式綁定的常見形式爲 obj.fn(),若obj對象中有 fn 這個方法(fn能夠在別處定義,但必須被添加到obj中做爲obj的一個屬性方法),那麼 fn 中的 this 就指向 obj 對象,如最開始代碼中 obj.sayHi() 輸出3,就是由於sayHi中的this隱式綁定到obj對象。(我的以爲默認綁定中綁定到window對象時也能夠歸類爲隱式綁定,由於在全局對象中,非嚴格模式且不考慮ES6的話,全部的全局變量都自動成爲window的屬性)。來看個具備迷惑性的例子(出自於原書)post

function foo() { 
 console.log( this.a );
}
var obj = { 
 a: 2,
 foo: foo 
};
var bar = obj.foo; // 函數別名!
var a = "oops, global"; // a 是全局對象的屬性
bar(); // "oops, global"

這是個具備迷惑性的例子,obj 對象有函數 foo 這個方法,後面又在全局做用域中,bar 引用了這個方法,最後再調用 bar。咱們只須要關注函數最後被調用的位置,它是在全局做用域中被單獨調用的,因此仍是爲默認綁定,指向 windowthis

顯式綁定

顯示綁定就比較簡單了,用 call,apply,bind方法,都會綁定函數中的this到傳入的參數對象中code

new 綁定

所謂new綁定就是在用構造函數new一個對象的時候,其中的this指向生成的對象。這就完了嘛?尚未哦。對象

箭頭函數,詞法做用域中的this

簡單的說,箭頭函數中的this,會綁定到函數外(也就是上一層做用域中的this),函數外的this指向哪,箭頭函數中的this就指向哪。(代碼出自於原書)blog

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

var obj1 = { 
    a:2
};

var obj2 = { 
    a:3
};

var bar = foo.call( obj1 );
/*
 * foo先綁定this到obj1對象上,因此foo內的this指向obj1,
 * 返回的箭頭函數根據詞法做用域規則,繼承了外部foo的this,
 * 因此箭頭函數中的this指向obj1
 */


bar.call( obj2 ); // 2, 不是 3 !
/*
 * 箭頭函數中this一但綁定,不可更改
 * 此時仍是指向obj1
 * 因此輸出的是 obj1.a => 2
 */

綁定的優先級

new > 顯示 > 隱式 > 默認繼承

上述知識來自《你不知道的JavaScript(上卷)》

相關文章
相關標籤/搜索