Js 中的let_js构造器constructor

Js 中的let_js构造器constructor它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。 创建一个指针对象,指向当前数据结构的起始位置。 每一次调用next方法,都会返回数据结构的当前成员的信息。(返回一个包含value和done两个属性的对象。…

Iterator

它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

Iterator 的遍历过程:

  1. 创建一个指针对象,指向当前数据结构的起始位置。
  2. 不断调用指针对象的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方法还有returnthrow方法,它们是可选的。

return方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句),就会调用return方法。return方法必须返回一个对象。

Generator

Generator 函数是 ES6 提供的一种异步编程解决方案,执行 Generator 函数会返回一个遍历器对象。

Generator 函数使用function*定义(有没有空格都行),内部可以使用yieldyield*表达式。

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后面的值和donefalse,如果遇到return就返回return后的值和donetrue的对象,如果没有碰到yieldreturn则返回值为undefineddonetrue的对象。

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

只要donetruefor...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语句或者抛出错误。

  1. await命令后面一般是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
  2. await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象。
  3. 任何一个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 对象被rejectfor 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 函数内部,能够同时使用awaityield命令。

如果异步 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

(0)

相关推荐

  • Python jieba库的安装方法

    Python jieba库的安装方法自然语言处理是人工智能领域的重要研究方向之一。Python jieba是一款专业的中文分词库,能够高效地将中文文本按照词语进行切分,适用于文本分类、情感分析、信息检索等多种自然语言处理任务。但是,在使用Python jieba之前,需要进行安装配置。本文将从多个方面进行详细讲解Python jieba库的安装方法。

    2024-09-19
    15
  • 更换ip地址_深入盘点更换IP地址的几种方式

    更换ip地址_深入盘点更换IP地址的几种方式文章浏览阅读815次。如今实现网络的互联是如此的简单,拥有一台电脑或手机就可以帮助我们打开网络世界的大门,享受网络带来的欢乐,而实现这些设备上网的前提则必须分配到一个IP地址才能进行网络连接,它相当于通行证,没有它我们就不能进行上网操作,即

    2023-11-05
    126
  • innodb存储引擎对mvcc的实现原理_最早支持的存储引擎

    innodb存储引擎对mvcc的实现原理_最早支持的存储引擎一、InnoDB 体系架构 InnoDB 存储引擎有多个内存块,可以认为这些内存块组成了一个大的内存池,负责如下工作: 维护所有进程/线程需要访问的多个内部数据结构。 缓存磁盘上的数据,方便快速的读取

    2023-03-20
    158
  • mysql数据类型范围_MySQL修改数据类型

    mysql数据类型范围_MySQL修改数据类型数据类型(精) MySQL中的数据类型 整型类型 类型介绍 可选属性 M 显示宽度不会影响类型的实际宽度 设置字段f1,f2,f3 f1 INT, f2 INT(5), f3 INT(5) ZEROF

    2023-05-09
    156
  • 数据库恢复技术的方法_数据库恢复技术方法

    数据库恢复技术的方法_数据库恢复技术方法10.1 事务的基本概念: 什么是事务?事务是用户定义的一个数据库操作序列,该操作要么全做,要么全不做,是一个不可分割的工作单位,是恢复(知识点)和并发控制(知识点)的基本单位 事务和程序的区别: 在

    2023-06-17
    127
  • 利用Python defaultdict提高字典操作效率

    利用Python defaultdict提高字典操作效率
    Python字典是一种类似于映射的数据结构,由一系列键值对组成。字典中的键必须是唯一的、不可变的类型,例如字符串、整数或元组,而值可以是任意类型的Python对象。对于许多应用程序来说,Python字典是一种最方便的数据结构,它允许快速访问、插入和删除元素,并支持非常快速的查找操作。

    2024-01-09
    114
  • 初识MySQL数据库「终于解决」

    初识MySQL数据库「终于解决」一 、引言 假设现在你已经是某大型互联网公司的高级程序员,让你写一个火车票购票系统,来hold住双十一期间全国的购票需求,你怎么写? 由于在同一时段抢票的人数太多,所以你的程序不可能写在一台机器上,应

    2023-05-06
    144
  • 深度剖析Python中的get方法

    深度剖析Python中的get方法Python是一门高级编程语言,它提供了丰富的工具和库,使得开发人员能够快速开发出高效的应用程序。其中一个非常有用的工具就是get方法。在Python中,get方法是字典(dictionary)类中的常用方法之一,可以用于获取字典中指定元素的值。get方法的作用非常广泛,本文将从多个方面阐述get方法的使用,更好地帮助读者了解和掌握这个强大而有用的方法。

    2024-03-18
    76

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注