JavaScript中的this常见误区
在JavaScript中,this是函数中的一个特殊对象,它表示的是函数执行环境所在的环境对象。函数执行的时候会生成一个执行环境,这个执行环境又会包含在某个对象中,如:直接在脚本中声明一个函数再执行,那么这个环境对象就是window;在一个对象中执行函数,这个环境对象就是当前执行函数所在的对象。下面列出几种常见的陷阱:
函数传递中的this
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
};
(object.getName = object.getName)(); //"The Window"
打印出来的结果是”The Window”,也许有点奇怪,但是按照上面的思路来分析,其实很简单,object.getName引用传递给本身,然后匿名函数在window环境中执行,上面的代码可以转换为:
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return this.name;
}
};
object.getName = object.getName;
var temp = object.getName;
temp();//输出The Window
这样转换之后就很清楚的看到,最后匿名函数的执行环境其实包含在window对象中的,所以输出的是The Window。
构造函数中的this
var name = 'global';
function Person() {
var name = 'local';
console.log(this.name);
}
Person();//输出global
new Person();//输出undefined
如代码所示,第一次执行表示正常的函数执行,输出global没问题,第二个就纳闷了,怎么是undefined?new Person()是构造函数方式创建对象,有点特殊性,可以参考前面的《理解JavaScript原型模式》,可以这样理解,当执行new Person时会分配一个Person实例的内存空间,加上括号就执行Person构造函数,这时Person构造函数所属的对象就是新建的实例对象,而这个实例对象里面什么都没有,所以输出undefined了,如果想要输出属性值,就得给实例赋值,如:this.name=’local’赋值之后,创建的实例就可以输出local了。
闭包中的this
同样是上面的例子,直接把getName函数的返回值改成一个匿名函数:
var name = "The Window";
var object = {
name : "My Object",
getName: function(){
return function(innerName) {
console.log(this.name);//输出The Window
};
}
};
object.getName()('inner');
如果不小心,单从object去分析就很容易出错,object.getName()(‘inner’)这段代码其实也是window对象中的一个匿名函数的执行,等价于:
var temp = object.getName();//获得返回的匿名函数
temp('inner');
这样可以看到temp就是一个闭包,执行环境是在window对象中执行,所以this就是当前window全局变量了。
有时候在命名自己的私有空间时,也常常会用到闭包,下面再来一个闭包场景下的this:
var name='global';
var Global = (function() {
var name = 'local';
var getName = function() {
console.log(this.name);
}
return {
name : 'obj',
objGetName : getName
}
})();
Global.objGetName();//输出obj
上面代码可以看出有3个name,看上去输出哪个name有点晕乎,前面说了只要记住函数执行环境所在的对象,不管嵌套多少个函数,其实分析起来都很简单的。这里我们看到会最先执行匿名函数,然后把返回的一个匿名对象赋值给Global对象,在返回对象中getName赋值给了objGetName,执行objGetName函数所在对象是Global,里面的this对象其实就变成了返回的匿名对象了,所以this.name表示的就是匿名对象中的name了。
HTML事件绑定中的this
HTML元素其实都是一个对象,里面的各种事件就是函数,事件里面执行函数就相当于一个闭包,拿个例子来说:
<input type="text" id="nameInput" onclick="this.value = 2"/>
var nameInput = document.getElementById('nameInput');
console.log(nameInput.onclick);//onclick属性是一个函数
执行onclick事件时,执行函数环境就包含在input标签对象中了,所以事件中的this表示的就是input对象了。
如果onclick事件中嵌套一个函数,那这个函数中的this还是input对象么?
<input type="text" id="nameInput" onclick="doAction()"/><br>
function doAction() {
var nameInput = document.getElementById('nameInput');
console.log(nameInput.onclick);//onclick属性是一个函数
console.log(this);//输出window对象
this.value = Math.random();
}
意料之中输出的是window对象,最简单粗暴的理解方式就是最后执行doAction函数所在对象是在window对象中,所以不用管onclick到底所属对象。可以换一种写法:
function doAction() {
console.log(this);//输出window对象
}
function onclick() {
doAction();
}
var nameInput = document.getElementById('nameInput');
nameInput.onclick();
这样就可以更直观的看到其实就是一般的函数之间的调用了。如果想用onclick所在的html元素对象,就执行函数里面添加一个this参数就可以了,如:
<input type="text" id="nameInput" onclick="doAction(this)"/><br>