stage 0
MyPrototypeWhat opened this issue · 0 comments
MyPrototypeWhat commented
function-is-callable-is-constructor
https://github.com/caitp/TC39-Proposals/blob/trunk/tc39-reflect-isconstructor-iscallable.md
分为两个函数,分别是isCallable \ isConstructor
很简单,看下述注释就行
-
isCallable
core-js/packages/core-js/modules/esnext.function.is-callable.js
// eslint-disable-next-line es-x/no-object-getownpropertydescriptor -- safe var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; var classRegExp = /^\s*class\b/; var exec = uncurryThis(classRegExp.exec); var isClassConstructor = function (argument) { try { // `Function#toString` throws on some built-it function in some legacy engines // (for example, `DOMQuad` and similar in FF41-) // 判断环境支持和排除class if (!DESCRIPTORS || !exec(classRegExp, inspectSource(argument))) return false; } catch (error) { /* empty */ } // 拿到prototype var prototype = getOwnPropertyDescriptor(argument, 'prototype'); // 判断prototype是否可写,普通函数的prototype是可写的,class相反 return !!prototype && hasOwn(prototype, 'writable') && !prototype.writable; }; // `Function.isCallable` method $({ target: 'Function', stat: true, sham: true, forced: true }, { isCallable: function isCallable(argument) { // $iCallable: typeof argument == 'function' return $isCallable(argument) && !isClassConstructor(argument); } });
-
isConstructor
core-js/packages/core-js/modules/esnext.function.is-constructor
core-js/packages/core-js/internals/is-constructor.js
var uncurryThis = require('../internals/function-uncurry-this'); // fails:通过执行try catch包裹返回入参函数执行结果,如果报错返回true var fails = require('../internals/fails'); var isCallable = require('../internals/is-callable'); // 相当于Object.prototype.toString.call.slice(argument,8,-1) var classof = require('../internals/classof'); // 获取内部函数 var getBuiltIn = require('../internals/get-built-in'); // inspectSource:返回argument.toString() var inspectSource = require('../internals/inspect-source'); var noop = function () { /* empty */ }; var empty = []; // 拿到Reflect.construct函数 var construct = getBuiltIn('Reflect', 'construct'); var constructorRegExp = /^\s*(?:class|function)\b/; var exec = uncurryThis(constructorRegExp.exec); var INCORRECT_TO_STRING = !constructorRegExp.exec(noop); var isConstructorModern = function isConstructor(argument) { if (!isCallable(argument)) return false; try { construct(noop, empty, argument); return true; } catch (error) { return false; } }; var isConstructorLegacy = function isConstructor(argument) { if (!isCallable(argument)) return false; switch (classof(argument)) { case 'AsyncFunction': case 'GeneratorFunction': case 'AsyncGeneratorFunction': return false; } try { // we can't check .prototype since constructors produced by .bind haven't it // `Function#toString` throws on some built-it function in some legacy engines // (for example, `DOMQuad` and similar in FF41-) // 某些环境,通过.bind函数生成的函数没有Function#toString值 // 如果argument是一个箭头函数,通过exec正则匹配后返回false return INCORRECT_TO_STRING || !!exec(constructorRegExp, inspectSource(argument)); } catch (error) { return true; } }; isConstructorLegacy.sham = true; // `IsConstructor` abstract operation // https://tc39.es/ecma262/#sec-isconstructor module.exports = !construct || fails(function () { // 可以看到通过判断环境支持分为两种模式, // 如果环境支持construct // isConstructorLegacy // isConstructorModern var called; return isConstructorModern(isConstructorModern.call) || !isConstructorModern(Object) || !isConstructorModern(function () { called = true; }) || called; }) ? isConstructorLegacy : isConstructorModern;
可以看到最后有很多判断,下面逐个分析下
isConstructorModern(isConstructorModern.call)
- 报错 错误信息
TypeError: function call() { [native code] } is not a constructor
- 返回
false
- 报错 错误信息
!isConstructorModern(Object)
- 返回
false
- 返回
!isConstructorModern(function () { called = true; })
- 通过
construct
api调用,入参的函数是不会被执行的,也就是called为undefined - 返回
false
- 通过
三种情况个人理解是为了判断js执行环境,排除
[native code]
函数、检测Object构造函数返回情况、检测入参函数是否会被调用