is-generator-function 源码
is-generator-function
源码(version: 1.0.7
)
'use strict';
var toStr = Object.prototype.toString;
var fnToStr = Function.prototype.toString;
var isFnRegex = /^\s*(?:function)?\*/;
var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
var getProto = Object.getPrototypeOf;
var getGeneratorFunc = function () { // eslint-disable-line consistent-return
if (!hasToStringTag) {
return false;
}
try {
return Function('return function*() {}')();
} catch (e) {
}
};
var generatorFunc = getGeneratorFunc();
var GeneratorFunction = generatorFunc ? getProto(generatorFunc) : {};
module.exports = function isGeneratorFunction(fn) {
if (typeof fn !== 'function') {
return false;
}
if (isFnRegex.test(fnToStr.call(fn))) {
return true;
}
if (!hasToStringTag) {
var str = toStr.call(fn);
return str === '[object GeneratorFunction]';
}
return getProto(fn) === GeneratorFunction;
};
解析
源码一:
var toStr = Object.prototype.toString;
使用Object.prototype.toString
来判断对象的类型,输出[object xxx]
,如对一个数组调用此方法
toStr.call([])
返回[object Array]
。jquery
里使用此方法来判断对象类型,但,ES2015
以后,这方法不再可靠,因为ES2015
为一些对象设置了Symbol.toStringTag
属性,此属性的值可以自定义,Object.prototype.toString
方法返回的值为Symbol.toStringTag
属性的值。
function Teacher(name) {
this.name = name
}
Teacher.prototype[Symbol.toStringTag] = 'Teacher'
const teacher = new Teacher('Jason')
console.log(teacher.toString()) //[object Teacher]
console.log(Object.prototype.toString.call(teacher)) //[object Teacher]
源码二:
var fnToStr = Function.prototype.toString;
函数类自定义了自己的toString
方法,此方法返回函数的原代码。
const fnToStr = Function.prototype.toString;
function fn(){return "function*"}
let obj = {
getName(){return 'http://www.tensweets.com'}
};
console.log(fnToStr.call(obj.getName));
//getName(){return 'http://www.tensweets.com'}
console.log(fnToStr.call(fn));
//function fn(){return "function*"}
对于一个generator
函数,在node
版本小于10时,Function.prototype.toString
总是返回function*
或*funName
的形式;但在node
版本大于等于10时,其返回的是原generator
函数原始内容的样子。
node
小于10:
const fnToStr = Function.prototype.toString;
let obj = {
*getName(){},
*getAge(){}
};
let fa = function* (){};
function* fb(){}
let fn = Function('return function *() {}')();
console.log(fnToStr.call(obj.getName));
//*getName(){}
console.log(fnToStr.call(obj.getAge));
//*getAge(){}
console.log(fnToStr.call(fa));
//function* (){}
console.log(fnToStr.call(fb));
//function* fb(){}
console.log(fnToStr.call(fn));
//function* () {}
node
大于等于10:
const fnToStr = Function.prototype.toString;
let obj = {
*getName(){},
*getAge(){}
};
let fa = function* (){};
function* fb(){}
let fn = Function('return function *() {}')();
console.log(fnToStr.call(obj.getName));
//*getName(){}
console.log(fnToStr.call(obj.getAge));
//* getAge(){}
console.log(fnToStr.call(fa));
//function* (){}
console.log(fnToStr.call(fb));
//function* fb(){}
console.log(fnToStr.call(fn));
//function * () {}
源码三:
var isFnRegex = /^\s*(?:function)?\*/;
用于匹配generator
函数通过Function.prototype.toString
执行后返回的字符串,表示的意思是:以0个或多个空格开头,后面紧接0个或1个不捕获的function
字符串,再后面紧接一个*
字符。
显然,当node>=10
时这个正则匹配不能完全匹配generator
函数的字符串原码(有function *
这种情况)。
源码四:
var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
这段代码码用于判断当前环境是否支持对象Symbol.toStringTag
属性,Object.prototype.toString
返回值不一定可靠了。
var getProto = Object.getPrototypeOf;
取得对象原型的函数。
源码五:
var getGeneratorFunc = function () { // eslint-disable-line consistent-return
if (!hasToStringTag) {
return false;
}
try {
return Function('return function*() {}')();
} catch (e) {
}
};
定义一个函数,如果环境不支持Symbol.toStringTag
返回false
,反之试图返回一个generator
函数。
var generatorFunc = getGeneratorFunc();
var GeneratorFunction = generatorFunc ? getProto(generatorFunc) : {};
如果不支持Symbol.toStringTag
或者无法创建generator
函数GeneratorFunction
值为空对象{}
,反之GeneratorFunction
值为新建generator
函数的原型。
源码六:
module.exports = function isGeneratorFunction(fn) {
if (typeof fn !== 'function') {
return false;
}
if (isFnRegex.test(fnToStr.call(fn))) {
return true;
}
if (!hasToStringTag) {
var str = toStr.call(fn);
return str === '[object GeneratorFunction]';
}
return getProto(fn) === GeneratorFunction;
};
模块导出为一个函数,接收一个要检测的参数。
- 如果参数不是函数类型,肯定也不是
generator
函数。 - 参数是函数了,将这函数换成字符串,如果字符串以
*
、function*
开头,则是一个generator
函数,个判断是很不严谨的, 因为function *
无法匹配 - 如果上面的方法不行, 就尝试利用
toString
的方法,当环境不支持Symbol.toStringTag
时Function.prototype.toString
方法返回的类型字符串是可信的,对于一个generator
函数,其执行Function.prototype.toString
方法,返回的是[object GeneratorFunction]
字符串。 - 上面还是没结果的话,判断参数函数的原型跟我们创建的
generator
函数的原型是否指向同一个对象。
以下是在node>=10
的环境测试输出的结果
var toStr = Object.prototype.toString;
var fnToStr = Function.prototype.toString;
var isFnRegex = /^\s*(?:function)?\*/;
var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
var getProto = Object.getPrototypeOf;
var getGeneratorFunc = function () { // eslint-disable-line consistent-return
if (!hasToStringTag) {
return false;
}
try {
return Function('return function*() {}')();
} catch (e) {
}
};
var generatorFunc = getGeneratorFunc();
var GeneratorFunction = generatorFunc ? getProto(generatorFunc) : {};
function isGeneratorFunction(fn) {
if (typeof fn !== 'function') {
console.log("11")
return false;
}
if (isFnRegex.test(fnToStr.call(fn))) {
console.log("22")
return true;
}
if (!hasToStringTag) {
console.log("33")
var str = toStr.call(fn);
return str === '[object GeneratorFunction]';
}
console.log("44")
return getProto(fn) === GeneratorFunction;
};
let fn = Function('return function *() {}')();
console.log(fnToStr.call(fn));
console.log(isGeneratorFunction(fn));
function *() {}
44
true