大家好,我是考100分的小小码 ,祝大家学习进步,加薪顺利呀。今天说一说Js 中的let_js构造器constructor,希望您对编程的造诣更进一步.
Iterator
它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
Iterator 的遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置。
- 不断调用指针对象的
next
方法
每一次调用next
方法,都会返回数据结构的当前成员的信息。(返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。)
ES6 规定,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历的”。Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。(Symbol)
Symbol.iterator
,它是一个表达式,返回Symbol
对象的iterator
属性,这是一个预定义好的、类型为Symbol
的特殊值。
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
}
class LinkedList {
[Symbol.iterator] () {
return {
next () {
return { value: 1, done: true }
}
}
}
}
原生数据结构部署了遍历器接口有:Array Map Set String TypedArray 函数的 arguments 对象 NodeList 对象
。
for…of
只要部署了遍历器接口就可以使用for...of
遍历。
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value); // 0, 1, 2
}
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a', 'b', 'c'
}
以下场景会调用 Iterator 接口
let [first, ...rest] = [1,2,3] // 解构赋值
[...'hi'] // 扩展运算符
let generator = function* () {
yield* [1,2,3] // yield*
};
// for...of
// Array.from()
// Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
// Promise.all() Promise.race()
遍历器返回对象除了next
方法还有return
和throw
方法,它们是可选的。
return
方法的使用场合是,如果for...o
f循环提前退出(通常是因为出错,或者有break
语句),就会调用return
方法。return
方法必须返回一个对象。
Generator
Generator 函数是 ES6 提供的一种异步编程解决方案,执行 Generator 函数会返回一个遍历器对象。
Generator 函数使用function*
定义(有没有空格都行),内部可以使用yield
和yield*
表达式。
function* g() {
yield 1
yield 2
return 3
yield 4
}
let a = g()
// 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,遍历器对象
console.log(a)
/*
__proto__: Generator
__proto__: Generator
constructor: GeneratorFunction {prototype: Generator, constructor: ƒ, Symbol(Symbol.toStringTag): "GeneratorFunction"}
next: ƒ next()
return: ƒ return()
throw: ƒ throw()
Symbol(Symbol.toStringTag): "Generator"
__proto__: Object
[[GeneratorLocation]]: VM89:1
[[GeneratorStatus]]: "suspended"
[[GeneratorFunction]]: ƒ* g()
[[GeneratorReceiver]]: Window
[[Scopes]]: Scopes[2]
*/
// 必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。
a.next() // { value: 1, done: false }
a.next() // { value: 2, done: false }
a.next() // { value: 3, done: true }
a.next() // { value: undefined, done: true }
Generator 函数是分段执行的,yield
表达式是暂停执行的标记,而next
方法可以恢复执行,当碰到yield
就返回yield
后面的值和done
为false
,如果遇到return
就返回return
后的值和done
为true
的对象,如果没有碰到yield
和return
则返回值为undefined
和done
为true
的对象。
yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
yield
表达式如果用在另一个表达式之中,必须放在圆括号里面,表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
function* demo() {
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
}
function* demo() {
foo(yield 'a', yield 'b'); // OK
let input = yield; // OK
}
任意一个对象的Symbol.iterator
方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象,可以把 Generator 赋值给对象的Symbol.iterator
属性,从而使得该对象具有 Iterator 接口。
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
yield
可以有返回值,它通过next
方法传入。
function* g() {
let v1 = yield
console.log(v1)
let v2 = yield 2
console.log(v2)
}
let a = g()
a.next() // { value: undefined, done: false }
a.next([1,2,3]) // { value: 2, done: false }
a.next() // { value: undefined, done: true }
// [1,2,3]
// undefined
上面的例子,第一个next
方法执行到第一个yield
暂停,执行第二次next
方法时,我们用[1,2,3]
做为参数,这时第一个yield
就返回[1,2,3]
,所以第一次打印[1,2,3]
第二次打印undefined
。
所以对第一个next
方法传递参数是没有用的,第二个next
的参数才作为第一个yield
的返回值。
for...of
循环可以自动遍历 Generator函数运行时生成的Iterator
对象,且此时不再需要调用next
方法。
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5
只要done
为true
,for...of
就会终止循环,所以return
的返回值没有打印。
Generator 函数原型上有throw
方法,可以用来在函数体外抛出错误,然后在 Generator 函数体内捕获。
var g = function* () {
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
};
var i = g();
i.next();
try {
i.throw('a'); // throw 方法可以接受一个参数,该参数会被catch语句接收
i.throw('b'); // throw 方法会附带执行一次 next 方法
} catch (e) {
console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b
如果内部没有捕获,那么错误就会跑到外面来,被外部捕获。如果内外都没有捕获错误,那么程序将报错,直接中断执行。
在使用throw
之前,必须执行一次next
方法,否则抛出的错误不会被内部捕获,而是直接在外部抛出,导致程序出错。
函数体内的错误可以被外部捕获。
function* g() {
yield 1
yield 2
throw new Error('err')
yield 3
}
let a = g()
try {
console.log(a.next()) // { value: 1, done: tfalse }
console.log(a.next()) // { value: 2, done: false }
console.log(a.next()) // 报错
} catch(e) {}
console.log(a.next()) // { value: undefined, done: true }
只要内部错误没有捕获,跑到外面来,那么迭代器就会自动停止。
Generator 函数返回的遍历器对象,还有一个return
方法,可以返回给定的值,并且终结遍历 Generator 函数。
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
// ---------------
function* numbers () {
yield 1;
try {
yield 2;
yield 3;
} finally {
yield 4;
yield 5;
}
yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }
// 如果 Generator 函数内部有try...finally代码块,且正在执行try代码块,那么return方法会推迟到finally代码块执行完再执行
yield*
表达式,用来在一个 Generator 函数内部,调用另一个 Generator 函数。
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
yield 'a';
yield 'b';
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}
for (let v of bar()){
console.log(v);
}
// "x"
// "a"
// "b"
// "y"
function* g() {
yield 1
yield 2
return 3
}
function* e() {
let returnValue = yield* g()
console.log(returnValue) // 3
// 如果另一个函数带有 return 则需要自己获取
yield* ['a', 'b', 'c'] // 还可以是 数组,字符串这些原生带有迭代器的对象
}
如果yield
表达式后面跟的是一个遍历器对象,需要在yield
表达式后面加上星号,表明它返回的是一个遍历器对象。
function* iterTree(tree) {
if (Array.isArray(tree)) {
for(let i=0; i < tree.length; i++) {
yield* iterTree(tree[i]);
}
} else {
yield tree;
}
}
const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
[...iterTree(tree)]
yield*
可以轻松的抹平数组。
Generator 函数不能和new
命令一起用。
Async
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。它可以非常清晰的将异步操作写成同步操作。
let p = async function getData() {
let data = await db.getData()
console.log(data)
}
console.log(p)
/* Promise {<resolved>: undefined} __proto__: Promise [[PromiseStatus]]: "resolved" [[PromiseValue]]: undefined */
let b = async function () {} // 函数表达式
let c = async () => {} // 箭头函数
async
函数 以async
开头,其内部可以使用await
等待异步操作完成。async
函数会返回一个Promise
对象,所以可以使用then方法添加回调函数。
函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
async
函数内部return
语句返回的值,会成为then
方法回调函数的参数,async
函数内部抛出错误,会导致返回的 Promise
对象变为reject
状态。抛出的错误对象会被catch
方法回调函数接收到。
async function a() {
throw new Error('err')
}
a() // Uncaught (in promise) Error 报错,但不会终止程序
console.log(123) // 正常执行
async
函数返回的Promise
对象,必须等到内部所有await
命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return
语句或者抛出错误。
await
命令后面一般是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。await
命令后面是一个thenable
对象(即定义then方法的对象),那么await
会将其等同于 Promise 对象。- 任何一个
await
语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}
catch (err) {
console.error(err);
}
} // 我们可以将多个 await 命令放入 try-catch 中
async
函数其实它就是 Generator 函数的语法糖。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
function spawn(genF) {
return new Promise(function(resolve, reject) { // 返回一个 Promise 对象
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e); // 执行 fn 中的代码,如果出错直接 reject 返回的 Promise
}
if(next.done) { // 如果 fn 执行完成则 resolve fn return 的值
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
// 将 yield 返回的值变成 Promise 并执行它的 then 方法
step(function() { return gen.next(v); });
// 当 yield 后面的 Promise 执行成功完成时则继续执行 fn 函数
// 并将它产生的值传入 fn 函数
}, function(e) {
step(function() { return gen.throw(e); });
// 如果出现错误则将错误传入 fn 内部。
// 如果内部没有捕获则被本函数上面的 try-catch 捕获
});
}
step(function() { return gen.next(undefined); });
});
}
异步遍历器
异步遍历器和遍历器的区别在于,异步遍历器返回的是一个 Promise 对象,但是它的值的格式和遍历器一样。异步遍历器接口,部署在Symbol.asyncIterator
属性上面。
异步遍历器它的next
不用等到上一个 Promise resolve 了才能调用。这种情况下,next
方法会累积起来,自动按照每一步的顺序运行下去。
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
const [{value: v1}, {value: v2}] = await Promise.all([ asyncIterator.next(), asyncIterator.next() ]);
console.log(v1, v2); // a b
for await…of
for await...of
循环,是用于遍历异步的 Iterator 接口。
async function f() {
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
如果next
方法返回的 Promise 对象被reject
,for await...of
就会报错,要用try...catch
捕捉。
它也可以用于同步遍历器。
异步 Generator 函数
就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。
async function* gen() {
yield 'hello';
}
const genObj = gen();
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }
// 同步 Generator 函数
function* map(iterable, func) {
const iter = iterable[Symbol.iterator]();
while (true) {
const {value, done} = iter.next();
if (done) break;
yield func(value);
}
}
// 异步 Generator 函数
async function* map(iterable, func) {
const iter = iterable[Symbol.asyncIterator]();
while (true) {
const {value, done} = await iter.next();
if (done) break;
yield func(value);
}
}
Generator 函数处理同步操作和异步操作时,能够使用同一套接口。异步 Generator 函数内部,能够同时使用await
和yield
命令。
如果异步 Generator 函数抛出错误,会导致 Promise 对象的状态变为reject
,然后抛出的错误被catch
方法捕获。
yield*
yield*语句也可以跟一个异步遍历器。
async function* gen1() {
yield 'a';
yield 'b';
return 2;
}
async function* gen2() {
// result 最终会等于 2
const result = yield* gen1();
}
for await (const x of gen2()) {
console.log(x);
}
// a
// b
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13068.html