這是我參與8月更文挑戰的第5天,活動詳情查看:8月更文挑戰markdown
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identify.call( this );
console.log( greeting );
}
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
identify.call( me ); // KYLE
identify.call( you ); // READER
speak.call( me ); // Hello, 我是 KYLE
speak.call( you ); // Hello, 我是 READE
複製代碼
這段代碼能夠在不一樣的上下文對象(me 和 you)中重複使用函數 identify() 和 speak(), 不用針對每一個對象編寫不一樣版本的函數。app
若是不使用 this,那就須要給 identify() 和 speak() 顯式傳入一個上下文對象。ide
function identify(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = "Hello, I'm " + identify( context );
console.log( greeting );
}
identify( you ); // READER
speak( me ); //hello, 我是 KYLE
複製代碼
this
關鍵字, 被自動定義在全部函數的做用域中。函數
注意:this的指向在函數建立時肯定不了,被調用時才能肯定。post
咱們在找this指向時,要先找到調用位置
,而後判斷符合哪一條綁定規則,同時注意綁定規則的優先級。ui
在找不到函數的調用對象時,this
注意: 對於默認綁定來講,決定 this 綁定對象的並非調用位置是否處於嚴格模式,而是函數體是否處於嚴格模式
。編碼
例1:函數調用前面沒有對象spa
function sayHi(){
console.log('Hello,', this.name);
}
var name = 'YvetteLau';
sayHi();
// 'Hello,' YvetteLau
複製代碼
經過對象調用函數,那麼函數中this會指向最後調用它的對象。code
隱式丟失
1.將obj.fn()賦值給某個變量
雖然 bar 是 obj.foo 的一個引用,可是實際上,它引用的是 foo 函數自己,所以此時的 bar() 實際上是一個不帶任何修飾的函數調用,所以應用了默認綁定。
以下示例, o.foo
的 this
隱式綁定在了 o
對象上, 而 bar
引用了 o.foo
函數自己, 因此此時的 bar()
實際上是一個不帶任何修飾的函數調用, 所以使用了 默認綁定 規則:
var o = {
a: 1,
foo() {
console.log(this.a)
}
}
var bar = o.foo
o.foo() // 1
bar() // undefined
複製代碼
2.將obj.fn()賦值給函數的形參
參數傳遞其實就是一種隱式賦值,所以咱們傳入函數時也會被隱式賦值。示例中, bar(o.foo)
實際上採用了隱式賦值: callback = o.foo
, 事實上跟上面的例子同樣, 都是直接引用了 o.foo
函數自己, 因此形成了 隱式丟失:
function bar(callback) {
callback()
}
var o = {
a: 1,
foo() {
console.log(this.a)
}
}
bar(o.foo) // undefined
複製代碼
3.setTimeOut函數中的this
例2:setTimeOut中包含的函數,在後面執行時,並無對象去調用這個函數,因此也是默認綁定。
btn.onclick = function(){
setTimeout(function(){
console.log(this);
},0)
}
btn.onclick();
// window
複製代碼
雖然onclick函數是btn對象調用的,但setTimeout函數中的內容並沒有
在btn對象調用onclick函數時當即執行
,等到setTimeout函數執行時,setTimeout函數找不到調用對象,這時變爲默認綁定
,非嚴格模式下,setTimeout函數內的this指向window
。
(setTimeOut任務是宏任務
,會在下一輪事件循環
執行。具體參見:juejin.cn/post/693532…)
咱們有時想要強制爲某個函數綁定this,能夠經過使用 call()
和 apply()
方法來實現;
函數.call(要綁定this的對象, 參數1, 參數2, ...)
函數.apply(要綁定this的對象, [參數1, 參數2, ...])
硬綁定
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
var bar = function() {
foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬綁定的 bar 不可能再修改它的 this
bar.call( window ); // 2
複製代碼
咱們建立了函數 bar(),並在它的內部手動調用 了 foo.call(obj) ,所以強制把 foo 的 this 綁定到了 obj。不管以後如何調用函數 bar,它 總會手動在 obj 上調用 foo。這種綁定是一種顯式的強制綁定,所以咱們稱之爲硬綁定。
硬綁定ES5API
bind(..) 會返回一個硬編碼的新函數,它會把參數設置爲 this 的上下文並調用原始函數。
硬綁定時傳入null/undefined,則會忽略本次硬綁定,採用默認綁定規則。
當使用 new 操做符調用某個函數時,這個函數被構造調用
。
new
操做符在調用函數時作的工做:
this
會指向這個新對象new
表達式中的函數調用會自動返回這個新對象new綁定 > 顯式綁定 > 隱式綁定 > 默認綁定
判斷this:
- 函數是否在 new 中調用(new 綁定)?若是是的話 this 綁定的是新建立的對象。
- 函數是否經過 call、apply(顯式綁定)或者硬綁定調用?若是是的話,this 綁定的是 指定的對象。
- 函數是否在某個上下文對象中調用(隱式綁定)?若是是的話,this 綁定的是那個上 下文對象。
- 若是都不是的話,使用默認綁定。若是在嚴格模式下,就綁定到 undefined,不然綁定到全局對象。
箭頭函數沒法使用上述規則。根據根據外層(函數或者全局)做用域來決定 this。
箭頭函數的this指向它外層做用域調用時的this。
foo() 內部建立的箭頭函數會捕獲調用時 foo() 的 this
。因爲 foo() 的 this 綁定到 obj1, bar(引用箭頭函數)的 this 也會綁定到 obj1,箭頭函數的綁定沒法被修改。(new 也不行!)
function foo() {
// 返回一個箭頭函數
return (a) => {
//this 繼承自 foo()
console.log( this.a );
};
}
var obj1 = {
a:2
};
var obj2 = {
a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是 3
複製代碼
箭頭函數會繼承外層函數調用的 this 綁定(不管 this 綁定到什麼)。
你不知道的JavaScript