一、爲什麼要用this
能夠隱式的傳遞對象引用,更加簡潔。
二、this指向規則和詞法作用域
詞法作用域:作用域由函數書寫代碼時決定(定義時上下文)
this指向規則:不指向函數詞法作用域,不指向函數本身,由函數調用時的位置決定(運行時上下文,類似於動態作用域)
三、this指向規則
this的指向規則如下四條,優先級爲:new +構造函數 > 顯式綁定 > 隱式綁定 > 默認
1.默認(獨立調用)
獨立函數調用情況下,this指向全局變量或undefined。
(1)函數在非嚴格模式下,this指向全局變量
(2)函數在嚴格模式下(use strict),this 指向undefined
2.隱式綁定
(1)調用位置有上下文對象的,this綁定在上下文對象上
(2)調用位置有多個鏈式上下文對象的,this綁定在就近的對象上
注意:隱式綁定可能會出現this的隱式丟失,原因如下:
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 單獨執行
f() // 1
// obj 環境執行
obj.f() // 2
(上例和圖片來自http://www.ruanyifeng.com/blog/2018/06/javascript-this.html)
由以上例子能夠看出,當定義一個對象,並在對象中定義函數的過程是先開闢一個內存存儲函數,然後將地址傳遞給對象的foo。當調用foo()時,是直接調用全局中的foo函數;當調用obj.foo()時是訪問obj.foo存儲的地址,此時this指向obj。
若再次定義var a = obj.foo;即將地址傳給了全局的a,此時調用a()相當於直接調用全局中的函數,此時this指向全局變量(非嚴格模式下)造成this的隱式丟失。
在回調函數和參數傳遞(隱式賦值)時,容易發生this的隱式丟失。
隱式丟失的解決辦法:
(1)var self = this
(2)硬綁定(bind)
(3)箭頭函數(ES6)
3.顯式綁定
顯式的綁定this指向可以採用call()、apply()和硬綁定bind()
call()和apply()的作用完全一樣,只是參數傳遞方式不一樣
(1)函數.call ( this新指向,數據1,... ,數據n)
將函數中的this指向新指向,並向該函數傳遞數據1-n
(2)函數.apply(this新指向,數組或類數組) 其中數組或類數組爲:[數據1,... ,數據n]
將函數中的this指向新指向,並向該函數傳遞數組 [數據1,...,數據n]
call()和apply() 的用法:
- 劫持別人的方法
var foo = {
name:"mingming",
logName:function(){
console.log(this.name);
}
}
var bar={
name:"xiaowang"
};
foo.logName.call(bar);//xiaowang
- 實現繼承
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
var cat = new Cat("Black Cat");
cat.showName(); //Black Cat
- 強制改變this指向
window.id="window";
document.querySelector('#test').onclick = function(){
console.log(this.id);//test
var fun = function(){
console.log(this.id);
}
fun();//window
fun.call(this);//test
}
(3)硬綁定bind(未完)
bind()是ES5新增(IE6,7,8不支持)
var 新函數 = 舊函數.bind(this新指向,數據1,... ,數據n)
bind() 作用和前兩個相同,即改變this指向
call()和apply()在改變指向以後立即執行改變後的函數,而bind()卻返回一個改變後的新函數。
注意:call(),apply()和bind() 在非嚴格模式下,第一個參數爲null或者undefined時this會自動替換爲指向全局對象
4.new+構造函數
在傳統面嚮對象語言中,構造函數是類的特殊函數
在JavaScript中,構造函數只是能夠使用new操作符調用的普通函數,他不屬於某個類,也不能實例一個類
當使用new 來調用構造函數時,自動執行一下操作:
(1)創建一個新的對象
(2)對新對象執行 [[原型]] 連接
(3)把新對象綁定到函數的this
(4)若函數沒有返回其他對象,則函數自動返回這個新對象
四、ES6改進(箭頭函數)
箭頭函數的this不按照以上四條規則,而是按照詞法作用域由外部作用域決定this指向
箭頭函數能夠通過call、apply、bind 改變this指向,但只能改變一次
參考資料:
http://www.ruanyifeng.com/blog/2018/06/javascript-this.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind