Javascript基礎之-this
this應該算是前期比較容易混淆的一個關鍵字了,在這裡,我就打算按照我的理解來說一下
首先呢,this的值是跟執行時被呼叫的位置相關的,而不是詞法作用域。
也就是說,他的繫結的值很可能是動態的,不同的呼叫位置,很可能值就不一樣,比如說:
function foo() {
console.log(this.a);
}
var a = 2;
var obj = {
a: 3,
foo,
}
foo(); //2
obj.foo(); //3
var c = obj.foo;
c(); //2
然後你會驚訝的發現,foo()通過不同的形式呼叫,他最終的值是不一樣的,我們們這一節主要就來講一講他們裡面的這些繫結的規則
預設繫結
獨立的函式呼叫,它的作用域是作用在全域性的,這種繫結規則最常見並且是優先順序最低的一種繫結規則。來一個例子
function foo() {
console.log(this.a); //2
console.log(this === window); //true
}
var a = 2;
foo();
在瀏覽器中執行,會發現,如果foo()作為一個函式單獨呼叫,那麼this指向的就是全域性物件windows。
但是呢,也有一種例外,如果函式使用的嚴格模式的話,那麼全域性物件講無法使用預設繫結,this將會繫結到undefined,還是剛才的例子:
function foo() {
"use strict"
console.log(this.a);
console.log(this === window);
}
var a = 2;
foo(); //Uncaught TypeError: Cannot read property `a` of undefined
可以看到,foo直接丟擲一個error,說this是undefined,取不到a,如果用到嚴格模式的時候,可以稍微注意下這個區別
我們們剛剛的例子,說的是函式內用嚴格模式的話,講無法使用預設繫結,那麼如果是在函式呼叫位置用嚴格模式,會受影響嗎
function foo() {
console.log(this.a); //2
console.log(this === window); //true
}
var a = 2;
(function () {
"use strict"
foo();
})();
很明顯,可以看出,是不受影響的,還有一個問題,如果把var定義變成let會怎麼樣呢
function foo() {
console.log(this.a); //undefined
console.log(this === window); //true
}
let a = 2;
(function () {
"use strict"
foo();
})();
也就是說,let定義並不會加到全域性變數中,這個和var的區別需要稍微留意一下
隱式繫結
這一條規則考慮的是是否有上下文物件,舉個例子
function foo() {
console.log(this.a); //2
console.log(this === obj); // true
}
var obj = {
a: 2,
foo,
}
obj.foo();
在這種情況下,foo會使用obj的上下文物件來引用函式,此時的this指向的就是obj。
那麼如果是巢狀物件這種的,會怎麼處理呢?
function foo() {
console.log(this.a); // 1
console.log(this === obj) // true
console.log(this === obj1) // fale
}
var obj = {
a: 1,
foo,
};
var obj1 = {
a: 2,
obj,
}
obj1.obj.foo();
可以看到,實際上此時foo中的this指向了離他最近的物件obj,也就是說,物件屬性引用鏈的最後一層會影響它的呼叫位置
還有一種情況。
function foo() {
console.log(this.a);
}
var obj = {
a: 3,
foo,
}
var a = 2;
var f = obj.foo;
f(); // 2
這種情況下,雖然f是obj.foo的引用,但是最後依然用的是預設繫結,而不是隱式繫結,所以說呢,我們們可以這麼理解,雖然他是物件中函式的引用,但是在執行的時候,呼叫的方式確是函式呼叫的方式,不是物件.屬性()的方式,這塊在使用的時候需要注意一下
顯式繫結
很簡單,顯式繫結就是手動直接改變其this指向的一種方式,這個主要是用幾種api來實現的
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
};
foo.call(obj);
foo.apply(obj);
var f = foo.bind(obj);
f();
這個就是顯式繫結的常見的三種形式
這三種形式的話,call和apply差不多,就不展開了,詳情看文件就行,我們們仔細看一下bind函式,來在看一個例子
function foo(num1, num2) {
console.log(num1 + num2); //5
console.log(this === obj); // true
}
var obj = {
a: 1,
};
var f = foo.bind(obj, 2);
f(3);
這個可以看出,bind的用法和call以及apply還是有一些差別的。
注意,如果顯式繫結傳入的是null或者是undefined的話,將會使用預設繫結規則,舉個例子
function foo() {
console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
new繫結
這個主要是new 函式()的繫結形式的,這個的this指向的是函式構造出的那個物件,new的時候,它的執行過程可以大概執行以下幾步
建立一個全新的物件
這個新物件會被執行prototype連線
新物件繫結到函式呼叫的this
如果函式沒有返回其他物件,那麼new表示式中的函式呼叫會自動返回這個新物件
注意最後一句話,如果函式沒有返回物件,那麼就會返回函式呼叫的新物件,如果返回了一個物件呢,那麼結果就是直接返回這個物件
var abc = {
a: 10
}
function foo() {
return abc;
}
foo.prototype = {
b: 20
};
var a = new foo();
console.log(a === abc); // true
console.log(a.b); // undefined
function bar() {
this.a = 10;
}
bar.prototype = {
b: 20
};
var b = new bar();
console.log(b.a); // 10
console.log(b.b); // 20
以上四種常見的this繫結方式已經介紹的差不多了,那麼問題來了,誰的優先順序更高呢
很明顯,預設繫結的優先順序最低,就不展開說了
顯式繫結與隱式繫結哪個優先順序高呢,舉個例子
function foo() {
console.log( this. a );
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2
很明顯,顯式繫結優先順序要高於隱式繫結
那麼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的優先順序會更高
所以,這樣的話,優先順序排序就出來了,分別為
new繫結 > 顯式繫結 > 隱式繫結 > 預設繫結
以上就是大家常用到的幾種,不過es6出現了一種箭頭函式,箭頭函式理論上不能應用上面四種的任意一種,它的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
結果已經很明顯了,
好了,以上內容就講完了,通過我的一些講解,是不是有一點理解了呢,如果有任何的不同意見,歡迎討論哦
參考書籍《你不知道的Javascript》上卷
轉載自:http://www.lht.ren/article/4/
相關文章
- Javascript基礎之-PromiseJavaScriptPromise
- 前端基礎之JavaScript引入前端JavaScript
- JavaScript 基礎之物件ObjectJavaScript物件Object
- JavaScript基礎之BOM操作JavaScript
- JavaScript基礎之DOM操作JavaScript
- 深入JavaScript基礎之深淺拷貝JavaScript
- javascript基礎JavaScript
- 前端基礎之JavaScript的資料型別前端JavaScript資料型別
- JavaScript基礎2JavaScript
- JavaScript基礎1JavaScript
- JavaScript基礎8JavaScript
- JavaScript基礎10JavaScript
- JavaScript基礎原理JavaScript
- JavaScript Promise(基礎)JavaScriptPromise
- 長篇總結之JavaScript,鞏固前端基礎JavaScript前端
- 前端之路---入坑篇之JavaScript基礎筆記前端JavaScript筆記
- Javascript基礎之-強制型別轉換(三)JavaScript型別
- Javascript基礎之-強制型別轉換(一)JavaScript型別
- JavaScript 基礎卷(一):基礎語法JavaScript
- 前端基礎入門四(JavaScript基礎)前端JavaScript
- javaScript基礎講解JavaScript
- JavaScript入門基礎JavaScript
- JavaScript基礎筆記JavaScript筆記
- JavaScript 基礎語法JavaScript
- JavaScript WebGL 基礎概念JavaScriptWeb
- javascript基礎知識JavaScript
- JavaScript基礎總結JavaScript
- JavaScript基礎(一)概述JavaScript
- 【Javascript 基礎】原型鏈JavaScript原型
- 前端基礎之jQuery基礎前端jQuery
- JavaScript 基礎 - 第1天JavaScript
- 前端-JavaScript基礎知識前端JavaScript
- 最全JavaScript基礎總結JavaScript
- JavaScript WebGL 基礎疑惑點JavaScriptWeb
- 重學javascript基礎-typeofJavaScript
- javascript基礎使用筆記JavaScript筆記
- JavaScript基礎——使用陣列JavaScript陣列
- JavaScript基礎總結(二)JavaScript