初学JS的朋友可能对this指向问题有些困惑。最近在看《Javascript设计模式与开发实践》一书,里面总结得很到位。梳理下大致可以分为4情况,有兴趣的可以自行去翻书阅读。
作为对象的方法调用
作为普通函数调用
构造器调用
Function.prototype.call和Function.prototype.apply调用
1、作为对象的方法调用
当函数作为对象的方法被调用时,此时this指向该对象。
var obj = {name: lq,getName: function () {console.log (this === obj); // trueconsole.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()); //输出:lqconsole.log (obj1.getName.call(obj2)); // 输出:xiaomingconsole.log (obj1.getName.apply(obj2)); //输出:xiaoming复制代码