参考优质博文:嗨,你真的懂this吗?
每次看到this
都会痛苦面具。
首先我们想一下this
的含义,我觉得应该是:本。
英文含义本身是:这个。
在不手动修改的前提下,基本遵循这个意思。
要想知道this
指向何方,我们要先了解四种this
的绑定:
- 默认绑定
- 隐式绑定
- 显示绑定
new
绑定
以下问题的前提是在浏览器环境下:
- 默认绑定
最简单的情况,看一下代码
var name = 'snowt'
function sayName() {
console.log(this.name)
console.log(this)
}
sayName()
// snowt
// window
一般来说,函数独立调用,非严格模式下,this
指向全局对象(浏览器中是window
,node
中是global
),严格模式下,this
指向undefined
如果是node
环境下,会打印出undefined
和global
,因为全局变量并非挂载在global
对象上
- 隐式绑定
情况一般是一个具体的对象调用某个方法,即调用位置存在上下文对象
var name = 'snowt'
function sayName() {
console.log(this.name)
console.log(this)
}
var person = {
name: 'meiko',
say: sayName
}
person.say()
// meiko
// { name: 'meiko', say: [Function: sayName] }
sayName
的this
在调用时指向了person
,没有明显地在代码中表示,但实际上this
改绑了。
但是一般来说,被调用方法的this
只会被绑定给最接近他的一层对象,看下面代码:
var name = 'snowt'
function sayName() {
console.log(this.name)
console.log(this)
}
var person = {
name: 'meiko',
say: sayName
}
var lover = {
name: 'nana',
friend: person
}
lover.friend.say()
// meiko
// { name: 'meiko', say: [Function: sayName] }
这里的函数调用跟lover
没有半点关系,最终输出的还是person
的name
所以我们只关注最后一层。
- 绑定丢失问题
如果一个方法没有直接执行,而是赋值给了另一个变量,那么不管它之前有多少爹爹妈妈把他点出来,都不再好使,不再是隐式绑定。代码:
var name = 'snowt'
function sayName() {
console.log(this.name)
console.log(this)
}
var person = {
name: 'meiko',
say: sayName
}
var call = person.say
call()
// snowt
// window
这里变成了默认绑定,输出了snowt
,跟person
没有关系
另一种情况也是绑定丢失:
var name = 'snowt'
function sayName() {
console.log(this.name)
// console.log(this)
}
var person = {
name: 'meiko',
say: function () {
setTimeout(function () {
console.log(this.name);
});
}
}
var person2 = {
name: 'tom',
say: sayName,
}
person.say(); // snowt
setTimeout(person2.say, 100); // snowt
setTimeout(function () {
person2.say() // tom
}, 200);
第一行输出snowt
是因为setTimeout
回调函数中,在非严格模式下,this
执行默认绑定,指向全局对象window
第二行是因为相当于将person2.say
赋值给了一个新变量,然后执行这个变量,这个this
自然就和person2
没关系了
第三行是因为进行了正常的隐式绑定,this
指向person2
- 显示绑定
显示绑定就是通过call
、apply
、bind
对this
进行改绑
var name = 'snowt'
function sayName() {
console.log(this.name)
}
var person = {
name: 'meiko',
say: sayName
}
var say = person.say;
say.call(person) // meiko
硬绑定啦,this
指向person
但实际上还是会出现绑定丢失的问题:
var name = 'snowt'
function sayName() {
console.log(this.name)
}
var person = {
name: 'meiko',
say: sayName
}
var exer = function (fn) {
fn()
}
exer.call(person, person.say) // snowt
传入的是person.say
但输出的是全局对象的name
,本质上还是那个问题,person.say
相当于赋值给了一个新的变量,然后执行(没有涉及到this
),自然就和person
没关系了
解决方法就是fn
执行的时候也进行显示绑定:
var name = 'snowt'
function sayName() {
console.log(this.name)
}
var person = {
name: 'meiko',
say: sayName
}
var exer = function (fn) {
fn.call(this)
}
exer.call(person, person.say) // meiko
不要让前面的硬绑定白干,fn.call(this)
这里我们拿到的this
指向person
,直接拿来给fn
绑对象即可
new
绑定
new
实际上把构造函数的this
指向了创建的实例
function sayName(name) {
this.name = name;
}
var say = new sayName('tom')
console.log(say.name); // tom
-
绑定优先级
new
绑定 > 显示绑定 > 隐式绑定 > 默认绑定 -
例外
当call
、bind
、apply
传入的第一个参数是null
或undefined
时,会被忽略,然后执行默认绑定
function sayName() {
console.log(this.name);
}
var name = 'jerry'
var obj = {
name: 'meiko'
}
sayName.call(null) // jerry
this
指向了全局对象,执行了默认绑定。
箭头函数比较特殊 下一篇整理好了。
留一个练习:
var number = 5;
var obj = {
number: 3,
fn: (function () {
var number;
this.number *= 2;
number = number * 2;
number = 3;
return function () {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3;
console.log(number);
}
})()
}
var myFun = obj.fn;
myFun.call(null);
obj.fn();
console.log(window.number);
// 10
// 9
// 3
// 27
// 20
涉及到闭包和立即执行函数。