前提
ES6中的Generator函数
,这个函数与两个内部原生类有关,分别是GeneratorFunction类
和Generator类
,这个两个类是内部原生类我们只能在谷歌浏览器的控制台上能看见,但是我们无法直接使用这两个类。
GeneratorFunction类
使用function
关键字定义的普通函数
,其本质是一个Function类
的实例对象:
// 使用 function 关键字定义了一个普通函数
function normalFunction(){
var variable = 1;
console.log(variable);
}
// normalFunction 的原型对象就是 Function类 的 prototype 属性的值
Object.getPrototypeOf(normalFunction) === Function.prototype // true
使用 function
关键字 和 *
符号 定义的Generator函数
,其本质是一个GeneratorFunction类
的实例对象,GeneratorFunction类
在代码中无法使用,暂时只能通过浏览器的控制台显示而知道它的存在:
function* generatorFunction(){
yield 1;
return 2;
}
// 获取 Generator函数 的原型对象
// GeneratorFunction {prototype: Generator, constructor: ƒ, Symbol(Symbol.toStringTag): "GeneratorFunction"}
var prototype = Object.getPrototypeOf(generatorFunction);
// 获取 Generator函数 的 构造函数对象
// ƒ GeneratorFunction() { [native code] }
var constructor = Object.getPrototypeOf(generatorFunction).constructor;
// Generator函数 的构造函数对象上的prototype属性 === Generator函数 的原型对象
constructor.prototype === prototype; // true
而GeneratorFunction类
是Function类
的子类,所以Generator函数
本质依然是一个函数对象:
// GeneratorFunction类 的原型对象的原型对象是 Function类 的原型对象
Object.getPrototypeOf(Object.getPrototypeOf(generatorFunction)) === Function.prototype; // true
// GeneratorFunction类 的构造函数对象的原型对象是 Function类 的构造函数对象
Object.getPrototypeOf(Object.getPrototypeOf(generatorFunction).constructor) === Function; // true
既然Generator函数
本质上也是一个函数对象,那么Generator函数
对象和普通函数
对象在属性上其实大部分是一致的,但其中有一个属性是有很大的区别的:prototype
属性。
-
普通函数
对象C在新建完成后,会自动以Object.prototype
作为原型对象创建一个新对象N,然后在对象N上添加一个constructor
属性,属性的值就是对象C,最后将对象N作为对象C的prototype
属性的值。Reflect.ownKeys(normalFunction); // ["length", "name", ... "prototype"] // 普通函数 对象的原型对象上是没有 prototype 属性的,所以 普通函数 对象的上 prototype 属性不是来自继承 Object.getPrototypeOf(normalFunction).prototype; // undefined Object.getPrototypeOf(normalFunction.prototype) === Object.prototype; // true normalFunction.prototype.constructor === normalFunction; // true
-
GeneratorFunction类
原型对象上是存在prototype
属性的,按照继承原型链的规则,那么Generator函数
对象上的prototype
属性应该是继承自原型对象的,但是实际上不是,Generator函数
实例对象在新建完成后,会自动以GeneratorFunction类
原型对象上的prototype属性的值作为原型对象创建一个新对象G,然后将对象G作为Generator函数
对象的prototype属性的值,但是注意这里不会和普通函数
一样给prototype
属性的值的constructor
属性进行重新赋值。Reflect.ownKeys(generatorFunction); // ["length", "name", "prototype"] // Generator {constructor: GeneratorFunction, next: ƒ, return: ƒ, throw: ƒ, Symbol(Symbol.toStringTag): "Generator"} Object.getPrototypeOf(generatorFunction).prototype; generatorFunction.prototype === Object.getPrototypeOf(generatorFunction).prototype; // false Object.getPrototypeOf(generatorFunction.prototype) === Object.getPrototypeOf(generatorFunction).prototype; // true
GeneratorFunction类
的原型对象 是所有的Generator函数
实例对象的原型对象,但是其不止这一个身份,其也是Generator
类的构造函数对象,只是这个构造函数对象极其特殊。
总结:GeneratorFunction类
是Function类
的子类,Generator函数
是GeneratorFunction类
的实例对象,GeneratorFunction类
是定义声明Generator函数
的模版。
Generator类
JavaScript中我们想要定义一个类的时候,首先需要一个函数对象,其次这个函数对象上必须有一个prototype
属性,而prototype
属性的值是一个对象,这个对象上有一个constructor
属性,而constructor
属性的值指向前面的函数对象。那么我们可以以函数对象的函数名为类名,函数对象是这个类的构造函数,函数对象上的prototype属性的值则是类的原型对象。
按照上文的描述,我们可以把 GeneratorFunction类
的原型对象 认为是一个类的构造函数对象。
// 获取 GeneratorFunction类 的原型对象
var generatorFunctionPrototype = Object.getPrototypeOf(function*(){
});
// generatorFunctionPrototype 的 prototype 属性的值是一个对象,下面是谷歌浏览器控制台上的显示
// Generator {constructor: GeneratorFunction, next: ƒ, return: ƒ, throw: ƒ, Symbol(Symbol.toStringTag): "Generator"}
generatorFunctionPrototype.prototype;
// generatorFunctionPrototype 的 prototype 属性值的 constructor 属性的值等于 generatorFunctionPrototype
generatorFunctionPrototype.prototype.constructor === generatorFunctionPrototype; // true
// 而且 generatorFunctionPrototype 对象其实是与普通函数平级的,有着同一个原型对象
// 所以我们可以将 generatorFunctionPrototype 对象当作一个特殊类的特殊构造函数对象。
Object.getPrototypeOf(generatorFunctionPrototype) === Object.getPrototypeOf(function(){
}); // true
按照谷歌浏览器控制台上的显示我们将这个特殊内部类称为Generator类
,如此上面代码中的generatorFunctionPrototype
就是Generator类
的构造函数对象。
而Generator函数
则可以认为是Generator类
的子类
的构造函数对象
function* generatorFunction(){
}
// Generator函数 的原型对象是 generatorFunctionPrototype
Object.getPrototypeOf(generatorFunction) === generatorFunctionPrototype;
// Generator函数 的 prototype 属性的值的原型对象是 generatorFunctionPrototype 的 prototype 属性的值
Object.getPrototypeOf(generatorFunction.prototype) === generatorFunctionPrototype.prototype;
我们此时完全可以将Generator函数
的函数名作为一个类的类名,且这个类是Generator类
的子类,但是这个类有一个缺陷,Generator函数
的prototype
属性值的constructor
属性的值并不是指向Generator函数
,而是从原型对象上继承来的:
generatorFunction.prototype.constructor === generatorFunction; // false
generatorFunction.prototype.constructor === generatorConstructor // true
所以我们本文中将Generator函数
作为Generator类
的次级构造函数对象,GeneratorFunction类
的原型对象作为Generator类
的初始构造函数对象,Generator函数
的prototype
属性值作为Generator类
的次级原型对象,GeneratorFunction类
的原型对象的prototype
属性值作为Generator类
的初始原型对象。
猜测:之所以有次级与初始的分别,估计是为了Generator类
实例对象的归属进行区分,因为Generator类
实例对象是以次级原型对象进行创建的,而次级原型对象又是以初始原型对象创建的,那么每定义一个Generator函数
就会出现一个新的次级原型对象,同时每调用一次Generator函数
就会返回一个属于次级原型对象的Generator类
实例对象。如此初始原型对象只会有一个,而次级原型对象会有多个,如果想再所有Generator类
实例对象上添加新属性,那直接在初始原型对象上添加就行,而如果只是想在某个Generator函数
返回的Generator类
实例对象上添加属性,则在次级原型对象上添加就行。
一、Generator函数 调用
函数调用大致可以分为两种,一种通过new
关键字调用,另一种被某个对象调用。
普通函数
两种调用方法都可以使用,而Generator函数
只能使用被某个对象调用这个方法:
function normalFunction(){
var variable = 1;
console.log(variable);
}
function* generatorFunction(){
yield 1;
return 2;
}
// 被全局对象window调用
normalFunction(); // 运行正常
// 被{}对象调用
normalFunction.call({
a:1});
new normalFunction(); // 运行正常
// 被全局对象window调用
generatorFunction(); // 运行正常
// 被{}对象调用
generatorFunction.call({
b:2});
new generatorFunction(); // 报错
普通函数
与Generator函数
在调用时的区别:
-
普通函数
在被某个对象调用时,是直接执行函数体中的代码。而通过new
关键字调用则会先以函数实例对象的prototype
属性的值作为原型对象创建一个对象N,然后在用对象N调用这个函数,对对象N进行初始化,此时我们可以认为函数名是一个类名,对象N是这个类的实例对象,最后将对象N作为函数的返回值。 -
Generator函数
在被某个对象W调用时,则不在是直接执行Generator函数
的函数体中的代码了,而是先以Generator函数
函数实例对象的prototype
属性的值作为原型对象创建一个对象G(这个对象就是Generator类
实例对象),然后才会使用对象W(注意不是新建的Generator类
实例对象去调用函数)调用Generator函数
去执行函数体代码,但是这里的调用不是平常的函数调用,JS解释器从Generator函数
实例对象的内存中取出字符串形式的函数体后,解析字符串形式的函数体,解析成功会产生一个属于Generator函数
的上下文执行环境
,但是不会将这个新生上下文执行环境
加入上下文执行环境栈
(压栈)中从而执行函数体代码(此时产生了函数暂停执行的效果),而是将这个上下文执行环境
挂靠在对象G上,然后将对象G作为Generator函数
的返回值。function* He(){ console.log('He'); } He(); // He()调用的返回值 // He { // 谷歌浏览器控制台上的显示 // [[GeneratorStatus]]: "suspended", // [[GeneratorFunction]]: ƒ* He(), // [[GeneratorReceiver]]: Window // }
Generator函数
被某个对象调用后,返回的是一个Generator类
实例对象,这个实例对象通过浏览器控制台的显示可以大致将其包含的信息分为三个部分:
- [[GeneratorStatus]]:这个内部属性的值是
Generator函数
的执行状态,其有三个值,分别是suspended
(属于函数的上下文执行环境
不在上下文执行环境栈
中,而是移出执行栈中并被挂靠冻结了,代表着函数体暂停执行)、running
(属于函数的上下文执行环境
重新加入到上下文执行环境栈
并执行,代表着函数体正在执行中)、closed
(函数执行结束,无法在使用Generator类
实例对象去重新执行函数) - [[GeneratorFunction]]:这个内部属性的值指向了被调用的
Generator函数
- [[GeneratorReceiver]]:这个内部属性的值指向了调用
Generator函数
的某个对象
function* generator(){
// 此处打了个断点,可以清楚知道g1这个 Generator类 实例对象上挂靠的 Generator函数 此时的状态
console.log(g1); // {[[GeneratorStatus]]: "running", [[GeneratorFunction]]: ƒ* generator(), [[GeneratorReceiver]]: obj{a:1,b:2,c:3},}
debugger;
yield 1;
console.log('Generator');
yield 2;
}
var obj = {
a:1,b:2,c:3};
var g1 = generator.call(obj);
// g1是一个 Generator类 的实例对象,这个对象上挂靠了一个处于暂停执行状态下的 Gnerator函数
g1; // {[[GeneratorStatus]]: "suspended", [[GeneratorFunction]]: ƒ* generator(), [[GeneratorReceiver]]: obj{a:1,b:2,c:3},}
// 当调用了 Generator类 的实例对象的next方法后,挂靠在对象上的处于暂停执行状态下的 Gnerator函数会重新执行
g1.next(); // {value: 1, done: false}
g1.next(); // {value: 2, done: false}
g1.next(); // {value: undefined, done: true}
// 当 Generator函数 中的函数体代码都执行完成后,在此查看挂靠在g1对象上的 Generator函数 的状态已经是执行结束了
g1; // {[[GeneratorStatus]]: "closed", [[GeneratorFunction]]: ƒ* generator(), [[GeneratorReceiver]]: obj{a