JavaScript作為一種腳本語言身份的存在华坦,因此被很多人認(rèn)為是簡單易學(xué)的。然而情況恰恰相反不从,JavaScript支持函數(shù)式編程惜姐、閉包、基于原型的繼承等高級功能椿息。由于其運(yùn)行期綁定的特性歹袁,JavaScript 中的 this 含義要豐富得多,它可以是全局對象寝优、當(dāng)前對象或者任意對象宇攻,這完全取決于函數(shù)的調(diào)用方式。JavaScript中函數(shù)的調(diào)用有以下幾種方式:作為對象方法調(diào)用倡勇,作為函數(shù)調(diào)用,作為構(gòu)造函數(shù)調(diào)用,和使用 apply 或 call 調(diào)用妻熊。本文就采擷些例子以淺顯說明在不同調(diào)用方式下的不同含義夸浅。
『有則推薦』: 自 2017 年初,就有開始利用閑余時(shí)光扔役,打磨個(gè)人最新作品——「傾城之鏈」 帆喇,有意將其打造成優(yōu)良開放型平臺,旨在云集全球優(yōu)秀網(wǎng)站亿胸,讓您更為便捷地探索互聯(lián)網(wǎng)中那更廣闊的世界坯钦;在這里,您可以輕松發(fā)現(xiàn)侈玄、學(xué)習(xí)婉刀、分享更多
有用
或有趣
的事物。目前仍在不斷迭代序仙、優(yōu)化中突颊,如果您對此感興趣,不妨先嘗試一下: 「傾城之鏈」潘悼;亦十分歡迎提出您寶貴意見或建議律秃。 (Upade@2018-01-23 于深圳.南山)。也可以通過微信治唤,掃描如下「小程序碼」訪問體驗(yàn)棒动。
全局的this
全局this一般指向全局對象,瀏覽器中的全局對象就是 window宾添。例如:
console.log(this.document === document); //true
console.log(this === window); //true
this.a = 91;
console.log(window.a); //91
一般函數(shù)的 this
function f1 () {
return this;
}
console.log(f1() === window);//true, global object
可以看到一般函數(shù)的 this 也指向 window船惨,在 nodeJS 中為 global object
function f2 () {
"use strict";//使用嚴(yán)格模式
return this;
}
console.log(f1() === undefined);//true
嚴(yán)格模式中,函數(shù)的 this 為 undefined,因?yàn)閲?yán)格模式禁止this關(guān)鍵字指向全局對象辞槐;對于js“嚴(yán)格模式”具體可以看阮一峰先生的Javascript 嚴(yán)格模式詳解
作為對象方法的函數(shù)的 this
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // 37
上述代碼通過字面量創(chuàng)建對象 o掷漱。
f 為對象 o 的方法。這個(gè)方法的 this 指向這個(gè)對象榄檬,在這里即對象 o卜范。
var o = {
prop: 37
};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // 37
上面的代碼,創(chuàng)建了對象 o鹿榜,但是沒有給對象 o海雪,添加方法。而是通過 o.f = independent 臨時(shí)添加了方法屬性舱殿。這樣這個(gè)方法中的 this 同樣也指向這個(gè)對象 o奥裸。
作為函數(shù)調(diào)用
函數(shù)也可以直接被調(diào)用,此時(shí) this 綁定到全局對象沪袭。在瀏覽器中湾宙,window 就是該全局對象。比如下面的例子:函數(shù)被調(diào)用時(shí),this被綁定到全局對象侠鳄,接下來執(zhí)行賦值語句埠啃,相當(dāng)于隱式的聲明了一個(gè)全局變量,這顯然不是調(diào)用者希望的伟恶。
function makeNoSense(x) {
this.x = x;
}
makeNoSense(5);
x;// x 已經(jīng)成為一個(gè)值為 5 的全局變量
對于內(nèi)部函數(shù)碴开,即聲明在另外一個(gè)函數(shù)體內(nèi)的函數(shù),這種綁定到全局對象的方式會產(chǎn)生另外一個(gè)問題博秫。以下面moveTo方法為例潦牛,內(nèi)定義兩個(gè)函數(shù),分別將 x挡育,y 坐標(biāo)進(jìn)行平移巴碗。結(jié)果可能出乎大家意料,不僅 point 對象沒有移動静盅,反而多出兩個(gè)全局變量 x良价,y。
var point = {
x : 0,
y : 0,
moveTo : function(x, y) {
// 內(nèi)部函數(shù)
var moveX = function(x) {
this.x = x;//this 綁定到了哪里蒿叠?
};
// 內(nèi)部函數(shù)
var moveY = function(y) {
this.y = y;//this 綁定到了哪里明垢?
};
moveX(x);
moveY(y);
}
};
point.moveTo(1, 1);
console.log(point.x) //0
console.log(point.x) //0
console.log(x) //1
console.log(y) //1
這屬于 JavaScript 的設(shè)計(jì)缺陷,正確的設(shè)計(jì)方式是內(nèi)部函數(shù)的this應(yīng)該綁定到其外層函數(shù)對應(yīng)的對象上市咽,為了規(guī)避這一設(shè)計(jì)缺陷痊银,聰明的JavaScript程序員想出了變量替代的方法,約定俗成施绎,該變量一般被命名為 that溯革。
對象原型鏈上的this
var o = {
f: function() {
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 2;
console.log(p.f()); //3
通過 var p = Object.create(o) 創(chuàng)建的對象,p 是基于原型 o 創(chuàng)建出的對象谷醉。
p 的原型是 o致稀,調(diào)用 f() 的時(shí)候是調(diào)用了 o 上的方法 f(),這里面的 this 是可以指向當(dāng)前對象的俱尼,即對象 p抖单。
get/set 方法與 this
function modulus() {
return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
re: 1,
im: -1,
get phase() {
return Math.atan2(this.im, this.re);
}
};
Object.defineProperty(o, 'modulus', {
get: modulus,
enumerable: true,
configurable: true
});
console.log(o.phase, o.modulus); // -0.78 1.4142
get/set 方法中的 this 也會指向 get/set 方法所在的對象的。
構(gòu)造器中的 this
function MyClass() {
this.a = 25;
}
var o = new MyClass();
console.log(o.a); //25
new MyClass() 的時(shí)候遇八,MyClass()中的 this 會指向一個(gè)空對象矛绘,這個(gè)對象的原型會指向 MyClass.prototype。MyClass()沒有返回值或者返回為基本類型時(shí)刃永,默認(rèn)將 this 返回货矮。
function C2() {
this.a = 26;
return {
a: 24
};
}
o = new C2();
console.log(o.a); //24
因?yàn)榉祷亓藢ο螅瑢⑦@個(gè)對象作為返回值
call/apply 方法與 this
function add(c, d) {
return this.a + this.b + c + d;
}
var o = {
a: 1,
b: 3
};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // "[object Number]"
bar.call(); //[object global]
bar.call("7");//[object String]
bar.call(true);//[object Boolean]
console.log(add.call(o,5,7));//16
bind 方法與 this
function f() {
return this.a;
}
var g = f.bind({
a: "test"
});
console.log(g()); // test
var o = {
a: 37,
f: f,
g: g
};
console.log(o.f(), o.g()); // 37, test
綁定之后再調(diào)用時(shí)斯够,仍然會按綁定時(shí)的內(nèi)容走囚玫,所以 o.g() 結(jié)果是 test
JavaScript中this的些許看似怪異現(xiàn)象
<body>
<!--JavaScript偽協(xié)議和內(nèi)聯(lián)事件對于this的指向不同-->
<a href="#" onclick="alert(this.tagName);">click me</a> <!--彈出A-->
<a href="javascript:alert(this.tagName);">click me</a> <!--彈出undefined-->
<a href="javascript:alert(this==window);">click me</a> <!--彈出true-->
<input id="btn" type="button" value="this demo" name="button"/>
</body>
var name = 'somebody';
var angela = {
name: 'angela',
say: function () {
alert("I'm " + this.name);
}
};
var btn = document.getElementById('btn');
setTimeout和setInterval也會改變this的指向
angela.say();//I'm angela
setTimeout(angela.say, 1000); //I'm somebody
setInterval(angela.say, 1000); //I'm somebody
on...也會改變this的指向
angela.say(); //I'm angela
btn.onclick = angela.say; //I'm button
click等回調(diào)也會改變this指向
$("#btn").click = angela.say; // I'm button
$("#btn").click(angela.say); // I'm button
如果在say中用了this喧锦,this會綁定在angela上么?顯然這里不是劫灶,賦值以后裸违,函數(shù)是在回調(diào)中執(zhí)行的,this會綁定到$(“#btn”)元素上本昏。這個(gè)函數(shù)被完整復(fù)制到onclick屬性(現(xiàn)在成為了函數(shù))。因此如果這個(gè)even thandler被執(zhí)行枪汪,this將指向HTML元素;因此涌穆,結(jié)果顯示的是"I'm button"。而雀久,匿名函數(shù)可以調(diào)整this指向,EG:
$("#btn").click(function(){
angela.say(); //I'm angela
});
這是JavaScript新手們經(jīng)常犯的一個(gè)錯誤宿稀,為了避免這種錯誤,許多JavaScript框架都提供了手動綁定 this 的方法赖捌。比如Dojo就提供了lang.hitch祝沸,該方法接受一個(gè)對象和函數(shù)作為參數(shù),返回一個(gè)新函數(shù)越庇,執(zhí)行時(shí)this綁定到傳入的對象上罩锐。使用 Dojo,可以將上面的例子改為:
button.onclick = lang.hitch(angela, angela.say);
其實(shí)在我們使用比較多的jQuery也提供了對應(yīng)的解決方案:jQuery.proxy(function, scope).返回一個(gè)新函數(shù)卤唉,并且這個(gè)函數(shù)始終保持了特定的作用域涩惑。其作用跟Dojo就提供了lang.hitch類似,具體可以參考這里桑驱。其中有一例如下:
<div id="test">Click Here!</div> //html Code
var obj = {
name: "John",
test: function() {
alert( this.name );
$("#test").unbind("click", obj.test);
}
};
$("#test").click( jQuery.proxy( obj, "test" ) );
//強(qiáng)制設(shè)置函數(shù)的作用域竭恬,讓this指向obj而不是#test對象。
// 以下代碼跟上面那句是等價(jià)的:
// $("#test").click( jQuery.proxy( obj.test, obj ) );
// 可以與單獨(dú)執(zhí)行下面這句做個(gè)比較熬的。
// $("#test").click( obj.test );
在新版的 JavaScript 中痊硕,已經(jīng)提供了內(nèi)置的 bind 方法供大家使用。
匿名函數(shù)調(diào)整this指向比如:
setTimeout(function () { angela.say(); }, 1000); //I'm angela
setInterval(function () { angela.say(); }, 1000) //I'm angela
btn.onclick = function () { angela.say(); }; //I'm angela
setTimeout(function () { alert(this == window); }, 1000);//true
btn.onclick = function () { alert(this == btn); }//true
匿名函數(shù)賦值給了click屬性(好吧押框,現(xiàn)在成了函數(shù))岔绸,此時(shí)這個(gè)匿名函數(shù)指向的即是Html屬性。因此所調(diào)用的函數(shù)(比如angela.say())this上下文沒有被更改强戴,所以其打印出來的結(jié)果就是'I'm angela'亭螟。事實(shí)上,也用這樣的方法來消解this在回調(diào)函數(shù)中不堪使用的'特色'骑歹。
$("#btn").click(function(){
if(window == this){
alert("window == this");
}else{
alert("window != this") //彈出來
}
alert(this.name); // button
angela.say(); //I'm angela
});
將this指向的對象保存到變量(一般用that)
var mydemo = {
name: 'angela',
say: function () { alert("I'm " + this.name); },
init: function () {
var that = this;
document.getElementById('btn').onclick = function () {
that.say(); //彈出Alert:I'm angela
this.say(); //這兒報(bào)錯: undefined is not a function (evaluating 'this.say()')
}
}
};
mydemo.init();
第三方庫or框架中的this
比如预烙,使用backbone框架中events時(shí)間回調(diào)中的this,其指向的就是對應(yīng)的視圖道媚,而不是Dom元素扁掸,因?yàn)樵摶卣{(diào)時(shí)通過events哈希綁定的翘县,實(shí)質(zhì)上也是自對應(yīng)視圖那里callback到對應(yīng)的函數(shù);
Javascript中的eval 方法
JavaScript 中的 eval 方法可以將字符串轉(zhuǎn)換為 JavaScript 代碼,使用 eval 方法時(shí)谴分,this 指向哪里呢锈麸?答案很簡單,看誰在調(diào)用 eval 方法牺蹄,調(diào)用者的執(zhí)行環(huán)境(ExecutionContext)中的 this 就被 eval 方法繼承下來了忘伞。(悪,還沒用過,有待實(shí)踐下)沙兰!
但是:在嚴(yán)格模式之下氓奈,eval的作用域也被改變了。正常模式下鼎天,eval語句的作用域舀奶,取決于它處于全局作用域,還是處于函數(shù)作用域斋射。嚴(yán)格模式下育勺,eval語句本身就是一個(gè)作用域,不再能夠生成全局變量了罗岖,它所生成的變量只能用于eval內(nèi)部涧至。
"use strict";
var x = 2;
console.info(eval("var x = 5; x")); // 5
console.info(x); // 2
后記:由于javascript的動態(tài)性(解釋執(zhí)行,當(dāng)然也有簡單的預(yù)編譯過程)呀闻,this的指向在運(yùn)行時(shí)才確定化借,因此在只要足夠留心其運(yùn)行時(shí)的上下文,即可無痛揮霍this的強(qiáng)大捡多。
原文鏈接:http://www.jeffjade.com/2015/08/03/2015-08-03-javascript-this/
參考AJavaScript 中的 this
參考BJavaScript中this的一些怪異現(xiàn)象
參考CJavascript的this用法-阮一峰
參考D深入淺出 JavaScript 中的 this