假设我们有一个整数列表:
var fibonacci = [1,1,2,3,5,8,13,21];
我希望能够以以下方式获取下一个和前一个元素(只是移动元素指针,而不修改数组)(例如,可以不使用prototype来重新定义Array接口,但为什么不可以):
fibonacci.prev(); // returns false
fibonacci.next(); // returns 1
fibonacci.next(); // returns 1
fibonacci.next(); // returns 2
fibonacci.next(); // returns 3
fibonacci.next(); // returns 5
fibonacci.next(); // returns 8
fibonacci.prev(); // returns 5
fibonacci.next(); // returns 8
fibonacci.next(); // returns 13
fibonacci.next(); // returns false
发布于 2012-09-12 22:39:27
如果你想保持列表作为一个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
属性,从而导致不可预知的结果。
后脚本:我不建议在索引超出范围时让prev
和next
返回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
array extension,它导致了ECMA to change the name to includes
(糟糕的举动,因为我们已经在像Element.classList
这样的DOMTokenList
对象中使用了contains
)。
话虽如此,这并不是说你不能扩展原生原型,但你应该意识到你在做什么。我能给你的第一个建议是选择不会与未来的标准扩展冲突的名称,例如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];
}
}
遗憾的是,转译器有a hard time with native class extensions,而Babel删除了它的支持。但这是因为它们不能精确地复制一些在我们的情况下没有影响的行为,所以你可以坚持使用上面的旧ES3代码。
发布于 2012-09-12 23:16:59
我通常建议不要向Array.prototype
添加东西,因为外面有很多非常糟糕的JavaScript。例如,如果您设置了Array.protoype.next = function () {}
,而某人具有以下代码,那么就会出现问题:
var total = 0, i, myArr = [0,1,2];
for(i in myArr) {
total += myArr[i];
}
total; //is probably "3next"
这种对for-in
循环的不良使用是令人不安的。因此,您添加到Array
的原型中是自找麻烦。然而,构建一个包装器来完成您想要做的事情是相当容易的:
var iterifyArr = function (arr) {
var cur = 0;
arr.next = (function () { return (++cur >= this.length) ? false : this[cur]; });
arr.prev = (function () { return (--cur < 0) ? false : this[cur]; });
return arr;
};
var fibonacci = [1, 1, 2, 3, 5, 8, 13];
iterifyArr(fibonacci);
fibonacci.prev(); // returns false
fibonacci.next(); // returns 1
fibonacci.next(); // returns 1
fibonacci.next(); // returns 2
fibonacci.next(); // returns 3
fibonacci.next(); // returns 5
fibonacci.next(); // returns 8
fibonacci.prev(); // returns 5
fibonacci.next(); // returns 8
fibonacci.next(); // returns 13
fibonacci.next(); // returns false
以下是一些注意事项:
首先,如果超过end,您可能希望让它返回undefined
而不是false
。其次,因为此方法使用闭包隐藏了cur
,所以您无法在数组上访问它。因此,您可能希望使用cur()
方法来获取当前值:
//Inside the iterifyArr function:
//...
arr.cur = (function () { return this[cur]; });
//...
最后,您的需求并不清楚“指针”在多大程度上保持在结束之后。以下面的代码为例(假设fibonacci
设置如上):
fibonacci.prev(); //false
fibonacci.prev(); //false
fibonacci.next(); //Should this be false or 1?
在我的代码中,它应该是false
,但您可能希望它是1
,在这种情况下,您必须对我的代码进行几个简单的更改。
哦,因为函数返回arr
,所以您可以在定义数组的同一行上“迭代”它,如下所示:
var fibonacci = iterifyArr([1, 1, 2, 3, 5, 8, 13]);
这可能会让你的事情变得更干净。您还可以通过在数组上重新调用iterifyArr
来重置迭代器,或者可以编写一个方法来轻松地重置迭代器(只需将cur
设置为0)。
发布于 2018-01-22 17:23:40
这方面的next方面现在被构建到数组中,因为从ES2015开始,数组是可迭代的,这意味着您可以为它们获得一个具有next
方法的迭代器(但请继续阅读"prev“部分):
const a = [1, 2, 3, 4, 5];
const iter = a[Symbol.iterator]();
let result;
while (!(result = iter.next()).done) {
console.log(result.value);
}
迭代器只能前进,不能双向前进。当然,您通常不会显式地使用迭代器,而是将其用作某些迭代构造的一部分,比如for-of
const a = [1, 2, 3, 4, 5];
for (const value of a) {
console.log(value);
}
您可以很容易地为自己提供一个双向迭代器:
创建一个接受数组并返回迭代器的独立函数,或者创建一个接受数组并返回迭代器的独立函数,或者创建一个子类Array
并覆盖子类中的迭代器的
Array
迭代器(只需确保它的工作方式与默认的完全一样!)
G215>
下面是一个带有子类的示例:
class MyArray extends Array {
// Define the iterator function for this class
[Symbol.iterator]() {
// `index` points at the next value we'll return
let index = 0;
// Return the iterator
return {
// `next` returns the next
next: () => {
const done = index >= this.length;
const value = done ? undefined : this[index++];
return { value, done };
},
// `prev` returns the previous
prev: () => {
const done = index == 0;
const value = done ? undefined : this[--index];
return { value, done };
}
};
}
}
// Demonstrate usage:
const a = new MyArray("a", "b");
const i = a[Symbol.iterator]();
console.log("next", JSON.stringify(i.next()));
console.log("next", JSON.stringify(i.next()));
console.log("next", JSON.stringify(i.next()));
console.log("prev", JSON.stringify(i.prev()));
console.log("prev", JSON.stringify(i.prev()));
console.log("prev", JSON.stringify(i.prev()));
console.log("next", JSON.stringify(i.next()));
.as-console-wrapper {
max-height: 100% !important;
}
https://stackoverflow.com/questions/12390626
复制相似问题