初学JS的朋友可能对this指向问题有些困惑。最近在看《Javascript设计模式与开发实践》一书,里面总结得很到位。梳理下大致可以分为4情况,有兴趣的可以自行去翻书阅读。
作为对象的方法调用
作为普通函数调用
构造器调用
Function.prototype.call和Function.prototype.apply调用
1、作为对象的方法调用
当函数作为对象的方法被调用时,此时this指向该对象。
var obj = {
name: lq,
getName: function () {
console.log (this === obj); // true
console.log (this.name); // lq
}
}
2、作为普通函数调用
当函数不作为对象属性被调用,而是通过普通的函数调用,此时的this总是指向全局对象。在浏览器环境的JavaScript里,就是window对象。
window.name = 'lq';
let getName = function () {
return this.name;
}
console.log ( getName() ); // 输出:lq 此时this指向window对象
又比如:
window.name = 'lq';
let myObj = {
name: 'xiaoming',
getName: function () {
return this.name;
}
};
let getName = myObj.getName;
console.log ( getName() ); // 输出:lq
有时候在我们点击div节点的事件函数内部,如果有一个callback回调函数,当callback被当作普通函数调用时,callback内部的this指向window,但实际上我们想让它指向被点击的div节点本身。代码如下:
<html>
<body>
<div id='div1'>my is div node</div>
</body>
<script>
window.id = 'window';
document.querySelector('#div1').onclick = function () {
console.log (this.id); // 输出:div1
let callback = function () {
console.log (this.id); // 输出:window
}
callback();
}
</script>
</html>
通常的解决方法是用一个变量来保存div节点的引用:
document.querySelector('#div1').onclick = function () {
let _this = this;
let callback = function () {
console.log (_this.id); //输出:div1
}
callback();
}
另外,在ESMAScript5下的strict严格模式下,这种普通函数调用已经被规定为不会指向全局对象,而是undefined。
function fun () {
'use strict'
console.log (this); //输出了:undefined
}
fun();
3、构造器调用
Javascript种没有类的概念,但提供了new运算符,这样我们可以从构造器中创建对象。除了宿主提供的一些内置函数,大部分的Javascript函数可以当作构造器使用。构造器跟普通函数没什么太大区别,唯一的区别是被调用的方式。
当用new运算符调用函数时,该函数会返回一个对象,一般情况下,构造器里的this都指向返回的这个对象。如下:
let myObj = function () {
this.name = 'lq';
};
let obj = new myObj();
console.log (obj.name); //输出:lq
如果new构造器显式地返回一个object类型的对象,那么运算的结果就会最终返回这个对象,this就不是我们之前所期望的this了。如下:
let myObj = function () {
this.name = 'lq';
return {
name: 'xiaoming';
}
}
let obj = new myObj();
console.log(obj.name); // 输出:xiaoming
如果构造器返回的不是object类型的对象,而是一个非对象类型的数据,就没有上面的问题。
let myObj = function () {
this.name = 'lq';
return 'xiaoming';
};
let obj = new myObj();
console.log (obj.name); //输出:lq
4、Function.prototype.call和Function.prototype.apply调用
这两种方式调用可以动态地改变传入函数的this:
let obj1 = {
name: 'lq',
getName: function () {
return this.name;
}
};
let obj2 = {
name: 'xiaoming'
};
console.log (obj1.getName()); //输出:lq
console.log (obj1.getName.call(obj2)); // 输出:xiaoming
console.log (obj1.getName.apply(obj2)); //输出:xiaoming