this全面解析
2019-12-06 本文已影响0人
唐井儿_
1 定义
谁调用的函数,该函数的this就指向谁。
2 四种绑定规则
2.1 默认绑定,this指向全局对象,严格模式下绑定到undefined
var a = 1;
function foo() {
console.log(this.a);
}
foo(); // 1
2.2 隐式绑定,this绑定到调用函数的上下文对象上
- 在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接绑定到这个对象上,比如obj.foo属性:
function foo() {
console.log(this.a);
}
var a = 1;
var obj = {
a: 2,
foo: foo // 核心!
};
obj.foo(); // 2
- 隐式丢失问题:被对象所间接引用的函数,再次赋值后调用,会丢失this在该对象上的绑定
// 情况一
function foo() {
console.log(this.a);
}
var a = 'I`m in global scope!';
var bar = {
a: 'I`m in bar`s scope!',
foo:foo
};
var copyFoo = bar.foo; // 核心!重新赋值到另一变量上。copyFoo是bar.foo的一个引用,实际上,引用的是foo本身
copyFoo(); // 'I`m in global scope!'
// 情况二
function foo() {
console.log(this.a);
}
function doFoo(fn) {
fn();
}
var a = 'I`m in global scope!';
var bar = {
a: 'I`m in bar`s scope!',
foo:foo
};
// 函数传递是一种隐式赋值,所以结果和上例一样
doFoo(bar.foo); // 'I`m in global scope!'
setTimeout(bar.foo, 100); // 'I`m in global scope!'
2.3 显式绑定,使用apply/call方法,显式绑定到指定对象上
function foo() {
console.log(this.a);
}
var bar = {
a: 'I`m in bar`s scope!'
};
foo.call(bar); // 'I`m in bar`s scope!'
- 使用“硬绑定”(bind)方法解决2.2中“隐式丢失问题”
function foo() {
console.log(this.a);
}
function doFoo(fn, context) {
return function () { // 核心!
fn.apply(context, arguments);
}
}
var a = 'I`m in global scope!';
var bar = {
a: 'I`m in bar`s scope!',
foo:foo
};
doFoo(bar.foo, bar)(); // 'I`m in bar`s scope!'
setTimeout(doFoo(bar.foo, bar), 100); // 'I`m in bar`s scope!'
// 此模式很有用,于是es5提供了内置的方法Function.prototype.bind,用法如下:
setTimeout(bar.foo.bind(bar), 100); // 'I`m in bar`s scope!'
- apply/call与bind的区别:使用前者时,会直接执行函数;而使用bind只是返回一个函数声明,要执行函数的话,还需'()'调用一下
- bind实现方式、功能:1) 实现方式是把前者包裹起来返回一个函数声明;2) 功能之一就是可以把除了第一个参数(第一个参数用于绑定this)之外的其他参数都传给下层函数(“柯里化”的一种)
2.4 new绑定,this绑定新创建的对象
function Foo(a) {
this.a = a;
}
var fooInstantiation = new Foo('1');
fooInstantiation .a; // '1'
3 优先级
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
4 被忽略的this
有些调用需要使用默认绑定规则,如果想“更安全”地忽略this绑定,可以用Object.create(null)
Math.max.apply(Object.create(null), [1, 3]);
5 特殊的箭头函数
箭头函数并不会使用上边的四条规则,而是根据词法作用域来决定this。也就是说箭头函数会继承外层函数调用的this绑定,就和es6之前的self = this机制一样
function foo() {
setTimeout(() => {
console.log(this.a);
}, 0);
}
var a = 1;
var obj = {a: 2};
foo.call(obj); // 2
function foo2() {
var self = this;
setTimeout(function() {
console.log(self.a);
}, 0);
}
foo2.call(obj); // 2
- 虽然self = this和箭头函数看起来都可以取代bind(...),但本质上,它们想替代的是this机制
- 良好的策略是:1) 只使用词法作用域完全抛弃错误this风格代码;2) 完全采用this风格,在必要时使用bind(...),尽量避免使用self = this和箭头函数;两种方式不要混用
参考《你不知道的JavaScript》上卷 / Simpson, K.