|
/* |
|
So here is our RangeSeq implementation. It stores the from and to values |
|
and uses those to determine what values it should return from this.next, |
|
and if the end of the sequence has been reached in this.end. |
|
*/ |
|
function RangeSeq(from, to) { |
|
this.from = from; |
|
this.to = to; |
|
} |
|
|
|
/* |
|
Here we are using this.from to keep track of what value were at. |
|
We increment it each time next is called. One thing to note is |
|
that I'm incrementing this.from on the same line where I return |
|
it. There are two ways to use the increment (++) operator. You |
|
can put it before, which will equal the incremented value, or |
|
you put it after which will give you the current value. So even |
|
though I'm returning this.from++, it actually returns the current |
|
value of this.from then increments it. |
|
*/ |
|
RangeSeq.prototype.next = function() { |
|
// We can actually do whats called "eating our own dogfood" here |
|
// and make use of this.end within this.next. |
|
if (this.end) { |
|
// If we don't have any more values, return null. Returning null |
|
// when there is no more data is a standard practice in a lot of |
|
// languages including JavaScript, so we'll use that convention. |
|
return null; |
|
} else { |
|
return this.from++; |
|
} |
|
}; |
|
|
|
/* |
|
Here's how we know if we've reached the end of the sequence. We've |
|
been incrementing this.from, so if it's greater than this.to we |
|
know we've reached the end. |
|
*/ |
|
Object.defineProperty(RangeSeq.prototype, 'end', { |
|
get: function() { |
|
return this.from > this.to; |
|
} |
|
}) |
|
|
|
/* |
|
So here is the ArraySeq implementation. Again, we need to have a next |
|
and end function to match the interface, but the implementation will |
|
be different since we're dealing with an actual array of values. |
|
*/ |
|
function ArraySeq(arr) { |
|
// We don't want someone modifying the passed array and changing the |
|
// internal state of our sequence, so we make an internal copy of the |
|
// passed array by calling Array#slice passing no arguments. |
|
this.arr = arr.slice(); |
|
// We need to keep track of our current position in the array, so we |
|
// create a property called index to store that position. We start at |
|
// zero since that's the first index in the array. |
|
this.index = 0; |
|
} |
|
|
|
ArraySeq.prototype.next = function() { |
|
// Again, eating our own dogfood. |
|
if (this.end) { |
|
return null; |
|
} else { |
|
// This is the same incrementing situation as in the RangeSeq. This |
|
// statement gets the current value of this.index, then increments it. |
|
return this.arr[this.index++]; |
|
} |
|
} |
|
|
|
/* |
|
Since we're incrementing this.index, we know we've reached the end of the |
|
array when this.index is equal to this.arr.length. |
|
*/ |
|
Object.defineProperty(ArraySeq.prototype, 'end', { |
|
get: function() { |
|
return this.index === this.arr.length; |
|
} |
|
}); |
|
|
|
/* |
|
Now we can make use of our sequence implementations. As far as interacting |
|
with our sequences, the code that uses our API (called the API consumer) |
|
doesn't care which sequence it is, only that it implements the functions |
|
of the sequence interface. This is really powerful, and is the whole point |
|
of this pattern. We can write code that is "agnostic" to the particular |
|
implementation, so if we want to use the code with another sequence, or |
|
internally change how a sequence works, we won't have to change the code |
|
that uses it. |
|
*/ |
|
function logFive(sequence) { |
|
for (var i = 0; i < 5 && !sequence.end; i++) { |
|
console.log(sequence.next()); |
|
} |
|
} |
|
|
|
/* |
|
And that's it! Now we can use our sequence implementations and log to the |
|
console. |
|
*/ |
|
console.log('logFive(new ArraySeq([1, 2]));\n') |
|
logFive(new ArraySeq([1, 2])); |
|
console.log('logFive(new RangeSeq(100, 1000)));\n'); |
|
logFive(new RangeSeq(100, 1000)); |