1. 程式人生 > >關於this繫結的四種方式

關於this繫結的四種方式

一、前言

我們每天都在書寫著有關於this的javascript程式碼,似懂非懂地在用著。前陣子在看了《你不知道的JavaScript上卷》之後,也算是被掃盲了一邊關於this繫結的四種方式。

二、繫結規則

關於this應用的是哪條規則,得先找到呼叫的位置,再判斷應用了哪條規則。

1、預設繫結

先上程式碼:

var a = 2;

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

foo(); // 結果:2

先來分析下上面的程式碼宣告,

首先我們在全域性作用域中定義了一個變數a,而在全域性作用域宣告的變數,就相當於為window物件聲明瞭同名屬性a並賦值為2,接著又在全域性作用域下聲明瞭foo函式,最後我們在呼叫foo函式時,是直接不帶任何修飾下呼叫foo函式,此時的this的繫結規則為預設繫結

,this指向window物件,所以結果輸入為2。

如果此時使用了嚴格模式,即在程式碼中加了"use strict",this指向undefined,呼叫的結果就會出錯而不是輸出2。

2、隱式繫結

var a = 2;

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

var obj = {
    a: 4,
    foo:foo
};

foo(); // 結果:2
obj.foo();  // 結果4

程式碼還是差不多的程式碼,只是加了一個obj物件,物件有一個a屬性和一個foo屬性引用了foo函式。對於foo();輸出結果2,在上一節已經說明,因為是沒用帶任何修飾的情況下呼叫,應用了預設繫結。而在obj.foo()的呼叫中,foo函式的呼叫上下文是obj物件,obj包含了foo函式,此時this的繫結就發生了隱式繫結

,this指向obj,this.a相當於obj.a,所以結果自然而然也輸入4。

對於這種方式,我在程式碼中也經常用到。把可以歸類的方法、變數都寫成一個物件的形式,就形成了一個模組,也相當於一種設計模式,模組模式。

3、顯示繫結

var a = 2;

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

var obj = {
    a: 4
};

foo.call(obj); // 結果:4
foo.apply(obj); // 結果:4
foo.bind(obj)(); // 結果:4

對於call、apply、bind的這三種呼叫方式都是屬於顯示繫結

,作用是通過顯示傳入一個物件,改變this的上下文為此物件。call和apply是直接改變上下文物件直接呼叫,而bind是返回一個已經顯示繫結的上下文的函式。

call和apply兩個都是顯示改變上下文並執行,唯一不同的就是傳參方式,call是物件後面可以跟著多個引數,而apply傳遞引數,需要傳遞一個數組,即:

foo.call(obj, arg1, arg2, arg3, ...);
foo.apply(obj, [arg1, arg2, arg3, ...])

4、new繫結

學過後端語言的人都知道,通過建構函式,可以new一個物件例項,而在JavaScript中,物件也是通過new建構函式生成的,但,卻和麵向物件語言的new方式是不一樣。下面來看看new操作符到底做了什麼。

當使用new來呼叫函式時,發生了以下步驟:

a、建立了一個全新的物件,如:var obj = {};

b、連線全新物件與呼叫函式之間的[[Prototype]],讓函式的prototype指向全新物件,如obj.__proto__ = foo.prototype;

c、新物件會繫結到函式呼叫的this,如:foo.call(obj);

d、返回物件。如果函式沒有返回其他物件,那麼new操作會自動返回這個新物件。

三、優先順序

一般情況下:new繫結 > 顯示繫結 > 隱式繫結 > 預設繫結

四、總結

還有其他的一些細節知識點,推薦還是看《你不知道的JavaScript上卷》這本書,真心不錯。