Теперь у нас есть ECMAScript 2 015 (ECMA-262 6-е издание; ES6), мы имеем proxy objects, и они позволяют реализовать Array
поведение в самом языке, что-то вдоль линий:
function FakeArray() {
const target = {};
Object.defineProperties(target, {
"length": {
value: 0,
writable: true
},
[Symbol.iterator]: {
// http://www.ecma-international.org/ecma-262/6.0/#[email protected]@iterator
value:() => {
let index = 0;
return {
next:() => ({
done: index === target.length,
value: target[index++]
})
};
}
}
});
const isArrayIndex = function(p) {
/* an array index is a property such that
ToString(ToUint32(p)) === p and ToUint(p) !== 2^32 - 1 */
const uint = p >>> 0;
const s = uint + "";
return p === s && uint !== 0xffffffff;
};
const p = new Proxy(target, {
set: function(target, property, value, receiver) {
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-exotic-objects-defineownproperty-p-desc
if (property === "length") {
// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-arraysetlength
const newLen = value >>> 0;
const numberLen = +value;
if (newLen !== numberLen) {
throw RangeError();
}
const oldLen = target.length;
if (newLen >= oldLen) {
target.length = newLen;
return true;
} else {
// this case gets more complex, so it's left as an exercise to the reader
return false; // should be changed when implemented!
}
} else if (isArrayIndex(property)) {
const oldLenDesc = Object.getOwnPropertyDescriptor(target, "length");
const oldLen = oldLenDesc.value;
const index = property >>> 0;
if (index > oldLen && oldLenDesc.writable === false) {
return false;
}
target[property] = value;
if (index > oldLen) {
target.length = index + 1;
}
return true;
} else {
target.property = value;
return true;
}
}
});
return p;
}
Я не могу гарантировать, что это на самом деле абсолютно правильно, и это не делает обрабатывать случай, когда вы изменяете длину, чтобы быть меньше ее предыдущего значения (поведение немного сложное, чтобы получить право, грубо оно удаляет свойства, чтобы сохранить инвариант свойства length
), но он дает грубую схему того, как вы можете реализовать Это. Он также не имитирует поведение [[Call]] и [[Construct]] на Array
, что является еще одной вещью, которую вы не могли сделать до ES6 - не было возможности иметь расходящееся поведение между этими двумя в ES-коде , хотя ничто из этого не сложно.
Это реализует свойство length
таким же образом, спецификация определяет его как работа: он перехватывает задания к свойствам на объекте, и изменяет length
свойство, если оно является «индекс массива».
В отличие от того, что можно делать с ES5 и геттерами, это позволяет получить length
в постоянное время (очевидно, это все еще зависит от того, что доступ к основным свойствам в VM является постоянным временем), и единственный случай, когда он обеспечивает Непостоянная производительность по времени - это не реализованный случай, когда свойства newLen - oldLen
удалены (и удаление в большинстве виртуальных машин происходит медленно).
Это скорее теоретический вопрос о границах javascript, чем что-либо практическое = P. Я думаю, что мой ответ заключается в том, что вы не можете этого сделать. – Claudiu 2008-12-14 01:36:39