async function* 表达式
async function*
关键字可用于在表达式中定义一个异步生成器函数。
你也可以使用 async function*
声明定义一个异步生成器函数。
async function* foo() {
yield await Promise.resolve('a');
yield await Promise.resolve('b');
yield await Promise.resolve('c');
}
let str = '';
async function generate() {
for await (const val of foo()) {
str = str + val;
}
console.log(str);
}
generate();
// Expected output: "abc"
async function*
表达式与 async function*
声明非常相似,语法几乎相同。_函数名_是 async function*
表达式和 async function*
声明之间最主要的区别,在 async function*
表达式中,可以创建_匿名_函数去忽略函数名。async function*
表达式可以用作立即调用函数表达式(IIFE),该表达式在被定义后立即运行,允许你去创建一个临时的异步的可迭代对象。
按位与(&)
按位与(&
)运算符在两个操作数对应的二进位都为 1
时,该位的结果值才为 1
。
const a = 5; // 00000000000000000000000000000101
const b = 3; // 00000000000000000000000000000011
console.log(a & b); // 00000000000000000000000000000001// Expected output: 1
操作数被转换为 32 位整数,并由一系列位(0 和 1)表示。超过 32 位的数字将丢弃其最高有效位。例如,以下大于 32 位的整数将被转换为 32 位整数:
Before: 11100110111110100000000000000110000000000001
After: 10100000000000000110000000000001
第一个操作数中的每个位都与第二个操作数中的相应位配对:第一位_到_第一位,第二位_到_第二位,依此类推。
将运算符应用于每对位,然后按位构造结果。
按位与赋值(&=)
按位与赋值运算符(&=
)使用两个操作数的二进制表示,对它们进行按位与运算并将结果赋值给变量。
let a = 5; // 00000000000000000000000000000101
a &= 3; // 00000000000000000000000000000011
console.log(a); // 00000000000000000000000000000001
// Expected output: 1
按位非(~)
按位非运算符(~
)将操作数的位反转。如同其他位运算符一样,它将操作数转化为 32 位的有符号整型。
const a = 5; // 00000000000000000000000000000101
const b = -3; // 11111111111111111111111111111101
console.log(~a); // 11111111111111111111111111111010
// Expected output: -6
console.log(~b); // 00000000000000000000000000000010
// Expected output: 2
操作数被转换为 32 位有符号整型,并以一系列比特(0 和 1)表示。超过 32 位的数字将丢弃其最高有效位。
按位或(|)
按位或(|
)运算符在其中一个或两个操作数对应的二进制位为 1
时,该位的结果值为 1
。
const a = 5; // 00000000000000000000000000000101
const b = 3; // 00000000000000000000000000000011
console.log(a | b); // 00000000000000000000000000000111
// Expected output: 7
操作数被转换为 32 位整数并由一系列二进制位(0 和 1)表示。超过 32 位的数字会丢弃其最高有效位。
按位异或(^)
按位异或(^
)运算符在两个操作数有且仅有一个对应的二进制位为 1
时,该位的结果值为 1
。
const a = 5; // 00000000000000000000000000000101
const b = 3; // 00000000000000000000000000000011
console.log(a ^ b); // 00000000000000000000000000000110
// Expected output: 6
操作数被转换为 32 位整数并由一系列二进制位(0 和 1)表示。超过 32 位的数字会丢弃其最高有效位。
类表达式
class
关键字可用于在表达式中定义类。类似于函数表达式,类表达式可以是命名的,也可以是匿名的。如果命名,则类的名称只能在类体内部才能访问到。
const Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
area() {
return this.height * this.width;
}
};
console.log(new Rectangle(5, 8).area());
// Expected output: 40
类表达式的语法类似于类声明。与 class
声明一样,class
表达式的主体在严格模式下执行。
类表达式和类声明之间存在一些差异,但是:
- 类表达式可以省略类名(“绑定标识符”),这在类声明中是不可能的。
- 类表达式允许你重新定义(重新声明)类而不会抛出
SyntaxError
。类声明不是这种情况。
constructor
方法是可选的。使用类表达式生成的类将始终响应 typeof
值为 "function"
。
逗号运算符(,)
逗号(,
)运算符对它的每个操作数从左到右求值,并返回最后一个操作数的值。这让你可以创建一个复合表达式,其中多个表达式被评估,复合表达式的最终值是其成员表达式中最右边的值。这通常用于为 for
循环提供多个参数。
let x = 1;
x = (x++, x);
console.log(x);
// Expected output: 2
x = (2, 3);
console.log(x);
// Expected output: 3
delete 运算符
delete
运算符用于删除对象的一个属性;如果该属性的值是一个对象,并且没有更多对该对象的引用,该属性所持有的对象最终会自动释放。
const Employee = {
firstname: 'John',
lastname: 'Doe',
};
console.log(Employee.firstname);
// Expected output: "John"
delete Employee.firstname;
console.log(Employee.firstname);
// Expected output: undefined
delete
运算符与其他像 typeof
这样的一元运算符具有相同的优先级。因此,它接受任何由更高优先级的运算符形成的表达式。
解构赋值
解构赋值语法是一种 Javascript 表达式。可以将数组中的值或对象的属性取出,赋值给其他变量。
let a, b, rest;
[a, b] = [10, 20];
console.log(a);
// Expected output: 10
console.log(b);
// Expected output: 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// Expected output: Array [30, 40, 50]
相等(==)
相等运算符(==
)检查其两个操作数是否相等,返回一个布尔值结果。与严格相等运算符(===
)不同,它会比较不同类型的操作数,并尝试强制类型转换。
console.log(1 == 1);
// Expected output: true
console.log('hello' == 'hello');
// Expected output: true
console.log('1' == 1);
// Expected output: true
console.log(0 == false);
// Expected output: true
function* 表达式
**function*
**关键字可以在表达式内部定义一个生成器函数。
const foo = function* (y) {
yield 'a';
yield 'b';
yield 'c';
yield y*y;
};
let str = '';
for (const val of foo(2)) {
str = str + vag(str,foo());
console.log(str);
// Expected output: "abc4"
import.meta
import.meta
是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的 URL
import.meta
对象由一个关键字"import"
,一个点符号和一个meta
属性名组成。通常情况下"import."
是作为一个属性访问的上下文,但是在这里"import"
不是一个真正的对象。
import.meta
对象是由 ECMAScript 实现的,它带有一个null
的原型对象。这个对象可以扩展,并且它的属性都是可写,可配置和可枚举的。
in
如果指定的属性在指定的对象或其原型链中,则 in
运算符返回 true
。
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log('make' in car);
// Expected output: true
delete car.make;
if ('make' in car === false) {
car.make = 'Suzuki';
}
console.log(car.make);
// Expected output: "Suzuki"
继承属性
如果一个属性是从原型链上继承来的,in
运算符也会返回 true
。
"toString" in {}; // 返回 true
instanceof
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);
// Expected output: true
console.log(auto instanceof Object);
// Expected output: true
// 定义构造函数
function C() {}
function D() {}
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype 不在 o 的原型链上
o instanceof Object; // true,因为 Object.prototype.isPrototypeOf(o) 返回 true
C.prototype instanceof Object; // true,同上
C.prototype = {};
var o2 = new C();
o2 instanceof C; // true
o instanceof C; // false,C.prototype 指向了一个空对象,这个空对象不在 o 的原型链上。
D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为 C.prototype 现在在 o3 的原型链上
需要注意的是,如果表达式 obj instanceof Foo
返回 true
,则并不意味着该表达式会永远返回 true
,因为 Foo.prototype
属性的值有可能会改变,改变之后的值很有可能不存在于 obj
的原型链上,这时原表达式的值就会成为 false
。另外一种情况下,原表达式的值也会改变,就是改变对象 obj
的原型链的情况,虽然在目前的 ES 规范中,我们只能读取对象的原型而不能改变它,但借助于非标准的 __proto__
伪属性,是可以实现的。比如执行 obj.__proto__ = {}
之后,obj instanceof Foo
就会返回 false
了。
左移 (<<)
左移操作符 (<<
) 将第一个操作数向左移动指定位数,左边超出的位数将会被清除,右边将会补零。
const a = 5; // 00000000000000000000000000000101
const b = 2; // 00000000000000000000000000000010
console.log(a << b); // 00000000000000000000000000010100
// Expected output: 20
9 (十进制): 00000000000000000000000000001001 (二进制)
--------------------------------
9 << 2 (十进制): 00000000000000000000000000100100 (二进制) = 36 (十进制)
左移赋值(<<=)
左移赋值运算符(<<=
)将变量向左移动指定的位数,并将结果赋值给变量。
let a = 5; // 00000000000000000000000000000101
a <<= 2; // 00000000000000000000000000010100
console.log(a);
// Expected output: 20
逻辑与赋值(&&=)
逻辑与赋值(x &&= y
)运算仅在 x
为真值时为其赋值。
let a = 1;
let b = 0;
a &&= 2;
console.log(a);
// Expected output: 2
b &&= 2;
console.log(b);
// Expected output: 0
逻辑或赋值(||=)
逻辑或赋值(x ||= y
)运算仅在 x
为假值时为其赋值。
const a = { duration: 50, title: '' };
a.duration ||= 10;
console.log(a.duration);
// Expected output: 50
a.title ||= 'title is empty.';
console.log(a.title);
// Expected output: "title is empty"
new 运算符
new
运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const car1 = new Car('Eagle', 'Talon TSi', 1993);
console.log(car1.make);
// Expected output: "Eagle"
new
关键字会进行如下的操作:
- 创建一个空的简单 JavaScript 对象(即
{}
); - 为步骤 1 新创建的对象添加属性
__proto__
,将该属性链接至构造函数的原型对象; - 将步骤 1 新创建的对象作为
this
的上下文; - 如果该函数没有返回对象,则返回
this
。
(译注:关于对象的 constructor
,参见 Object.prototype.constructor
)
创建一个用户自定义的对象需要两步:
- 通过编写函数来定义对象类型。
- 通过
new
来创建对象实例。
创建一个对象类型,需要创建一个指定其名称和属性的函数;对象的属性可以指向其他对象,看下面的例子:
当代码 new Foo(...)
执行时,会发生以下事情:
- 一个继承自
Foo.prototype
的新对象被创建。 - 使用指定的参数调用构造函数
Foo
,并将this
绑定到新创建的对象。new Foo
等同于new Foo
()
,也就是没有指定参数列表,Foo
不带任何参数调用的情况。 - 由构造函数返回的对象就是
new
表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤 1 创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
你始终可以对已定义的对象添加新的属性。例如,car1.color = "black"
语句给 car1
添加了一个新的属性 color
,并给这个属性赋值 “black
”。但是,这不会影响任何其他对象。要将新属性添加到相同类型的所有对象,你必须将该属性添加到 Car
对象类型的定义中。
你可以使用 Function.prototype
属性将共享属性添加到以前定义的对象类型。这定义了一个由该函数创建的所有对象共享的属性,而不仅仅是对象类型的其中一个实例。下面的代码将一个值为 null
的 color
属性添加到 car
类型的所有对象,然后仅在实例对象 car1
中用字符串 “black
” 覆盖该值。
function Car() {}
car1 = new Car();
car2 = new Car();
console.log(car1.color); // undefined
Car.prototype.color = "original color";
console.log(car1.color); // original color
car1.color = "black";
console.log(car1.color); // black
console.log(car1.__proto__.color); //original color
console.log(car2.__proto__.color); //original color
console.log(car1.color); // black
console.log(car2.color); // original color
new.target
new.target
属性允许你检测函数或构造方法是否是通过new运算符被调用的。在通过new运算符被初始化的函数或构造方法中,new.target
返回一个指向构造方法或函数的引用。在普通的函数调用中,new.target
的值是undefined
。
在普通的函数调用中(和作为构造函数来调用相对),new.target
的值是undefined
。这使得你可以检测一个函数是否是作为构造函数通过new被调用的。
function Foo() {
if (!new.target) throw "Foo() must be called with new";
console.log("Foo instantiated with new");
}
Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"
逻辑空赋值(??=)
逻辑空赋值运算符(x ??= y
)仅在 x
是空值(null
或 undefined
)时对其赋值。
const a = { duration: 50 };
a.duration ??= 10;
console.log(a.duration);
// Expected output: 50
a.speed ??= 25;
console.log(a.speed);
// Expected output: 25
短路评估
空值合并运算符从左至右求值,其使用以下规则测试是否可能进行语法短路求值:
(结果非 null 或 undefined 的表达式) ?? expr
被短路求值为左侧表达式,当左侧证明为既非 null
也非 undefined
.
语法短路意味着 expr
部分尚未被求值,因此任何与其求值产生的相关副作用都不会生效(例如,如果 expr
是一个函数调用,则该调用将不会发生)。
空值合并运算符(??)
空值合并运算符(??
)是一个逻辑运算符,当左侧的操作数为 null
或者 undefined
时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或运算符(||
)不同,逻辑或运算符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 ||
来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''
或 0
)时。见下面的例子。
const foo = null ?? 'default string';
console.log(foo);
// Expected output: "default string"
const baz = 0 ?? 42;
console.log(baz);
// Expected output: 0
可选链运算符(?.)
可选链运算符(?.
)允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.
运算符的功能类似于 .
链式运算符,不同之处在于,在引用为空 (nullish ) (null
或者 undefined
) 的情况下不会引起错误,该表达式短路返回值是 undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined
。
当尝试访问可能不存在的对象属性时,可选链运算符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能确定哪些属性必定存在,可选链运算符也是很有帮助的。
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
const dogName = adventurer.dog?.name;
console.log(dogName);
// Expected output: undefined
console.log(adventurer.someNonExistentMethod?.());
// Expected output: undefined
obj.val?.prop
obj.val?.[expr]
obj.func?.(args)
属性访问器
属性访问器提供了两种方式用于访问一个对象的属性,它们分别是点号和方括号。
const person1 = {};
person1['firstname'] = 'Mario';
person1['lastname'] = 'Rossi';
console.log(person1.firstname);
// Expected output: "Mario"
const person2 = {
firstname: 'John',
lastname: 'Doe',
};
console.log(person2['lastname']);
// Expected output: "Doe"
右移(>>)
右移运算符(>>
)将一个操作数的二进制表示形式向右移动指定位数,该操作数可以是数值或者 BigInt 类型。右边移出位被丢弃,左边移出的空位补符号位(最左边那位)。该操作也称为“符号位传播右移”(sign-propagating right shift)或“算术右移”(arithmetic right shift),因为返回值的符号位与第一个操作数的符号位相同。
const a = 5; // 00000000000000000000000000000101
const b = 2; // 00000000000000000000000000000010
const c = -5; // 11111111111111111111111111111011
console.log(a >> b); // 00000000000000000000000000000001
// Expected output: 1
console.log(c >> b); // 11111111111111111111111111111110
// Expected output: -2
右移赋值(>>=)
右移赋值运算符(>>=
)将变量向右移动指定的位数,并将结果赋值给变量。
let a = 5; // 00000000000000000000000000000101
a >>= 2; // 00000000000000000000000000000001
console.log(a);
// Expected output: 1
let b = -5; // 11111111111111111111111111111011
b >>= 2; // 11111111111111111111111111111110
console.log(b);
// Expected output: -2
展开语法
展开语法 (Spread syntax), 可以在函数调用/数组构造时,将数组表达式或者 string 在语法层面展开;还可以在构造字面量对象时,将对象表达式按 key-value 的方式展开。(译者注: 字面量一般指 [1, 2, 3]
或者 {name: "mdn"}
这种简洁的构造方式)
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// Expected output: 6
console.log(sum.apply(null, numbers));
// Expected output: 6
严格相等(===)
严格相等运算符(===
)会检查它的两个操作数是否相等,并且返回一个布尔值结果。与相等运算符不同,严格相等运算符总是认为不同类型的操作数是不同的。
console.log(1 === 1);
// Expected output: true
console.log('hello' === 'hello');
// Expected output: true
console.log('1' === 1);
// Expected output: false
console.log(0 === false);
// Expected output: false
严格不相等(!==)
严格不相等运算符(!==
)检查它的两个对象是否不相等,返回一个布尔结果。与不相等运算符不同,严格不相等运算符总是认为不同类型的对象是不同的。
console.log(1 !== 1);
// Expected output: false
console.log('hello' !== 'hello');
// Expected output: false
console.log('1' !== 1);
// Expected output: true
console.log(0 !== false);
// Expected output: true
super
super 关键字用于访问对象字面量或类的原型([[Prototype]])上的属性,或调用父类的构造函数。
super.prop
和 super[expr]
表达式在类和对象字面量任何方法定义中都是有效的。super(...args)
表达式在类的构造函数中有效。
super([arguments]) // 调用父类的构造函数
super.propertyOnParent
super[expression]
this
与其他语言相比,函数的 this
关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。
在绝大多数情况下,函数的调用方式决定了 this
的值(运行时绑定)。this
不能在执行期间被赋值,并且在每次函数被调用时 this
的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this
值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this
的值将保持为闭合词法上下文的值)。
const test = {
prop: 42,
func: function () {
return this.prop;
},
};
console.log(test.func());
// Expected output: 42
函数上下文
在函数内部,this
的值取决于函数如何被调用。可以将 this
看作是函数的一个隐藏参数(就像函数定义中声明的参数一样),this
是语言在函数体被执行时为你创建的绑定。
对于典型的函数,this
的值是函数被访问的对象。换句话说,如果函数调用的形式是 obj.f()
,那么 this
就指向 obj
。例如:
function getThis() {
return this;
}
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
obj1.getThis = getThis;
obj2.getThis = getThis;
console.log(obj1.getThis()); // { name: 'obj1', getThis: [Function: getThis] }
console.log(obj2.getThis()); // { name: 'obj2', getThis: [Function: getThis] }
回调
当一个函数作为回调函数传递时,this
的值取决于如何调用回调,这由 API 的实现者决定。回调函数通常以 undefined
作为 this
的值被调用(直接调用,而不附加到任何对象上),这意味着如果函数是在非严格模式,this
的值会是全局对象(globalThis
)。这在迭代数组方法、Promise()
构造函数等例子中都是适用的。
function logThis() {
"use strict";
console.log(this);
}
[1, 2, 3].forEach(logThis); // undefined、undefined、undefined
箭头函数
在箭头函数中,this
保留了闭合词法上下文的 this
值。换句话说,当对箭头函数求值时,语言不会创建一个新的 this
绑定。
例如,在全局代码中,无论是否在严格模式下,由于全局上下文绑定,this
值总是 globalThis
const globalObject = this;
const foo = () => this;
console.log(foo() === globalObject); // true
此外,当使用 call()
、bind()
或 apply()
调用箭头函数时,thisArg
参数会被忽略。不过,你仍然可以使用这些方法传递其他参数。
const obj = { name: "obj" };
// 尝试使用 call 设置 this
console.log(foo.call(obj) === globalObject); // true
// 尝试使用 bind 设置 this
const boundFoo = foo.bind(obj);
console.log(boundFoo() === globalObject); // true
typeof
typeof
运算符返回一个字符串,表示操作数的类型
console.log(typeof 42);
// Expected output: "number"
console.log(typeof 'blubber');
// Expected output: "string"
console.log(typeof true);
// Expected output: "boolean"
console.log(typeof undeclaredVariable);
// Expected output: "undefined"
无符号右移(>>>)
无符号右移运算符(>>>
)(零填充右移)将左操作数计算为无符号数,并将该数字的二进制表示形式移位为右操作数指定的位数,取模 32。向右移动的多余位将被丢弃,零位从左移入。其符号位变为 0
,因此结果始终为非负数。与其他按位运算符不同,零填充右移返回一个无符号 32 位整数。
const a = 5; // 00000000000000000000000000000101
const b = 2; // 00000000000000000000000000000010
const c = -5; // 11111111111111111111111111111011
console.log(a >>> b); // 00000000000000000000000000000001
// Expected output: 1
console.log(c >>> b); // 00111111111111111111111111111110
// Expected output: 1073741822
该运算符将第一个操作数向右移动指定的位数。向右移动的多余位将被丢弃。零位从左侧移入。其符号位变为 0
,因此其表示的结果始终为非负数。与其他按位运算符不同,零填充右移返回无符号 32 位整数。
以十进制(以 10 为基数)数字 9
和 -9
的 32 位二进制表示为例:
9 (base 10): 00000000000000000000000000001001 (base 2)
-9 (base 10): 11111111111111111111111111110111 (base 2)
无符号右移赋值(>>>=)
无符号右移赋值(>>>=
)运算符向右移动移动指定(二进制)位数,并将结果赋值给变量。
let a = 5; // 00000000000000000000000000000101
a >>>= 2; // 00000000000000000000000000000001
console.log(a);
// Expected output: 1
let b = -5; // -00000000000000000000000000000101
b >>>= 2; // 00111111111111111111111111111110
console.log(b);
// Expected output: 1073741822
void 运算符
void
运算符对给定的表达式进行求值,然后返回 undefined
。
const output = void 1;
console.log(output);
// Expected output: undefined
void console.log('expression evaluated');
// Expected output: "expression evaluated"
void (function iife() {
console.log('iife is executed');
})();
// Expected output: "iife is executed"
void function test() {
console.log('test function executed');
};
try {
test();
} catch (e) {
console.log('test function is not defined');
// Expected output: "test function is not defined"
}
这个运算符允许在表达式执行完成时,产生(某些地方)期望获得的 undefined
值。
void
运算符通常只用于获取 undefined
的原始值,一般使用 void(0)
(等同于 void 0
)。在上述情况中,也可以使用全局变量 undefined
来代替。
yield
yield
关键字用于暂停和恢复生成器函数。
function* foo(index) {
while (index < 2) {
yield index;
index++;
}
}
const iterator = foo(0);
console.log(iterator.next().value);
// Expected output: 0
console.log(iterator.next().value);
// Expected output: 1
yield
关键字使生成器函数执行暂停,yield
关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的 return
关键字。
yield
关键字实际返回一个 IteratorResult
对象,它有两个属性,value
和 done
。value
属性是对 yield
表达式求值的结果,而 done
是 false
,表示生成器函数尚未完全完成。
一旦遇到 yield
表达式,生成器的代码将被暂停运行,直到生成器的 next()
方法被调用。每次调用生成器的 next()
方法时,生成器都会恢复执行,直到达到以下某个值:
yield
,导致生成器再次暂停并返回生成器的新值。下一次调用next()
时,在yield
之后紧接着的语句继续执行。throw
用于从生成器中抛出异常。这让生成器完全停止执行,并在调用者中继续执行,正如通常情况下抛出异常一样。- 到达生成器函数的结尾。在这种情况下,生成器的执行结束,并且
IteratorResult
给调用者返回value
的值是undefined
并且done
为true
。 - 到达
return
语句。在这种情况下,生成器的执行结束,并将IteratorResult
返回给调用者,其value
的值是由return
语句指定的,并且done
为true
。
如果将参数传递给生成器的 next()
方法,则该值将成为生成器当前 yield
操作返回的值。
在生成器的代码路径中的 yield
运算符,以及通过将其传递给 Generator.prototype.next()
指定新的起始值的能力之间,生成器提供了强大的控制力。
yield*
yield*
表达式用于委托给另一个generator
或可迭代对象。
yield* [[expression]];
委托给其他生成器
以下代码中,g1()
yield
出去的每个值都会在 g2()
的 next()
方法中返回,就像那些 yield
语句是写在 g2()
里一样。
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
var iterator = g2();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }