this, call, apply 和 bind

葉葉Yeah~發表於2019-05-14

初衷:

這篇關於this的文章,僅僅是自己認為比較重要且易混淆的知識點。

內容

1.含義:

JavaScript中,this物件是指當前函式中正在執行的上下文環境,因此this並非是定義時(箭頭函式除外)。

2.this指向:

永遠指向最後呼叫它的物件。 簡單記憶:A.B()這裡的this就指.左邊的A.

3.this物件分類:

  • this物件分類:
    • 全域性物件
    • 當前物件
    • 任意物件

判斷處於哪種this物件,這個取決於函式的呼叫方式。而JavaScript中的函式呼叫方式又分為四種:

  • JavaScript中的函式呼叫方式:
    • 函式呼叫
    • 作為物件的方法呼叫
    • 建構函式呼叫
    • call 、apply 、bind

4. 具體內容:

4.1 函式呼叫
var a = 11;
function foo() {
    var a = 22;
    console.log(this.a);//11
}
foo();
console.log(this.a);//11
複製程式碼

函式呼叫:在獨立呼叫中,this在非嚴格模式下指向全域性物件window.在嚴格模式下thisundefined。在這裡,用var宣告一個變數和給this以及window新增屬性是等價的。

var myname = "princess";
console.log(this.myname); //princess
console.log(window.myname); //princess
複製程式碼

接著可能會有下面的疑惑:

var foo = 123;//foo 是變數
window.bar = 345;//bar 是屬性
delete foo;
delete bar;
console.log(this.foo,this.bar);//123,undefined  為什麼不一樣呢?
複製程式碼

delelte刪除不了變數、刪除不了原型鏈中的變數、可以刪掉物件屬性!

還得注意以下一點:

(…)();這樣形式(IIFE(立即呼叫的函式表示式))封裝的函式會立即被呼叫,也就是說等同於被獨立呼叫,因此它內部的this全域性變數,所以輸出的是全域性變數。

4.2 作為物件的方法呼叫
var b = 33;
var o = {
    b: 44,
    f: function() {
        return this.b;
    }
};
console.log(o.f());//44
複製程式碼

作為物件的方法呼叫this指向該物件(這裡即物件o)。

但是當物件的方法被賦予給一個變數時,其相當於函式觸發,此時的this為 window (非嚴格模式下)或者 undefined(嚴格模式下)。

var b = 33;
var o = {
    b: 44,
    f: function() {
        return this.b;
    }
};
console.log(o.f());//44
var c = o.f;    // this == window || undefined
c();//33
複製程式碼
4.3 建構函式呼叫
  • new 方法建立物件的時候,會經過如下過程:
    • 建立物件,將this值賦予新的物件;
    • 呼叫建構函式,為this新增屬性和方法;
    • 返回this給當前的物件.

注意關鍵字 new

function Person(name, age) {
    this.name = name;
    this.age = age;
    return {
        name: 'Mary'
    };
}
var person1 = new Person('Jack');
console.log(person1.name);    // 'Mary'
console.log(person1.age);     // undefined
複製程式碼

而下面的情況:

function Person(name, age) {
    this.name = name;
    this.age = age;
    return this;
}

var person1 = Person('Bob', 18);
console.log(person1.name);    // Bob
console.log(window.name);     // Bob
console.log(person1 === window);    // true
複製程式碼

建構函式呼叫this指向該例項物件。

4.4 call 、apply 、bind

callapply均可以改變this指向,差別在於傳入引數的不同。

A.call(this,arg1,arg2...,argn)

A.apply(this,[arg1,arg2...,argn])

如果第一個引數為null或者undefined,那麼就非嚴格模式下預設為此時的thiswindow物件,嚴格模式下為undefined.而bind函式返回的是一個新的函式,即方法,不是立即執行函式,需要手動去呼叫bind().

使用 bind()方法建立的上下文,其為永久的上下文環境,不可修改,即使是使用 call 或者 apply方法,也無法修改 this 所指向的值。

5. 還有一種情況:

箭頭函式:此時會從包裹它的函式或作用域中獲取this的值。

箭頭函式中的this定義它的時候已經決定了,與如何呼叫以及在哪裡呼叫它無關,包括 (call, apply, bind)等操作都無法改變它的this

var x=11;
var obj={
 x:22,
 y:this,
 say:()=>{
   console.log(this.x);
 }
}
obj.say();//11
console.log(obj.y);//window
複製程式碼

這裡按照上述“定義”時決定this指向,應該輸出22,但是又可以看出obj物件中的this指代的是window,也就是全域性環境,故箭頭函式中的this就會就近找到上一個物件中this所指代的物件。obj內部屬性y就為obj內部this指代的物件,輸出是window.

關於這點,解釋的還不是太清楚,再補充!

結語

溫故而知新。

相關文章