MyPrototypeWhat/take-down

stage 0

MyPrototypeWhat opened this issue · 0 comments

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; })
      • 通过 constructapi调用,入参的函数是不会被执行的,也就是called为undefined
      • 返回 false

    三种情况个人理解是为了判断js执行环境,排除[native code]函数、检测Object构造函数返回情况、检测入参函数是否会被调用