回顾一下,两年前刚刚进入这行的时候,也曾被 this 这个关键字折腾过,看了不少资料,基本上都提及到了 4 种规则:
默认绑定
默认绑定的 this 指向全局对象,严格模式下 this 指向 undefined。
function foo() {
// window
console.log(this);
}
foo();
/*-------------------------------*/
'use strict';
function foo() {
// undefined
console.log(this);
}
foo();
this 在浏览器下指向 window ,node 下指向 global
隐式绑定
函数在的调用位置是否有上下文对象,如果有,this 绑定到这个对象上。
const name = 'boy';
const obj = {
name: '94yk',
learn() {
console.log(this.name + ' is learning JS');
}
};
obj.learn();
// 94yk is learning JS
/* -------------------------- this'丢失' -------------------------- */
let learn = obj.learn;
learn();
// boy is learning JS
显式绑定
其实我更愿意称其为强制绑定哈哈哈。三个流氓 apply、call、bind 强行霸占 this 美女!
这三个函数都会劫持 this,绑定到指定的对象上。
不同的地方是:call 和 apply 会立即执行,而 bind 不会立即执行,返回 this 修改后的函数
。
如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值 在调用时会被忽略,实际应用的是默认绑定规则,也就是全局对象。
new 绑定
将 this 绑定到构造函数创建的实例上。
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype); // 创建空对象,把它的__proto_指向构造函数的ProtoType
let res = fn.apply(obj, args); // 改变函数this指向为新建对象然后执行构造函数
return res instanceof Object ? res : obj; // 如果执行结果是对象就返回它,没有就返回新建的对象
}
箭头函数
首先明确一点,箭头函数中没有 this!没有 this!没有 this!
你所见到的箭头函数中的 this,其实是它所在上下文外层第一个不是箭头函数的函数 this 的指向
。
可以看出与普通函数的显著区别: 1 不能实例化; 2this 绑定是在声明时就确定了的。
箭头函数还有一个特点就是一旦确定了 this 不会被任何代码所改变
,就算是 call 也不行!
function foo() {
return () => {
console.log(this.a);
};
}
foo.a = 10;
let student = {
a: 100
};
let bar = foo.call(foo);
bar(); // 10
let baz = foo.call(student);
baz(); // 10 惊不惊喜,意不意外!因为在上一次call时,this已经确定了它的对象是谁!不能三心二意呀!
总结
要知道 this 存在的原因就是为了方便程序员的书写,相比 window.xxx/someObj.xxx,this.xxx 显然来的更加简单便捷,可以看做它只不过是你调用的方法所在上下文的一个代理而已。简单一句话:谁调用的这个方法,this就指向谁
。
现在可以分析下隐式绑定里 this "丢失"的问题:
// 这一步其实就是创建了个learn并将其指向obj对象下的learn方法,这个时候并不知道会是谁去调用这个方法。
let learn = obj.learn;
// learn() ==> 其实就是 window.learn(),自然里面的this代理的就是window, window下的name为boy
learn();
解决办法也很简单,根据箭头函数的特点可以轻易应对:
const name = 'boy';
const obj = {
name: '94yk',
learn() {
() => {
console.log(this.name + ' is learning JS');
};
}
};
let learn = obj.learn();
learn();