Спецификация ECMAScript позволяет массиву быть "суперсклассируемым"?
Я ищу любые указания, будет ли "суперклассификация" встроенного типа работать в соответствии со спецификацией. То есть, учитывая любую гипотетическую согласованную реализацию ECMAScript, делает ли "суперклассирование" встроенный разрыв среды выполнения, влияя на алгоритм создания конструктора класса?
"Суперклассируемый", термин, который я использую, относится к классу, объекты которого возвращаются путем его построения или называются его функцией, если это применимо, будут созданы с одинаковыми внутренними слотами (кроме [[Prototype]]), независимо от того, из того, что является его прямым суперклассом, до тех пор, пока исходный [[Прототип]] конструктора класса и прототипа класса все еще находится в каждой соответствующей цепочке наследования после переназначения их. Следовательно, для того, чтобы быть "суперклассифицированным", класс не должен называть super()
во время создания.
Когда "суперклассирование" Array
, я ожидаю, что он будет выглядеть примерно так:
// clearly this would break Array if the specification allowed an implementation
// to invoke super() internally in the Array constructor
class Enumerable {
constructor (iterator = function * () {}) {
this[Symbol.iterator] = iterator
}
asEnumerable() {
return new Enumerable(this[Symbol.iterator].bind(this))
}
}
function setSuperclassOf (Class, Superclass) {
/* These conditions must be satisfied in order to
* superclass Class with Superclass
*/
if (
!(Superclass.prototype instanceof Object.getPrototypeOf(Class.prototype).constructor) ||
!(Superclass instanceof Object.getPrototypeOf(Class).constructor) ||
(Superclass.prototype instanceof Class)
) {
throw new TypeError('${Class.name} cannot have their superclass set to ${Superclass.name}')
}
// Now we can superclass Class with Superclass
Object.setPrototypeOf(Class.prototype, Superclass.prototype)
Object.setPrototypeOf(Class, Superclass)
}
setSuperclassOf(Array, Enumerable)
const array = new Array(...'abc')
// Checking that Array is not broken by Enumerable
console.log(array[Symbol.iterator] === Array.prototype[Symbol.iterator])
// Checking that Enumerable works as expected
const enumerable = array.asEnumerable()
console.log(array instanceof Enumerable)
console.log(!(enumerable instanceof Array))
for (const letter of enumerable) {
console.log(letter)
}
Ответы
Ответ 1
Вызов функции setSuperclassOf
для любого встроенного класса ECMAScript не повлияет на поведение конструктора.
Конструктор HypotheticalArray
не должен - не должен - вызывает super()
. В спецификации вы не должны просто смотреть раздел "Конструктор массива", который дает краткий обзор, а также в подразделах §22.1.1.1 "Массив"(), §22.1.1.2 "Массив" (len) и §22.1.1.3 "Массив"...items), которые дают подробные алгоритмы того, что происходит, когда вы вызываете Array
(как функцию или конструктор). Они ищут прототип newTarget (который будет подклассифицироваться, как обычно - с ES6), но они не ищут прототипа самой функции Array
. Вместо этого все они непосредственно отправляют в алгоритм ArrayCreate, который просто создает объект, устанавливает его прототип и устанавливает семантику экзотических свойств.
Он похож на String
(который отправляется алгоритму StringCreate при вызове как конструктор), абстрактный конструктор TypedArray
(который просто бросает и явно указывает, что "конструкторы TypedArray не выполняют супервызов к нему"), конкретные конструкторы TypedArray (которые отправляют алгоритмы AllocateTypedArray и IntegerIndexedObjectCreate), а также конструкторы Map
и Set
(которые отправляются в алгоритмы OrdinaryCreateFromConstructor и ObjectCreate). И afaik он одинаковый для всех других встроенных конструкторов, хотя я не проверял каждый из них по отдельности, их слишком много, как у ES8.
Я понимаю, что, поскольку сам Array.prototype
является экзотическим объектом Array, для выполнения внутри класса super()
внутри конструктора Array
не требуется выполнять совместимую реализацию, чтобы правильно инициализировать экземпляр как массив экзотических
Нет, это не имеет никакого отношения к этому. Объект не становится экзотическим, потому что он наследуется от экзотического объекта. Объект экзотический, потому что он был специально создан как таковой. Значение Array.prototype
может быть действительно чем угодно, оно не имеет отношения к созданию экземпляров массива - кроме того, оно будет использоваться в качестве прототипа при вызове new Array
(в отличие от new ArraySubclass
).
Что касается Object.setPrototypeOf(Array.prototype, …)
, обратите внимание, что Array.prototype
не является даже неизменным прототипом экзотического объекта, такого как Object.prototype
, так что да, вы можете это сделать.
Ответ 2
Основываясь на гарантии, изложенной в п. 9.3.3 CreateBuiltinFunction (шаги, internalSlotsList [, realm [, prototype]]) и шаги в §22.1.1 Array Constructor, невозможно вызвать вызов Array(…)
или new Array(…)
вызовет конструктор Object или конструктор Array, разрешенный суперкласс во время вызова, и, следовательно, "суперклассирование" Array гарантированно будет корректно вести себя в любой совместимой реализации ECMAScript 2018.
Из-за открытия п. 9.3.3 я подозреваю, что тот же вывод будет сделан для оставшихся классов в текущей спецификации, хотя для определения того, является ли это верным, и гарантировано на ECMAScript 2015, требуется гораздо больше исследований.
Это не полный ответ, и поэтому я не буду его принимать.Награда по-прежнему будет вознаграждена за полный ответ, независимо от того, предоставляется ли она до того, как мой вопрос будет иметь право на получение вознаграждения.