this全面解析

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.

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 我们现在知道了每个函数的this是在运行的时候进行绑定的,完全取决于函数的调用位置,也就是该函数的调用方法。 关于...
    FeRookie阅读 1,137评论 0 5
  • this到底是什么?this是在运行的进行绑定的,并不是编写时绑定的。他的上下文取决于函数调用的各种环境。this...
    小乌龟变王八阅读 391评论 0 1
  • 调用位置 在理解 this 的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(而不是声明的...
    如梦初醒Tel阅读 502评论 0 3
  • 关于 this this 关键字是 JavaScript 中最复杂的机制之一。它是一个很特别的关键字,被自动定义在...
    游学者灬墨槿阅读 669评论 1 2
  • 调用位置 通常而言,调用位置就是函数被调用的位置 注意调用栈 绑定规则 默认绑定独立函数调用是最常用的函数调用类型...
    nymlc阅读 231评论 0 0

友情链接更多精彩内容