这写了什么? JavaScript对象的幕后花絮

JavaScript是一种多范式语言,支持面向对象的编程和动态方法绑定-一种强大的概念,可在程序执行期间更改JavaScript代码的结构。 这给开发人员带来了很多机会,使语言更加灵活,但是您必须付出一切。 在这种情况下,您必须付出代码的可理解性。 this对此价格做出了重大贡献,围绕该行为已收集了许多可能使程序员感到困惑的行为。



动态方法绑定


动态绑定使您可以在程序执行期间而不是编译期间指定执行特定命令时必须调用的方法。 在JavaScript中,此机制是使用this和原型链实现的。 特别是,在运行时确定方法内部的this的特定值,确定该值的规则根据方法的声明方式而有所不同。

让我们玩一个游戏。 我称之为“这写了什么?”。 这是她的首选-ES6模块代码:

 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() answers数组来测试自己。 您是否在每种情况下都正确地“解密”了其价值?

我们将从第一个示例开始分析这个问题。 obj.getThis()构造返回undefined 。 怎么了 此箭头功能无法绑定。 此类函数从其周围的词汇范围中使用this函数。 该方法在ES6模块中调用,在其词法范围内thisundefined 。 出于相同的原因, undefined返回对obj.getThis.call(a)的调用。 即使使用.call().bind()也无法重新分配使用箭头功能时的this值。 此值将始终与此类功能所在的词法范围相对应。

obj.getThis2()命令演示了使用常规对象方法时如何使用它。 如果this未与类似方法绑定在一起,并且前提是该方法不是箭头函数,即它支持this绑定,则将this关键字绑定到使用以下方法访问对象属性的语法的对象:点或使用方括号。

obj.getThis2.call(a)构造已经有点棘手了。 call()方法允许您使用this的给定值调用函数,该值表示为可选参数。 换句话说,在这种情况下, this是从.call()参数获取的,因此,对obj.getThis2.call(a)的调用将返回对象a

使用命令obj.getThis3 = obj.getThis.bind(obj); 我们正在尝试绑定this方法,这是一个箭头函数。 正如我们已经发现的那样,这是无法完成的。 结果,对obj.getThis3()obj.getThis3.call(a)调用返回undefined

可以将普通函数的方法附加this ,因此obj.getThis4()按预期返回obj 。 调用obj.getThis4.call(a)返回obj ,而不是您可能期望a 。 事实是,在调用此命令之前,我们已经使用obj.getThis4 = obj.getThis2.bind(obj);命令将obj.getThis4 = obj.getThis2.bind(obj);绑定了obj.getThis4 = obj.getThis2.bind(obj); 。 结果,在执行obj.getThis4.call(a) ,将考虑到第一次绑定后方法所处的状态。

在课堂上使用它


这是我们游戏的第二个版本-相同的任务,但现在基于类。 在这里,我们使用该语法声明公共类字段(目前,此语法的建议处于批准的第三阶段,默认情况下在Chrome中可用,您可以将其与@babel/plugin-proposal-class-properties )。

 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) ]; 

在进一步阅读之前,请考虑一下代码,并写下您对answers2数组的answers2

完成了吗

在这里,除obj2.getThis2.call(a)之外的所有方法调用都将返回对对象实例的引用。 相同的调用将返回对象a 。 箭头函数仍然从词汇范围中获取this信息。 此示例与上一个示例之间的区别在于,引用范围不同。

也就是说,这里我们使用类属性来确定代码的行为。

事实是,在准备执行代码的过程中,将值写入以下类的属性中:

 class Obj { constructor() {   this.getThis = () => this; } ... 

换句话说,事实证明arrow函数是在构造函数的上下文内声明的。 由于我们正在使用一个类,因此实例化它的唯一方法是使用new关键字(如果您忘记了该关键字,则会显示一条错误消息)。

new关键字解决的最重要的任务是创建对象的新实例并将其绑定到构造函数。 考虑到我们在上一节中已经讨论的内容,此功能应该可以帮助您了解正在发生的事情。

总结


您是否已完成本文概述的任务? 很好地了解this关键字在JavaScript中的行为方式,可以在调试时查找不确定的错误的非显而易见原因时,为您节省大量时间。 如果您对某些问题的回答不正确,则对您的练习很有帮助。

试用示例代码,然后重试,依此类推,直到可以正确回答所有问题为止。 在弄清楚自己的想法之后,找一个愿意听你说话的人,告诉他为什么任务中的方法返回的正是他们返回的结果。

如果这一切对您来说似乎比您预期的要复杂,那么请知道您并不孤单。 我测试了很多开发人员的功能知识,并且我认为只有一个开发人员在所有答案中都是绝对准确的。

语言的子系统从一开始就看起来像是对可能受.apply().apply()影响的方法的动态搜索,在出现箭头函数和类之后,它看起来就变得更加复杂了。

显然,在使用this注意类和箭头函数的主要功能将很有用。 请记住,箭头函数始终在其词法范围内使用this ,并且实际上,类中的this绑定到该类的构造函数。 而且,如果您觉得自己不知道确切要指向什么,请使用调试器检查您对此的假设。

另外,请记住,您可以在JavaScript中做很多事情而无需在代码中使用this 。 经验告诉我,几乎任何JS代码都可以以纯函数的形式重写,这些函数可以接受它们使用的所有参数,形式是显式指定的参数列表( this可以解释为具有可变状态的隐式指定参数)。 纯函数中包含的逻辑是确定性的,从而提高了它们的可测试性。 这样的功能没有副作用,这意味着与使用它们不同,与操作它们不同,您不太可能“破坏”其中的任何内容。 每当您更改this ,您都将面临潜在的问题,即依赖this某些操作可能会停止正常运行。

尽管有上述内容,但应注意, this是一个有用的概念。 例如,可以应用它来组织多个对象共享某种方法。 即使在函数式编程中, this对于从一个对象的一个​​方法调用其他方法也很方便,这使您可以基于现有结构创建新的东西。



Source: https://habr.com/ru/post/zh-CN455527/


All Articles