为什么从Array继承很难在ES5中实现? [英] Why inheriting from Array is difficult to implement in ES5?

查看:78
本文介绍了为什么从Array继承很难在ES5中实现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在ES5中具有原型继承功能,从Array继承并获得预期的行为看起来并不容易,例如将项目添加到Array时自动更新.length(请参见下面的代码). ES5创建派生函数(MyArray)的对象,然后将其传递给基类型进行初始化,为什么该模型很难在该模型中获得预期的行为?

With prototype inheritance in ES5, it looks not trivial to inherit from Array and get the expected behavior, like updating .length automatically when adding item to Array (see below code). ES5 creates object of the derived function (MyArray) then pass it base type to do initialization, why is this model hard to get the expected behavior in this model?

ES6更改了行为并在基本构造函数中创建对象,然后派生类的构造函数将其初始化(在调用super()之后),想知道为什么这可以解决问题.

ES6 changed the behavior and creating object in base constructor, and constructor of derived class initialize it after that (after call to super()), wondering why this solved the problem.

function MyArray(){}
MyArray.prototype = Object.create(Array.prototype);

var myArr = new MyArray();
myArr[0] = 'first';
console.log(myArr.length); // expect '1', but got '0' in output

推荐答案

关于Array的关键是真正的数组对象是

The key thing about Array is that a real array object is an Array Exotic Object. An exotic object is an object that has behavior that could not be achived using standard JS language features, though in ES6 Proxy allows much more ability for user code to create exotic-like objects.

在对返回诸如Array之类的奇异对象的构造函数进行子类化时,子类化方法需要以这样一种方式来完成,即所创建的对象实际上是一个奇异对象.当你做类似的事情

When subclassing a constructor that returns an exotic object like Array, the subclassing method needs to be done in such a way that the object created is actually an exotic object. When you do something like

function ArraySubclass(){}
ArraySubclass.prototype = Object.create(Array.prototype);

然后

(new ArraySubclass()) instanceof Array

因为原型匹配,但是new ArraySubclass返回的对象只是一个普通对象,其原型链中恰好有Array.prototype.但是你会注意到

because the prototype matches up, but the object returned by new ArraySubclass is just a normal object that happens to have Array.prototype in its prototype chain. But you'll notice that

Array.isArray(new ArraySubclass()); // false

因为该对象不是真正的异国情调.在这种情况下

because the object isn't a real exotic. In this case

new ArraySubclass()

等同于

var obj = Object.create(ArraySubclass.prototype);
ArraySubclass.call(obj);

那么在ES5中如何扩展Array?您需要创建一个外来对象,但还需要确保该外来对象在其原型链中具有您的ArraySubclass.prototype对象.这就是ES5遇到问题的地方,因为在普通ES5中,无法更改现有对象的原型.使用许多引擎添加的__proto__扩展名,您可以使用类似

So in ES5 how do you extend Array? You need to create an exotic object, but you also need to ensure that the exotic object has your ArraySubclass.prototype object in its prototype chain. That is where ES5 hit it issues, because in vanilla ES5, there is no way to change an existing object's prototype. With the __proto__ extension that many engines added you could get the correct Array subclassing behavior with code like

var obj = new Array();
obj.__proto__ = ArraySubclass.prototype;
ArraySubclass.call(obj);

假设您要概括上面的模式,您将如何做?

Say you wanted to generalize the pattern above, how would you do it?

function makeSubclass(baseConstructor, childConstructor){
    var obj = new baseConstructor();
    obj.__proto__ = childConstructor.prototype;
    return obj;
}

function ArraySubclass(){
    var arr = makeSubclass(Array, ArraySubclass); 

    // do initialization stuff and use 'arr' like 'this'

    return arr;
}
ArraySubclass.prototype = Object.create(Array.prototype);

因此可以在ES5 + __proto__中使用,但是随着事情变得越来越复杂怎么办?如果要继承ArraySubclass怎么办?您必须能够将第二个参数makeSubclass更改为第二个.但是,我们该怎么做呢?这里的实际目标是什么?当你做类似的事情

so that works in ES5 + __proto__, but what about as things get more complicated? What if you want to subclass ArraySubclass? You'd have to be able to change the second the second parameter of makeSubclass. But how do we do that? What is the actual goal here? When you do something like

new ArraySubclass()

它是传递给new的值,我们将其作为第二个参数,并且应该传递构造函数的原型. ES5中没有很好的途径来实现这一目标.

it is the value passed to new that we care about as that second parameter, and it is that constructor's prototype that should be getting passed along. There is no nice avenue in ES5 to accomplish this.

这是ES6类受益的地方.

This is where ES6 classes have a benefit.

class ArraySubclass extends Array {
  constructor(){
    super();
  }
}

关键是,当super()运行时,它知道ArraySubclass是子类.当super()调用new Array时,它还会传递一个额外的隐藏参数,该参数表示嘿,当您创建此数组时,将其原型设置为ArraySubclass.prototype.如果继承级别很多,它将传递子级. -最原始的原型,以便返回的奇异对象是真正的奇异对象,同时还要确保它具有正确的原型.

The key thing is that when super() runs, it knows that ArraySubclass is the child class. When super() calls new Array, it also passes along an extra hidden parameter that says "hey, when you create this array, set its prototype to ArraySubclass.prototype. If there are many levels of inheritance, it will pass along the child-most prototype so that the returned exotic object is a real exotic while also making sure it has the correct prototype.

这不仅意味着事物结构正确,而且意味着引擎可以预先创建具有正确原型值的对象.众所周知,由于引擎处理和跟踪对象的方式,在创建对象后更改对象的__proto__值是众所周知的去优化点.

Not only does this mean that things are constructed properly, but it means that engines can create the object with the correct prototype value up front. Mutating an object's __proto__ value after creating is a well-known deoptimization point because of the ways engines process and track objects.

这篇关于为什么从Array继承很难在ES5中实现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆