Ответ 1
Если вы хотите сохранить список как Array
, вам придется изменить его [[prototype]]
, чтобы он выглядел как итеративная коллекция:
Array.prototype.next = function() {
return this[++this.current];
};
Array.prototype.prev = function() {
return this[--this.current];
};
Array.prototype.current = 0;
Теперь каждый Array
будет иметь методы prev
и next
и свойство current
, которое указывает на "текущие" элементы. Оговорка: свойство current
может быть изменено, что приводит к непредсказуемым результатам.
Post scriptum: я не рекомендую делать prev
и next
return false
, когда индекс выходит за пределы диапазона. Если вы действительно этого хотите, вы можете изменить методы на что-то вроде:
Array.prototype.next = function() {
if (!((this.current + 1) in this)) return false;
return this[++this.current];
};
ОБНОВЛЕНИЕ в середине 2016 года
Я обновляю этот ответ, потому что кажется, что он все еще получает мнения и голоса. Я должен был уточнить, что данный ответ является доказательством концепции и в целом продление прототипа родных классов является плохой практикой, и его следует избегать в производственных проектах.
В частности, это не так много, потому что это будет беспорядок с циклами for...in
, которые всегда следует избегать для массивов, и это определенно плохая практика для итерации через их элементы, а также потому, что с IE9 мы можем надежно сделать это вместо этого
Object.defineProperty(Array.prototype, "next", {
value: function() { return this[++this.current]; },
enumerable: false
});
Основная проблема заключается в том, что расширение родных классов не является будущим, т.е. может случиться так, что ECMA представит метод next
для массивов, который, вероятно, будет несовместим с вашей реализацией. Это уже произошло даже с очень распространенными структурами JS - последний случай был расширение массива MooTools contains
, которое привело ECMA, чтобы изменить имя на includes
(плохой ход, IMO, поскольку у нас уже есть contains
в DOMTokenList
, такие как Element.classList
).
Если не сказать, что вы не должны распространять собственные прототипы, но вы должны знать, что делаете. Первый совет, который я могу вам дать, - это выбрать имена, которые не будут сталкиваться с будущими стандартными расширениями, например. myCompanyNext
вместо next
. Это будет стоить вам некоторой элегантности кода, но заставит вас спать.
Еще лучше, в этом случае вы можете эффективно расширить класс Array
:
function MyTraversableArray() {
if (typeof arguments[0] === "number")
this.length = arguments[0];
else this.push.apply(this, arguments);
this.current = 0;
}
MyTraversableArray.prototype = [];
MyTraversableArray.prototype.constructor = MyTraversableArray;
MyTraversableArray.prototype.next = function() {
return this[++this.current];
};
MyTraversableArray.prototype.prev = function() {
return this[--this.current];
};
В ES6, кроме того, проще расширить родные классы:
class MyTraversableArray extends Array {
next() {
return this[++this.current];
}
}
Увы, у транспилеров трудное время с расширениями родного класса, и Babel удалил его поддержку. Но это потому, что они не могут точно воспроизвести некоторые виды поведения, которые не имеют никакого влияния в нашем случае, поэтому вы можете придерживаться вышеуказанного старого кода ES3.