對于那些沒有花時間學習 this
綁定機制如何工作的 JavaScript 開發(fā)者來說开财,this
綁定一直是困惑的根源。對于 this
這么重要的機制來說,猜測济欢、試錯、或者盲目地從 Stack Overflow 的回答中復制粘貼小渊,都不是有效或正確利用它的方法法褥,通過這篇文章大家可以更好地認識、理解this酬屉。
目錄:
有關(guān)this的困惑
針對以下兩種寫法半等,打印的this.a為什么會不同揍愁??
var obj = {
foo: function () {
console.log(this.a)
},
a: 1
};
var foo = obj.foo;
var a = 2;
// 方式一
obj.foo() // 1
// 方式二
foo() // 2
這里的this.count和foo.count有什么區(qū)別杀饵?莽囤?
function foo(num) {
console.log( "foo: " + num );
this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
foo( i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log( foo.count ); // 0
如果你對上面的內(nèi)容產(chǎn)生了很大的疑問,那么恭喜你切距,這篇文章非常適合你進一步閱讀朽缎。
我們先給出一個正確的結(jié)論,在后續(xù)講解中我們會直接用到:
this
實際上是在函數(shù)被調(diào)用時建立的一個綁定谜悟,它指向 什么 是完全由函數(shù)被調(diào)用的調(diào)用點來決定的话肖。
這個解釋可能對大家來說非常的含糊,我們接著往后分析:
分析
數(shù)據(jù)存儲結(jié)構(gòu)
首先我們需要了解數(shù)據(jù)的存儲結(jié)構(gòu):
以 var obj = {a: 5}
為例葡幸,
代碼含義:將一個對象賦值給obj最筒;
實際操作:JavaScript引擎會在內(nèi)存里面生成一個{a: 5}的對象,然后將這個對象的內(nèi)存地址賦值給obj蔚叨;實際上obj是一個內(nèi)存的地址(reference)床蜘。
讀取操作:當我們想要讀取obj.a
的值,引擎先從obj
拿到地址缅叠,然后在從地址找到原始對象悄泥,返回他的a屬性。原始的對象以字典結(jié)構(gòu)保存肤粱,每一個屬性名都對應一個屬性描述對象弹囚。
[圖片上傳失敗...(image-281894-1572599613293)]
代碼分析
[圖片上傳失敗...(image-9d7b58-1572599613293)]
知識點:
調(diào)用點:函數(shù)在代碼中被調(diào)用的位置(不是被聲明的位置)。
怎么判斷this:根據(jù)調(diào)用點我們可以清楚的了解當前的this究竟指向什么领曼。
【怎么判斷調(diào)用點鸥鹉?】
function baz() {
// 調(diào)用棧是: `baz`
// 我們的調(diào)用點是 global scope(全局作用域)
console.log( "baz" );
bar(); // <-- `bar` 的調(diào)用點
}
function bar() {
// 調(diào)用棧是: `baz` -> `bar`
// 我們的調(diào)用點位于 `baz`
console.log( "bar" );
foo(); // <-- `foo` 的 call-site
}
function foo() {
// 調(diào)用棧是: `baz` -> `bar` -> `foo`
// 我們的調(diào)用點位于 `bar`
console.log( "foo" );
}
baz(); // <-- `baz` 的調(diào)用點
this的四種常用綁定規(guī)則
默認綁定
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
隱含綁定
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
只有對象屬性引用鏈的最后一層是影響調(diào)用點的:
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
明確綁定
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
foo.apply(obj); // 2
new綁定
var a = 10;
function test() {
a = 5;
alert(a);
alert(this.a);
var a;
alert(this.a);
alert(a);
}
test()
new test()
new在創(chuàng)建的時候究竟經(jīng)歷了一些什么?
- 一個全新的對象會憑空創(chuàng)建(就是被構(gòu)建)庶骄;
- 這個新構(gòu)建的對象會被接入原形鏈([[Prototype]]-linked)毁渗;
- 這個新構(gòu)建的對象被設置為函數(shù)調(diào)用的 this 綁定;
- 除非函數(shù)返回一個它自己的其他 對象单刁,否則這個被 new 調(diào)用的函數(shù)將 自動 返回這個新構(gòu)建的對象灸异。
這個四個規(guī)則是依次更強的,也就是說new > 明確綁定 > 隱形綁定 > 默認綁定羔飞。
es6箭頭函數(shù)
function foo() {
setTimeout(() => {
// 這里的 `this` 是詞法上從 `foo()` 采用
console.log( this.a );
},100);
}
var obj = {
a: 2
};
foo.call( obj ); // 2
箭頭函數(shù)的本質(zhì)是使用廣為人知的詞法作用域來禁止了傳統(tǒng)的 this 機制肺樟,箭頭函數(shù)不會創(chuàng)建自己的this,它只會從自己作用域的上一層繼承this逻淌。
這里的箭頭函數(shù)繼承了上一層的this(obj么伯,這里用了明確綁定),所以最終的this指向了obj.a為2卡儒。
練習
練習一:
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
練習二:
var x = 10;
var foo = {
x: 20,
bar: function () {
var x = 30;
return this.x;
}
};
console.log(
foo.bar(),
(foo.bar)(),
(foo.bar = foo.bar)(),
(foo.bar, foo.bar)()
);
練習三:
var age = 20;
var person = {
"age" : 10,
"getAgeFunc" : function(){
return function(){
return this.age;
};
}
};
console.log(person.getAgeFunc()());
感謝觀看~