Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Let's say we have a list of integers:

var fibonacci = [1,1,2,3,5,8,13,21];

I want to be able to get the next and previous element (just to move the element pointer, without modifying the array) in following manner (example, might go without prototype to redefine the Array interface but why not):

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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
693 views
Welcome To Ask or Share your Answers For Others

1 Answer

If you want to keep the list as an Array, you'll have to change its [[prototype]] to make it look like an iterable collection:

Array.prototype.next = function() {
    return this[++this.current];
};
Array.prototype.prev = function() {
    return this[--this.current];
};
Array.prototype.current = 0;

Now every Array will have the methods prev and next, and the current property, which points to the "current" elements. A caveat: the current property can be modified, thus leading to impredictable results.

Post scriptum: I don't recommend to make prev and next return false when the index is out of range. If you really want to, you can change the methods to something like:

Array.prototype.next = function() {
    if (!((this.current + 1) in this)) return false;
    return this[++this.current];
};

UPDATE mid-2016

I'm updating this answer because it seems it's still receiving views and votes. I should have clarified that the given answer is a proof of concept and in general extending the prototype of native classes is a bad practice, and should be avoided in production projects.

In particular, it's not much because it's going to mess with for...in cycles - which should always be avoided for arrays and it's definitely a bad practice for iterating through their elements - and also because since IE9 we can reliably do this instead:

Object.defineProperty(Array.prototype, "next", {
    value: function() { return this[++this.current]; },
    enumerable: false
});

The main problem is that extending native classes is not future-proof, i.e. it may happen that ECMA will introduce a next method for arrays that will probably be incompatible with your implementation. It already happened even with very common JS frameworks - the last case was MooTools' contains array extension which led ECMA to change the name to includes (bad move, IMO, since we already have contains in DOMTokenList objects like Element.classList).

That being said, it's not that you must not extend native prototypes, but you should be aware of what you're doing. The first advice I can give you is to choose names that won't clash with future standard extensions, e.g. myCompanyNext instead of just next. This will cost you some code elegance but will make you sleep sound.

Even better, in this case you can effectively extend the Array class:

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];
};

In ES6, moreover, it's easier to extend native classes:

class MyTraversableArray extends Array {
    next() {
        return this[++this.current];
    }
}

Alas, transpilers have a hard time with native class extensions, and Babel removed its support. But it's because they can't exactly replicate some behaviours which have no influence in our case, so you can stick with the above old ES3 code.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...