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

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

内容简介: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 模块的根作用域中,有一个 undefinedthis 。同样的原因, 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


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

查看所有标签

猜你喜欢:

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

Web标准设计

Web标准设计

刘杰(嗷嗷) / 清华大学出版社 / 2009-1 / 75.00元

一扇经常开启的门的铰链不需要润滑油。 一条湍急的河流不会变得污浊。 无论是声音还是想法都不可能在真空中传播。 Web标准如果不用就会腐朽。 这世界真奇妙! 专题页面:http://www.aoao.org.cn/book/web-standards-design/一起来看看 《Web标准设计》 这本书的介绍吧!

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

RGB HEX 互转工具

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具