这是什么 JavaScript对象的内部操作


照片:好奇的莉莉安娜·萨布(CC BY 2.0)


JavaScript是一种多范式语言,支持面向对象的编程和动态链接。 动态链接是一个强大的概念,它使您可以在运行时更改JavaScript代码的结构,但是这些额外的功能和灵活性是通过一些混乱的代价来实现的,其中大多数与JavaScript中的this行为有关。


动态链接


使用动态绑定时,要调用的方法的定义在运行时而不是在编译时发生。 JavaScript this以及原型链进行了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) ]; 

考虑answers数组中的值是什么,并使用console.log()检查您的答案。 猜到了吗


让我们从第一种情况开始,然后按顺序继续。 obj.getThis()返回undefined ,但是为什么呢? 箭头函数从来没有自己的。 取而代之的是,他们总是从词法范围( 近似于词法this )中获取这个 。 对于ES6模块的根,词法区域将具有thisundefined值。 obj.getThis.call(a)相同的原因,也未定义obj.getThis.call(a) 。 对于箭头函数,即使使用.call().bind()也不能覆盖this功能。 this将始终取自词汇领域。


obj.getThis2()在方法调用期间获取绑定。 如果此函数之前没有此绑定,则可以将此绑定到this (因为这不是箭头函数)。 在这种情况下, this是一个obj对象, this对象在调用方法时绑定.[squareBracket]属性访问语法。 ( 注意隐式绑定


obj.getThis2.call(a)有点复杂。 call()方法使用给定的此值和可选参数调用一个函数。 换句话说,该方法从obj.getThis2.call(a) .call()参数获取this绑定,因此obj.getThis2.call(a)返回对象a 。 ( 注意显式绑定


如果是obj.getThis3 = obj.getThis.bind(obj); 我们正在尝试获得一个带有this界限的箭头函数,正如我们已经弄清楚的那样,它将不起作用,因此我们分别为obj.getThis3()obj.getThis3.call(a) undefinedobj.getThis3()


对于常规方法,您可以绑定,因此obj.getThis4()返回obj ,如预期的那样。 他已经在这里绑定了obj.getThis4 = obj.getThis2.bind(obj); ,而obj.getThis4.call(a)考虑到第一个绑定并返回obj而不是a


扭球


我们将解决相同的问题,但是这次我们使用带有公共字段的class来描述对象(撰写本文时, 第3阶段的创新默认情况下在Chrome中可用,并且具有@babel/plugin-offer-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) ]; 

在继续之前,请先考虑一下答案。


准备好了吗


obj2.getThis2.call(a)之外的所有调用obj2.getThis2.call(a)返回该对象的实例。 obj2.getThis2.call(a)返回a 。 Arrow函数仍然可以从其词法环境中获得this信息。 对于类属性,如何从词法环境中定义this有所不同。 在内部,类属性的初始化看起来像这样:


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

换句话说,arrow函数是在构造函数上下文中定义的。 由于这是一个类,因此创建实例的唯一方法是使用new关键字(省略new会导致错误)。 new关键字所做的最重要的事情之一是创建对象的新实例,并将其绑定到构造函数中。 这种行为,加上我们上面提到的其他行为,应该可以解释其余的行为。


结论


你成功了吗? 很好地了解JavaScript中的行为方式将为您节省大量调试复杂问题的时间。 如果您在答案中输入错误,则意味着您需要练习一些。 通过示例进行练习,然后返回并再次检查自己,直到可以运行测试并向其他人解释方法为何返回其返回值。


如果这比您预期的要难,那么您并不孤单。 我问了很多有关此主题的开发人员,我认为到目前为止,只有一个开发人员可以完成此任务。


开始搜索可以使用.apply().apply()重定向的动态方法时,由于添加了类方法和箭头函数,该方法变得更加复杂。 也许您应该再次专注于此。 请记住,箭头函数始终从词法范围中获取this ,而class实际上在词法上受引擎盖下类的构造函数限制。 如果对此有疑问,请记住可以使用调试器检查其值。


请记住,在解决许多JavaScript任务时,您可以不用这样做。 以我的经验,几乎所有东西都可以用纯函数来重新定义,这些函数将所有参数用作显式参数(可以认为是隐式变量)。 通过纯函数描述的逻辑是确定性的,这使其更具可测试性。 同样,使用这种方法也没有副作用,因此,与进行操作时不同,您不太可能破坏任何东西。 每次设置该值时,取决于其值的内容可能会中断。


但是,有时this很有用。 例如,在大量对象之间交换方法。 即使在函数式编程中, this也可用于访问其他对象方法,以实现在现有代数之上构建新代数所需的代数转换。 因此,可以使用this.map()this.constructor.of()获得通用的.flatMap() this.constructor.of()




感谢您提供翻译wksmirnowaVIBaH_dev的帮助

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


All Articles