this是由JavaScript引擎在执行函数时自动生成,存在于函数内的一个动态指针,指代当前调用对象。具体用法如下:
this[.属性]
如果this未包含属性,则传递的是当前对象。
this用法灵活,其包含的值也是变化多端。例如,下面示例使用call方法不断改变函数内this指代对象。
var x = "window"; //定义全局变量x,初始化为字符串"window"
function a(){ //定义构造函数a
this.x = "a"; //定义私有属性x,初始化为字符a
}
function b(){ //定义构造函数b
this.x = "b"; //定义私有属性x,初始化为字符b
}
function c(){ //定义普通函数,提示变量x的值
console.log( x );
}
function f(){ //定义普通函数,提示this包含的x的值
console.log( this.x );
}
f(); //返回字符串"window",this指向Window对象
f.call( window ); //返回字符串"window",this指向Window对象
f.call( new a() ); //返回字符a,this指向函数a的实例
f.call( new b() ); //返回字符b,this指向函数b的实例
f.call( c ); //返回undefined,this指向函数c对象
下面简单总结this在5种常用场景中的表现,以及应对策略。
普通调用
【示例1】 下面示例演示了函数引用和函数调用对this的影响。
var obj = { //父对象
name : "父对象obj",
func : function(){
return this;
}
}
obj.sub_obj = { //子对象
name : "子对象sub_obj",
func : obj.func //引用父父对象obj的方法func
}
var who = obj.sub_obj.func();
console.log(who.name); //返回"子对象sub_obj",说明this代表sub_obj
如果把子对象sub_obj的func改为函数调用。
obj.sub_obj = {
name : "子对象sub_obj",
func : obj.func() //调用父对象obj的方法func
}
则函数中的this所代表的是定义函数时所在的父对象obj。
var who = obj.sub_obj.func; console.log(who.name); //返回"父对象obj",说明this代表父对象obj
实例化
【示例2】使用new命令调用函数时,this总是指代实例对象。
var obj ={};
obj.func = function(){
if(this == obj) console.log("this = obj");
else if(this == window) console.log("this = window");
else if(this.constructor == arguments.callee) console.log("this = 实例对象");
}
new obj.func; //实例化
动态调用
【示例3】使用call和apply 可以强制改变this,使其指向参数对象。
function func(){
//如果this的构造函数等于当前函数,则表示this为实例对象
if(this.constructor == arguments.callee) console.log("this = 实例对象");
//如果this等于Window,则表示this为Window对象
else if (this == window) console.log("this = window对象");
//如果this为其他对象,则表示this为其他对象
else console.log("this == 其他对象 \n this.constructor = " + this.constructor );
}
func(); //this指向Window对象
new func(); //this指向实例对象
func.call(1); //this指向数值对象
在上面示例中,直接调用函数func()时,this代表Window。当使用new命令调用函数时,将创建一个新的实例对象,this就指向这个新创建的实例对象。
使用call方法执行函数func()时,由于call方法的参数值为数字1,则JavaScript引擎会把数字1强制封装为数值对象,此时this就会指向这个数值对象。
事件处理
【示例4】在事件处理函数中,this总是指向触发该事件的对象。
<input type="button" value="测试按钮" />
<script>
var button = document.getElementsByTagName("input")[0];
var obj ={};
obj.func = function(){
if(this == obj) console.log("this = obj");
if(this == window) console.log("this = window");
if(this == button) console.log("this = button");
}
button.onclick = obj.func;
</script>
在上面代码中,func()所包含的this不再指向对象obj,而是指向按钮button,因为func()是被传递给按钮的事件处理函数之后,才被调用执行的。
如果使用DOM 2级标准注册事件处理函数:
if(window.attachEvent){ //兼容IE模型
button.attachEvent("onclick", obj.func);
} else{ //兼容DOM标准模型
button.addEventListener("click", obj.func, true);
}
在IE浏览器中,this指向Window和button,而在DOM标准的浏览器中仅指向button。因为,在IE浏览器中,attachEvent()是Window对象的方法,调用该方法时,this会指向Window。
为了解决浏览器兼容性问题,可以调用call或apply方法强制在对象obj身上执行方法func(),避免不同浏览器对this解析不同。
if(window.attachEvent){
button.attachEvent("onclick", function(){ //用闭包封装call方法强制执行func()
obj.func.call(obj);
});
}
else{
button.addEventListener("click", function(){
obj.func.call(obj);
}, true);
}
当再次执行时,func()中包含的this始终指向对象obj。
定时器
【示例5】使用定时器调用函数。
var obj ={};
obj.func = function(){
if(this == obj) console.log("this = obj");
else if(this == window) console.log("this = window");
else if(this.constructor == arguments.callee) console.log("this = 实例对象");
else console.log("this == 其他对象 \n this.constructor = " + this.constructor );
}
setTimeout(obj.func, 100);
在IE中this指向Window和Button对象,具体原因与上面讲解的attachEvent()方法相同。在符合DOM标准的浏览器中,this指向Window对象,而不是Button对象。
因为方法setTimeout()是在全局作用域中被执行的,所以this指向Window对象。解决浏览器兼容性问题,可以使用call或apply方法来实现。
setTimeout(function(){
obj.func.call(obj);
}, 100);