標籤:efi src alt ext return 對象引用 指定 全域對象 箭頭
JavaScript this用法總結
在JavaScript中,this關鍵字可以說是最複雜的機制之一。對this的作用機制缺乏比較深入的理解很容易在實際開發中出現問題。
1、this的作用
為什麼要在JavaScript中使用this呢?因為this提供了一種簡明的方式來隱式傳遞一個對象引用,可以讓函數介面設計的簡單且容易複用:
function display() { console.log(this.name);}var obj1 = {name: "obj1"};var obj2 = {name: "obj2"};display.call(obj1); // "obj1"display.call(obj2); // "obj2"
通過call方法,我們可以在調用display函數時為this傳入不同的對象。如果不使用this關鍵字,那麼上面的函數就需要顯示增加一個調用時上下文參數:
function display(context) { console.log(context.name);}var obj1 = {name: "obj1"};var obj2 = {name: "obj2"};display(obj1); // "obj1"display(obj2); // "obj2"
實際上這不夠簡潔,當使用模式比較複雜時,顯示的上下文傳遞會讓代碼變得混亂複雜。使用this關鍵字,我們可以在調用時為this傳入不同的對象引用,保證了方法的使用靈活性。
2、this的使用複雜性
this使用機制複雜,在開發容易出問題的根本原因在於:this是在運行時綁定,而不是在編寫時綁定,this實際值取決於函數調用時的上下文。this的綁定和函式宣告的位置沒有關係,只取決於函數的調用方式。在JavaScript中,當函數被調用時,會建立一個活動記錄(執行時上下文),這個記錄包含函數在何處調用、函數的調用方法和傳入參數等資訊,this會記錄其中一個屬性。判斷this實際綁定值,關鍵在於分析函數實際調用的位置。
3、this綁定規則
前面說了函數的實際調用位置決定了this的綁定值。在JavaScript中,this有4種綁定規則。
3.1、new綁定
在JavaScript中使用new調用函數會自動執行下面的操作:
(1)建立一個新的對象
(2)對新對象執行原型連結
(3)新對象會被綁定到函數的this
(4)如果函數沒有返回其他對象,那麼新對象會被返回
new綁定容易理解,下面是一段常見的用new調用函數建立對象的代碼:
function Book(name, author, isbn) { this.name = name; this.author = author; this.isbn = isbn;}let book = new Book("Zakas", "ES6", 12345);console.log(book.name); // "Zakas" 3.2、隱式綁定
當對象內部包含一個指向函數的屬性,並且在調用時通過這個屬性間接引用函數(obj.prop()的形式),那麼函數內的this會隱式指向這個對象,也即隱式綁定:
function foo() { console.log(this.a);}var obj = { a: 2, foo: foo};obj.foo(); // 2
在調用位置上,函數是通過obj.foo來引用的,可以說函數被調用時obj對象擁有或包含它。此時,this綁定在obj這個內容物件上。
3.3、顯示綁定
在某些情況下,我們希望函數內的this綁定在某些指定的對象上,這稱為顯示綁定。在JavaScript中可以使用call和apply為函數顯示指定this綁定。call和apply的第一個參數是一個對象,這個對象會被綁定到this上:
function foo() { console.log(this.a);}var obj = { a:2};foo.call(obj); // 2
使用bind也可以讓this綁定在指定對象上,bind綁定也是一種顯示綁定,又稱為硬綁定:
function foo(something) { this.a = something;}var obj1 = {};var bar = foo.bind(obj1);bar(2);console.log(obj1.a); // 23.4、預設綁定
當使用獨立函數調用(func()形式),會發生預設綁定,可以把這條規則看成是無法使用其他規則時的預設規則。看下面的範例程式碼:
function foo() { console.log( this.a );}var a = 2;foo(); // 2
當調用foo時,使用預設綁定規則,this被綁定到全域對象上。在strict模式下,this會綁定到undefined。
4、綁定優先順序
上面4種綁定規則獨立使用的話,判斷this的綁定值並不複雜。但實際函數調用時,可能多條綁定規則都可以使用,那麼這時就要根據每個規則的綁定優先順序來判斷this實際的綁定值。接下來看各種綁定規則的優先順序。
4.1、預設綁定優先順序最低
預設綁定的優先順序最低,這個容易理解。因為當無法使用其他的綁定規則時才會使用預設規則。
4.2、顯示綁定優先順序高於隱式綁定
function foo() { console.log(this.a);}var obj1 = {a: 2, foo: foo};var obj2 = {a: 3, foo: foo};obj1.foo(); // 2obj1.foo.call(obj2); // 3
上面的代碼中,obj1.foo()使用隱式綁定規則,this綁定到obj1對象上。obj1.foo.call()可同時使用隱式綁定和顯示綁定規則,顯示綁定優先順序高於隱式綁定,this綁定到obj2對象上。
4.3、new綁定優先順序高於隱式綁定
function foo(something) { this.a = something;}var obj1 = { foo: foo};var obj2 = {};obj1.foo(2);console.log(obj1.a); // 2obj1.foo.call(obj2, 3);console.log(obj2.a); // 3var bar = new obj1.foo(4);console.log(obj1.a); // 2console.log(bar.a); // 4
上面代碼中,new obj1.foo(4)可同時使用new綁定和隱式綁定。由bar.a的值為4可以知道,new綁定優先順序高於隱式綁定。
4.4、new綁定優先順序高於顯示綁定
function foo(something) { this.a = something;}var obj1 = {};var bar = foo.bind(obj1);bar(2);console.log(obj1.a); // 2var baz = new bar(3);console.log(obj1.a); // 2console.log(baz.a); // 3
上面代碼中,new bar(3)可同時使用new綁定和bind綁定。baz.a的值為3,說明new綁定優先順序高於隱式綁定。
4.5、綜述
現在可以根據this綁定優先順序判斷函數在調用位置實際綁定的值。實際可以按照下面的順序判斷:
(1、函數是否在new中調用?如果是的話this綁定新建立的對象。調用例子:var bar = new foo()。
(2、函數是否通過apply、call顯示綁定或者bind硬綁定?如果是,this綁定指定的對象。調用例子:var bar = foo.call(obj)。
(3、函數是否在某個內容物件中調用(隱式綁定)?如果是,this綁定在內容物件上。調用例子:var bar = obj.foo()。
(4、如果都不是的話,使用預設綁定。在strict 模式下,this綁定到undefined,在非strict 模式下,綁定到全域對象。調用例子:var bar = foo()。
5、箭頭函數中的this
ES6中引入了箭頭函數,箭頭函數使用操作符=>定義。箭頭函數不使用上面4種this綁定規則,而是根據外層範圍來決定this:
function foo() { return (a) => { console.log(this.a); };} var obj1 = {a:2};var obj2 = {a:3};var bar = foo.call(obj1);bar.call(obj2); // 2
foo內部的箭頭函數建立時,foo函數內this綁定到obj1上,bar(箭頭函數)的this也會綁定到obj1上,箭頭函數內的this是不能被修改的。
JavaScript this用法總結