Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stage 0 #24

Open
MyPrototypeWhat opened this issue Apr 6, 2022 · 0 comments
Open

stage 0 #24

MyPrototypeWhat opened this issue Apr 6, 2022 · 0 comments

Comments

@MyPrototypeWhat
Copy link
Owner

MyPrototypeWhat commented Apr 6, 2022

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构造函数返回情况、检测入参函数是否会被调用

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant