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); // 2
3.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(); // 2
obj1.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); // 2
obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3
var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.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); // 2
var baz = new bar(3);
console.log(obj1.a); // 2
console.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、如果都不是的話,使用默認綁定。在嚴格模式下,this綁定到undefined,在非嚴格模式下,綁定到全局對象。調用例子: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是不能被修改的。
來自:http://www.cnblogs.com/xfshen/p/6011541.html