我一直在研究javascript生成器和迭代器,并且想知道是否有一种方法可以编写生成器函数以返回当前位置的值---当然不必调用next()
或记住从上次next()
调用返回的值。
更具体地说,我的尝试失败:
function* iterable(arr) {
this.index = 0;
this.arr = arr;
while(this.index < this.arr.length) {
yield this.arr[this.index++];
}
}
iterable.prototype.current = function () {
return this.arr[this.index];
}
const i = iterable([0, 1, 2]);
console.log(i.current()); // TypeError: Cannot read property 'undefined' of undefined
可以使用这样的类来实现所需的功能(我知道以下事实:迭代器的返回值将是像{ value: 1, done: false }
这样的对象):
class iterableClass {
constructor(arr) {
this.index = 0;
this.arr = arr;
}
get(i) {
return this.index < arr.length ? this.arr[this.index] : false;
}
next() {
const val = this.get(this.index);
this.index++;
return val;
}
current() {
return this.get(this.index);
}
}
const i = iterableClass([0, 1, 2]);
console.log(i.current()); // 0
虽然我只能使用该类(甚至是一个普通的旧函数),但我想知道这是否可以通过生成器/迭代器完成,或者还有更好的选择。
答案 0 :(得分:2)
生成器函数的问题是:a)调用它时它不会开始运行,它只是创建生成器(<html>
<head>
<title>Scroll top demo</title>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
</head>
<body>
<div class="content-wrapper">
<div class="section section1"></div>
<div class="section section2"></div>
<div class="section section3"></div>
<div class="section section4"></div>
<a class="scrolltop">Scroll top</a>
</div>
</body>
</html>
和this.arr
直到第一次调用时才被初始化到this.index
)和b)无法像使用next()
一样从函数内部访问生成器对象。
相反,您会想要
this
或者,除了使用生成器语法,您还可以直接实现Iterator接口:
function iterable(arr) {
const gen = Object.assign(function* () {
while (gen.index < gen.arr.length) {
yield gen.arr[gen.index++];
}
}(), {
arr,
index: 0,
current() {
return gen.arr[gen.index];
},
});
return gen;
}
(您当然也可以写成function iterable(arr) {
return {
arr,
index: 0,
current() { return this.arr[this.index]; },
next() {
const done = !(this.index < this.arr.length);
return { done, value: done ? undefined : this.arr[this.index++] };
},
[Symbol.iterator]() { return this; },
};
}
)
答案 1 :(得分:1)
这个问题似乎有多种解释。我的理解是,您需要一个迭代器,该迭代器提供一种访问最新检索到的值的方法,如最终代码块中的最后一行所示:
console.log(i.current()); // 0
这样做不是迭代器接口的一部分,并且不是生成器函数提供的。您可以提供一个执行此操作的迭代器包装器,然后在generator函数中的生成器上使用它(尽管您不需要为执行的操作生成器,但标准数组迭代器可以这样做),请参见注释:>
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next`
let current = null;
// The iterator method
it.next = () => {
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
return it;
}
这具有可重用和通用的优点,而与特定的可迭代项无关。
实时示例:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next`
let current = null;
// The iterator method
it.next = () => {
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
return it;
}
// Something to iterate over
const a = [1, 2, 3];
// Example use #1: Using `current`
const it = currentWrapper(a[Symbol.iterator]());
console.log("current", it.current()); // undefined
console.log("next", it.next()); // {value: 1, done: false}
console.log("current", it.current()); // 1
console.log("currentResult", it.currentResult()); // {value: 1, done: false}
// Example use #2: Just normal use of an iterator
for (const value of currentWrapper(a)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
我专注于current
位而不是index
位,因为我将可迭代对象视为流而不是数组,但是我想添加index
很容易。有点棘手的部分是迭代器完成时,是否在调用next
时增加索引?以下不是:
// Get the Iterator prototype, which has no global name
const itPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
function currentWrapper(source) {
// Allow source to be an iterable or an iterator
if (Symbol.iterator in source) {
source = source[Symbol.iterator]();
}
// Create our wrapper iterator
const it = Object.create(itPrototype);
// Remember the last value we saw from `next` and the current "index"
let current = null;
let index = -1;
// The iterator method
it.next = () => {
// Don't increase the index if "done" (tricky bit)
if (!current || !current.done) {
++index;
}
return current = source.next();
};
// Our additional methods
it.current = () => current && current.value;
it.currentResult = () => ({...current});
it.currentIndex = () => index;
return it;
}
// Something to iterate over
const a = [1, 2, 3];
// Example use #1: Using `current`
const it = currentWrapper(a[Symbol.iterator]());
console.log("current", it.current()); // undefined
console.log("next", it.next()); // {value: 1, done: false}
console.log("current", it.current()); // 1
console.log("currentResult", it.currentResult()); // {value: 1, done: false}
console.log("currentIndex", it.currentIndex()); // 0
console.log("next", it.next()); // {value: 2, done: false}
console.log("current", it.current()); // 2
console.log("currentResult", it.currentResult()); // {value: 2, done: false}
console.log("currentIndex", it.currentIndex()); // 1
// Example use #2: Just normal use of an iterator
for (const value of currentWrapper(a)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
答案 2 :(得分:-1)
为什么不使用MDN Iterators and generators中的函数,其中仅将返回部分替换为值而不是具有value
和done
属性的对象
function makeIterator(array) {
var nextIndex = 0,
lastValue;
return {
next: function() {
return lastValue = nextIndex < array.length ? array[nextIndex++] : undefined;
},
last: function () {
return lastValue;
}
};
}
var it = makeIterator(['yo', 'ya']);
console.log(it.next());
console.log(it.next());
console.log(it.last());
console.log(it.next());