JavaScript中this指向问题
JavaScript
中的this
总是指向一个对象,而具体指向哪个对象是在运行时基于函数执行环境决定的,而不是函数声明时的环境,大多数情况下我们可以把函数中this的指向分为四种情况
- 函数作为对象的方法调用时的this指向
- 函数作为普通函数调用时的this指向
- 函数作为构造器函数调用时的this指向
- 函数使用call方法或者apply方法调用时的this指向
函数作为对象的方法调用时
当函数作为对象的方法调用时,this指向该对象:
let obj = { a: 1, getA: function() { console.log(this === obj); // 输出:true console.log(this.a); // 输出:1 } } obj.getA(); // getA函数作为obj的方法调用
函数作为普通函数调用时
当函数作为普通函数调用时,this总是指向全局对象(在浏览器的JavaScript中,全局对象是window对象):
window.a = 'globalA'; let obj = { a: 1, getA: function() { return this.a; } } let getA = obj.getA; console.log(getA()); // 输出:global
在这种情形下我们通常会遇到给标签绑定事件函数时,回调函数的this指向问题:
window.id = 'globalId'; document.getElementById('btn').onclick = function() { console.log(this.id); // 输出:btn let callback = function() { console.log(this.id); // 输出: globalId }; callback(); }
当我们点击按钮时触发
onclick
函数,这时onclick
函数是作为document.getElementById('btn')
方法返回的节点对象的方法来调用的,所以此时函数里的this
指向该节点对象;
而callback
函数显然是作为普通函数直接调用的,所以此时callback
函数中this
指向全局对象window
。
有一种简单的解决方案就是手动保存一个当前调用对象的this
window.id = 'globalId'; document.getElementById('btn').onclick = function() { console.log(this.id); // 输出:btn const _this = this; let callback = function() { console.log(_this.id); // 输出: btn }; callback(); }
特殊情况: 在strict
模式下,普通函数调用下的this
是undefined
函数作为构造器调用时
当使用new
关键字来调用函数创建一个对象时,这个函数就是一个构造函数,它总会返回一个对象(这是new
关键字实现原理),而构造函数里面的this
就指向返回的这个对象:
function Class1() { this.name = 'weiwl'; } let obj1 = new Class1(); console.log(obj1.name) // 输出:weiwl function Class2() { this.name = 'weiwl'; return { name: 'wll' }; } let obj2 = new Class2(); console.log(obj2.name); // 输出:wll
函数使用call或者apply调用时
Function.prototype.call
和Function.prototype.apply
方法都可以动态修改函数this
指向,他们的第一个参数就算函数调用时的this
指向:
let obj1 = { name: 'weiwl', getName: function() { return this.name; } } let obj2 = { name: 'wll' } console.log(obj1.getName()); // 输出:weiwl console.log(obj1.getName.call(obj2)); //输出:wll console.log(obj1.getName.apply(obj2)); 输出:wll
call和apply的区别
apply
和call
方法都可以改变函数的this
指向,他们使用的区别就在于接收的参数上:
apply
接收两个参数:第一个参数是调用函数内this
的指向,第二个参数是一个带下标的集合(通常传数组或者arguments);function sum(a, b, c){ console.log([a, b, c]); } sum.apply(null, [1, 2, 3]); // 输出:[1, 2, 3]
call
可以接收多个参数:第一个参数也是调用函数内this
的指向,后续参数会依次作为实参传入函数sum.call(null, 1, 2, 3); // 输出:[1, 2, 3]
apply某种意义上比call高效
由于JavaScript
解释器不会计较形参和实参在数量、类型和顺序上的区别,其参数在内部就是用一个数组来表示的,所以我们如果不关心具体有多少参数被传入函数,这种意义上apply
比call
高效
有问题,烦请不吝赐教