只有深諳了this,你纔有可能用 JavaScript 建立相似谷歌地圖這樣大型的複雜應用bash
提供了一種更優雅的方式來隱式」傳遞」一個對象引用, 讓API設計更加簡潔和清晰app
首先來看一段代碼, 此處不使用this, 須要給identify()和speak()顯示的傳入一個上下文對象:ide
// 定義 you & me對象
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
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解決: 能夠在不一樣的上下文對象(me 和 you)中重複使用函數identify()和speak()函數
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = "Hello, I'm " + identify.call( this );
console.log( greeting );
}
identify.call( me ); // KYLE
identify.call( you ); // READER
speak.call( me ); // Hello, 我是 KYLE
speak.call( you ); // Hello, 我是 READER
複製代碼
顯然, 隨着你的使用模式愈來愈複雜, 顯式傳遞上下文對象會讓代碼變得愈來愈混亂, this可讓你的代碼變得更優雅。特別是當你使用對象(關聯)和原型時, 利用this使得函數能夠自動引用合適的上下文對象顯的尤其重要oop
事實上, 一部分人認爲"this既不指向函數自身也不指向函數的詞法做用域", 可是也是不對的, 在某種狀況下, this就指向函數自身, 也可能指向詞法做用域ui
this是在運行(函數被調用)時發生綁定的,並非在編寫時綁定, 它的上下文取決於函數調用時的各類條件,它指向什麼徹底取決於函數在哪裏被調用this
簡單來講, 有這大體四種spa
沒法應用其餘規則時的默認規則, 嚴格模式下綁定到undefined, 不然綁定到全局對象設計
最經常使用的函數調用類型:獨立函數調用code
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
複製代碼
代碼中, foo()是直接使用不帶任何修飾的函數引用進行調用的,只能適用於this的默認綁定,沒法應用其餘規則,所以this指向全局對象
// 嚴格模式下
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
複製代碼
因此, 不推薦這種寫法。
考慮調用位置是否有上下文對象,或者說是否被某個對象或者包含
當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象
必須在一個對象內部包含一個指向函數的屬性,並經過這個屬性間接引用函數,從而把 this 間接(隱式)綁定到這個對象上
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
複製代碼
對象屬性引用鏈中只有最後一層會影響調用位置, 即調用棧的末端
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
複製代碼
強制指定某些對象對函數進行調用,this則強制指向調用函數的對象
顯示綁定場景
function foo() {
console.log( this.a );
}
var obj = {
a:2
};
foo.call( obj ); // 2
複製代碼
硬綁定經常使用場景
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a:2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
複製代碼
new方式優先級最高,只要是使用new方式來調用一個構造函數,this必定會指向new調用函數新建立的對象
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2
複製代碼
,實際緣由是箭頭函數根本沒有本身的this
this指向的固定化,並非由於箭頭函數內部有綁定this的機制, 實際緣由箭頭函數沒有本身的this,它的this是繼承而來,默認指向在定義它時所處的對象(宿主對象)。 捕獲其所在(即定義的位置)上下文的this值,做爲本身的this值, 若是在當前的箭頭函數做用域中找不到變量,就像上一級做用域裏去找, 致使內部的this就是外層代碼塊的this
// demo 1
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 }) // id: 42
//demo 2
function Person() {
this.name = 'dog';
this.age = '18';
setTimeout( () => {
console.log(this);
console.log('my name:' + this.name + '& my age:' + this.age)
}, 1000)
}
var p = Person();
複製代碼
當被綁定的是null,則使用的是默認綁定規則
// 若是你把 null 或者 undefined 做爲 this 的綁定對象傳入 call 、 apply 或者 bind ,這些值在調用時會被忽略,
實際應用的是默認綁定規則
function foo() {
console.log( this.a );
}
var a = 2222;
foo.call( null ); // 2222
複製代碼
最多見的this綁定問題就是被隱式綁定的函數會丟失綁定對象,也是就說它會應用默認綁定,從而把this綁定到全局對象或者undefined上,取決因而否是嚴格模式
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函數別名!
var a = "oops, global"; // a是全局對象的屬性
bar(); // "oops, global"
複製代碼
雖然 bar 是 obj.foo 的一個引用,可是實際上,它引用的是 foo 函數自己,這就至關於:var bar = foo, obj對象只是一箇中間橋樑, obj.foo只起到傳遞函數的做用,因此bar跟obj對象沒有任何關係,此時的 bar() 實際上是一個不帶任何修飾的函數調用. 而bar自己又不帶a屬性,所以應用了默認綁定,最後a只能指向window.
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// fn其實引用的是foo
fn(); // <-- 調用位置!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // a是全局對象的屬性
doFoo( obj.foo ); // "oops, global"
複製代碼
參數傳遞其實就是一種隱式賦值,所以咱們傳入函數時也會被隱式賦值,因此結果和上一 個例子同樣
function thisTo(){
console.log(this.a);
}
var data={
a:2,
foo:thisTo //經過屬性引用this所在函數
};
var a=3;//全局屬性
setTimeout(data.foo,100);// 3
複製代碼
所謂傳參丟失,就是在將包含this的函數做爲參數在函數中傳遞時,this指向改變
setTimeout
函數的原本寫法應該是setTimeout(function(){......},100);
100ms後執行的函數都在「......」中, 能夠將要執行函數定義成var fun = function(){......},
即:setTimeout(fun,100),100ms後就有:fun();因此此時此刻是data.foo做爲一個參數,是這樣的:setTimeout(thisTo,100);100ms事後執行thisTo(), 實際道理還跟1.1差很少,沒有調用thisTo的對象,this只能指向window 實際上你沒辦法控制回調函數的執行方式,沒有辦法控制會影響綁定的調用位置. 所以, 回調函數丟失this綁定是很是常見的,甚至更加出乎意料的是,調用回調函數的函數可能會修改this,特別 是在一些流行的JavaScript庫中時間處理器會把回調函數的this強制綁定到觸發事件的DOM元素上
四種規則:
特殊狀況特殊處理: