[译] 从 JS 对象的内部原理来理解 this 关键字

栏目: JavaScript · 发布时间: 7年前

内容简介:JavaScript 是一种支持面向对象编程和动态绑定的多范式语言。动态绑定是一个非常强大的功能,它允许在运行时更改 JavaScript 代码的结构,但是这种强大的功能和它的灵活性将可能带来一些难以理解的问题,而这些问题主要集中在动态绑定是在运行时而不是编译时确定要调用的方法。JavaScript 通过让我们来看一段代码:

JavaScript 是一种支持面向对象编程和动态绑定的多范式语言。动态绑定是一个非常强大的功能,它允许在运行时更改 JavaScript 代码的结构,但是这种强大的功能和它的灵活性将可能带来一些难以理解的问题,而这些问题主要集中在 this 关键字的行为上。

动态绑定

动态绑定是在运行时而不是编译时确定要调用的方法。JavaScript 通过 this 和原型链实现了这一点。特别是,方法内部的 this 值是在运行时确定的,更改的规则取决于该方法是如何定义的。

让我们来看一段代码:

const a = {
  a: 'a'
};
const obj = {
  getThis: () => this,
  getThis2 () {
    return this;
  }
};
obj.getThis3 = obj.getThis.bind(obj);
obj.getThis4 = obj.getThis2.bind(obj);
const answers = [
  obj.getThis(),
  obj.getThis.call(a),
  obj.getThis2(),
  obj.getThis2.call(a),
  obj.getThis3(),
  obj.getThis3.call(a),
  obj.getThis4(),
  obj.getThis4.call(a)
];
复制代码

你可以先尝试把你的答案写下来。然后,使用 console.log() 检查你的答案是否正确?

我们从第一个例子开始往下看。 obj.getThis() 返回 undefined ,这是为什么呢?箭头函数的内部永远不会有自己 this 。相反,它们总是委托给词法作用域。在 ES6 模块的根作用域中,有一个 undefined 的 this 。同样的原因, obj.getThis.call(a) 也是 undefined 。对于箭头函数,即使使用 call() 或 bind() 也不能重新绑定 this 的值。它总是把 this 委托给词法作用域。

obj.getThis2() 通过常规的方式调用对 this 进行动态绑定。如果函数之前没有 this 绑定,函数可以使用这样的方式进行动态绑定(除了箭头函数), this 的值将会绑定到调用它的对象(通过 . 运算符或者 [] 语法)。

obj.getThis2.call(a) 有点棘手。 call 方法给函数绑定了给定的 this 值以及可选的参数。换句话说,它从 call() 第一个参数中获得 this 的绑定,因此 obj.getThis2.call(a) 返回对象 a 。

使用 obj.getThis3 = obj.getThis.bind(obj); ,我们试图绑定一个箭头函数的 this 值。我们已经知道了这样不会是预期的那样,所以 obj.getThis3() 和 obj.getThis3.call(a) 都会返回 undefined 。

你可以绑定常规的函数,因此 obj. getThis4() 如预期的那样返回 obj ,因为它的 this 已经与 obj 绑定。 obj.getThis4.call(a) 返回第一次使用 bind 绑定的 obj 而不是 a 。

Curve Ball

同样的问题,但这一次,是类中方法属性的 this 值(在编写本文时处于 Stage 3 ,默认情况下在 Chrome 中可用,请使用 @babel/plugin-proposal-class 属性):

class Obj {
  getThis = () => this
  getThis2 () {
    return this;
  }
}
const obj2 = new Obj();
obj2.getThis3 = obj2.getThis.bind(obj2);
obj2.getThis4 = obj2.getThis2.bind(obj2);
const answers2 = [
  obj2.getThis(),
  obj2.getThis.call(a),
  obj2.getThis2(),
  obj2.getThis2.call(a),
  obj2.getThis3(),
  obj2.getThis3.call(a),
  obj2.getThis4(),
  obj2.getThis4.call(a)
];
复制代码

同样,你可以先写下你的答案。

除了 obj2.getThis2.call(a) 返回一个对象 a 之外,所有的都返回实例对象 obj2 。箭头函数仍然委托给词法作用域中的 this 。不同之处在于,词法对于类是不同的。实际上,类属性赋值被编译成这样:

class Obj {
  constructor() {
    this.getThis = () => this;
  }
...
复制代码

也即是说,箭头函数是在构造函数的上下文中定义的。由于这是一个类,创建实例的唯一方法是使用 new 关键字(不使用 new 将抛出错误)。

new 关键字所做的最重要的事情之一是实例化一个新的对象实例,并将构造函数中的 this 绑定到新的对象实例上。这种特性,加上前面我们所讨论的,就可以解释上面代码的结果了。

总结

你觉得如何?很好地理解 JavaScript 中 this 的行为将为你节省大量调试棘手问题的时间。如果你有遇到任何问题,你可以再尝试一下本文中的例子。运行一下这些例子,然后回来再次调试自己的问题,直到你解决了问题,并去向其他人解释为什么这些函数它们会返回不同的结果。

如果你觉得这比你想象中的要难,不用担心。在这个问题上,我测试了很多的开发者,到目前为止,只有一个开发者获得了满分。

随着类和箭头函数行为特性的增加,使用 call() 、 bind() 或 apply() 方法更加复杂。总结一下:箭头函数总是将 this 委托给词法作用域,而类中的 this 实际上是词法作用域下的构造函数中的 this 值。如果你疑惑 this 是什么,你可以使用调试器来验证一下它的结果。

译者注:《JavaScript Weekly》周刊正在翻译中...

请戳 -> JavaScript-Weekly-zh-CN


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

转型之战

转型之战

吴晓波 / 2015-7-1

互联网时代大潮席卷而来,互联网究竟是“魔法手杖”,还是“效率金箍棒”?传统企业如何正确借助和利用互联网思维帮助自身转变思维、完成企业转型升级?本书分两篇,上篇为传统行业互联网转型极具代表性和借鉴意义的案例,下篇精选吴晓波转型大课的独 家内容,梳理了吴晓波、刘伟、刘润、金霞、刘博、赵峰、张蕴蓝、张荣耀、李嘉聪、曾玉波等各行业10位导师关于互联网思维的精华理念和观点,其中囊括各传统行业互联网转型成功的......一起来看看 《转型之战》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具