JavaScript this 從此不再疑惑

_ivenj發表於2019-02-16

1. 問題引入

function A() {}
A.prototype.fna = function() {
    console.log(this);
}

我的問題是 fnathis 是指向哪裡的?

var a = new A();
a.fna();  // A {}

var fnt = a.fna;
fnt();  // window {...}

再看我們經常遇到的情形

function A() {
    this.name = `A`;
}

A.prototype.fna = function() {
    return this.name;
}

function sayName(fn) {
    console.log(fn());
}

var a = new A();
sayName(a.fna); //undefined
sayName(a.fna.bind(a));  //A

這裡就是我們平時在寫程式碼的時候為什麼要呼叫 bind 函式來繫結上下文

function A() {
    this.fna = function() {
        console.log(this);
    }
}

A.prototype.getFna = function() {
    return this.fna;
}

function sayContext(fn) {
    fn();
}

var a = new A();
var fna = a.getFna();
sayContext(fna);  //window

為什麼會有以上這種情況呢,在 javathis 是始終指向呼叫物件的。是的,始終指向呼叫物件,呼叫物件,這個很重要,java 的靜態成員是沒有 this 的概念的。在 javascriptthis 只和函式的執行環境有關。只有三種情況,在瀏覽中 window、呼叫物件、嚴格模式下的undefined,對應我們開發者來說能接觸到的就是以上三者,所以我們可以理解為 函式的執行環境就是以上三者。

2. 確定 this 指向

我們如何確定 this 的指向呢,有很多文章介紹 this 確定指向,方式也有很多種,而我是根據函式的呼叫形勢去判斷的,有以下兩個判斷標準。

1 如果函式的最終呼叫形式是 fn(); 那麼在非嚴格模式下 this 指向 window 物件,在嚴格模式下指向 undefined
2 如果是通過物件呼叫 o.fn(); 這種形式 this 指向物件 o

是的就這兩個標準,就這麼簡單。

3. 通過 callapplybind 深入理解 this

函式呼叫原型

fn.call(thisArg, arg1, arg2, ...)
fn.apply(thisArg, [argsArray])
fn.bind(thisArg[, arg1[, arg2[, ...]]])

上面這三個函式都是用來改變函式的 this 指向的
1 call 第一個引數是 fnthis 的期望指向,值可以是 物件 或者 undefined,後面的引數是要傳遞 給 fn 的引數列表

2 apply 第一個引數是 fnthis 的期望指向,值可以是 物件 或者 undefined,後面的值是 fn 的 引數,是一個陣列

callapply 功能相同,唯一不同的是選擇將引數以 引數列表 傳入或者以 陣列 傳入,都可以,可以互換
使用。呼叫者兩個函式會立即執行 fn,這裡是立即執行

3 bind 第一個引數是 fnthis 的期望指向,值可以是 物件 或者 undefined,後面的引數是要傳遞 給 fn 的引數列表

呼叫 bind 函式會返回一個函式,這個函式是 fn 的包裝,和 fn 的唯一區別是繫結了 this,即 this指向明確。所以 bindcallapply,的區別是 bind 返回一個 this 明確的新函式,callapply立即執行了 fn

到這裡我想 javascriptthis 已經說的很清楚了。

相關文章