【问题标题】:ES6: call class constructor without new keywordES6:调用类构造函数而不使用 new 关键字
【发布时间】:2015-08-21 18:41:15
【问题描述】:

给定一个简单的类

class Foo {
  constructor(x) {
    if (!(this instanceof Foo)) return new Foo(x);
    this.x = x;
  }
  hello() {
    return `hello ${this.x}`;
  }
}

是否可以在没有new关键字的情况下调用类构造函数?

使用应该允许

(new Foo("world")).hello(); // "hello world"

或者

Foo("world").hello();       // "hello world"

但后者失败了

Cannot call a class as a function

【问题讨论】:

  • 请记住,解决方法是只定义一个工厂函数(名称略有不同),它只执行return new Foo(arg);
  • 是的,我考虑过这个,但是构造函数名称和类名称之间存在不对称:{
  • 有趣。 JS 程序员已经习惯于用“new”来调用构造函数。节省一些输入,有时使代码看起来更优雅,并且是错误和混乱的一大来源。看看这种做法在几年后会发生怎样的变化会很有趣。
  • @user949300 我几乎总是使用new 关键字。我打算将其用于其他用途。
  • @user949300 我已经放弃了 javascript 以支持 coffeescript。 ES6 和 ES7 包含很多天才特性,但它的新 the ugliest 语法令人恐惧。而且新的关键字也丑得要命。只需将Foo().bar() 代码与(new Foo()).bar() 进行比较。糟透了。创建新对象如此重要的是什么?创建对象是家常便饭,我不需要特殊的语法。

标签: javascript constructor ecmascript-6 instance


【解决方案1】:

类有一个“类主体”,是一个构造函数
如果您使用内部 constructor() 函数,该函数也将是相同的类主体,并且是在调用类时调用的函数,因此类始终是构造函数。

构造函数需要使用new 运算符来创建新实例,因此调用没有new 运算符的类会导致错误,因为类构造函数需要创建一个新实例。

错误信息也很具体,而且正确

TypeError:没有'new'就不能调用类构造函数

你可以;

  • 要么使用常规函数而不是类1
  • 始终使用new 调用课程。
  • 在包装常规函数中调用类,始终使用new,这样您就可以获得类的好处,但包装函数仍然可以在有或没有new 运算符的情况下调用2.

1)

function Foo(x) {
    if (!(this instanceof Foo)) return new Foo(x);
    this.x = x;
    this.hello = function() {
        return this.x;
    }
}

2)

class Foo {
    constructor(x) {
        this.x = x;
    }
    hello() {
        return `hello ${this.x}`;
    }
}

var _old = Foo;
Foo = function(...args) { return new _old(...args) };

【讨论】:

  • 在接下来的版本中会增加调用构造函数:class Cat { call constructor(){ new Cat() } }
  • 当前节点(v9.4.0)似乎没有正确支持参数传播运算符,这给我带来了问题。我根据另一个答案中提到的经典装饰器的转译输出制作了一个版本。 ``` function bindNew(Class) { function _Class() { for ( var len = arguments.length, rest = Array(len), key = 0; key
  • @Maxmaxmaximus 我认为您应该将其发布为答案并添加来源。这对我来说是新闻而且非常有趣。
  • 虽然该示例有效,但如果有人尝试class Bar extends Foo {},它将中断,因为它将不再扩展预期的类。
【解决方案2】:

正如其他人指出的那样,ES2015 规范严格规定此类调用应抛出 TypeError,但同时它提供了可用于准确实现所需结果的功能,即Proxies

代理允许我们虚拟化一个对象的概念。例如,它们可以用来改变特定对象的某些行为而不影响其他任何东西。

在您的特定用例中,class Foo 是可以调用的Function object——这通常意味着该函数的主体将被执行。但这可以用Proxy改变:

const _Foo = new Proxy(Foo, {
  // target = Foo
  apply (target, thisArg, argumentsList) {
    return new target(...argumentsList);
  }
});

_Foo("world").hello(); 
const f = _Foo("world");
f instanceof Foo; // true
f instanceof _Foo; // true

(请注意,_Foo 现在是您要公开的类,因此标识符应该反过来)

如果由支持代理的浏览器运行,调用_Foo(...) 现在将执行apply 陷阱函数,而不是原来的构造函数。

同时,这个“新”_Foo 类与原始Foo 没有区别(除了能够将其作为普通函数调用之外)。同样,您可以通过Foo_Foo 判断创建的对象没有区别。

最大的缺点是it cannot be transpilled or pollyfilled,但它仍然是未来在 JS 中应用类似 Scala 的类的可行解决方案。

【讨论】:

  • 这是唯一可行的解​​决方案。在某些情况下,所有其他答案都不起作用。惊讶于 StackOverflow 评级系统的不准确程度,这是列表底部唯一正确的答案。
  • @wandalen - 这显然不是 only 有效的答案,事实上该问题的正确答案只是 “不,这是不可能的” .这是一个不同的答案,它使用代理而不是使用new 创建的实例,这是一种处理问题的好方法。
  • 如果类是先声明的,那么你不需要为Proxy和类使用不同的名字。 class Foo {}; const Foo = new Proxy(Foo, {apply(target, thisArg, args) { return new target(...args) }})。但是,Foo 现在引用 Proxy 而不是原来的类。
【解决方案3】:

这是我遇到的一个对我很有帮助的模式。它不使用class,但也不需要使用new。双赢。

const Foo = x => ({
  x,
  hello: () => `hello ${x}`,
  increment: () => Foo(x + 1),
  add: ({x: y}) => Foo(x + y)
})

console.log(Foo(1).x)                   // 1
console.log(Foo(1).hello())             // hello 1
console.log(Foo(1).increment().hello()) // hello 2
console.log(Foo(1).add(Foo(2)).hello()) // hello 3

【讨论】:

  • 这值得加分。我真的很想知道在 JS 中添加class 是否是一种改进。这显示了 JS 代码应该是什么样子。对于想知道为什么在任何地方都没有this 的人,创建的对象只是使用传递给“构造函数”(箭头函数)的x。每当需要对其进行变异时,它都会返回一个 new 对象。对象是不可变的。
  • 我想知道它是否会将函数优化到原型中,或者它是否会为每个对象创建新函数。也许Object.freeze 会优化?
  • 它将创建新功能
  • javascript 没有接口;我不知道你在说什么
  • 技术的问题是每次调用 Foo 时,都必须重新创建所有方法。对于类,prototype 方法可以在实例之间有效地共享,而不必为每个实例重新创建。因为方法是重新创建的,所以也会占用更多的内存。出于生产目的,最好使用类似于 Tim 的答案并使用方法来创建新类。
【解决方案4】:

不,这是不可能的。使用class 关键字创建的构造函数只能用new 构造,如果它们是[[call]]ed 而没有它们总是throwTypeError1(甚至没有办法从外部检测)。
1:我不确定转译器是否正确

不过,您可以使用普通函数作为解决方法:

class Foo {
  constructor(x) {
    this.x = x;
  }
  hello() {
    return `hello ${this.x}`;
  }
}
{
  const _Foo = Foo;
  Foo = function(...args) {
    return new _Foo(...args);
  };
  Foo.prototype = _Foo.prototype;
}

免责声明:instanceof 和扩展 Foo.prototype 正常工作,Foo.length 不能,.constructor 和静态方法不能,但如果需要,可以通过添加 Foo.prototype.constructor = Foo;Object.setPrototypeOf(Foo, _Foo) 来修复。

要使用class Bar extends Foo … 子类化Foo(不是_Foo),您应该使用return Reflect.construct(_Foo, args, new.target) 而不是new _Foo 调用。无法以 ES5 样式(使用Foo.call(this, …))进行子类化。

【讨论】:

  • 这是唯一适合我想要的解决方案,因为创建了一个动态类层次结构(mixins 定义为类而不是函数),我需要能够用子类实例化基类类的原型。
【解决方案5】:

我刚刚为你制作了这个 npm 模块;)

https://www.npmjs.com/package/classy-decorator

import classy from "classy-decorator";
 
@classy()
class IamClassy {
    constructor() {
        console.log("IamClassy Instance!");
    }
}
 
console.log(new IamClassy() instanceof IamClassy());  // true 
 
console.log(IamClassy() instanceof IamClassy());  // true 

【讨论】:

  • 答题史上最被低估的答案之一。
【解决方案6】:
class MyClass {

  constructor(param) {
     // ...
  }

  static create(param) {
    return new MyClass(param);
  }

  doSomething() {
    // ...
  }

}

MyClass.create('Hello World').doSomething();

这是你想要的吗?

如果您在创建MyClass 的新实例时需要一些逻辑,那么实现“CreationStrategy”可能会有所帮助,以超越逻辑(例如具有验证的复杂构建器逻辑)

编辑:正如 cmets 中所讨论的,在 JavaScript 中使用单独的类创建某种 Builder 模式是没有意义的。删除了相关示例。

【讨论】:

  • 提示:只有静态成员的类不应该是classes,而是普通对象。如果只有一个成员,他们甚至根本不应该。
  • 如果您没有注意到:我说的是您的Strategy 课程。我希望您不提倡将create 设为这些实例方法? static 完全没问题。
  • 在JavaScript中,如果你需要做一件事,你就去做。您不需要为此编写一个类并为其创建一个实例。那是可笑的膨胀。只需使用一个简单的函数。
  • 因为声明 class 只是为了创建一个函数(并将其称为“方法”)不组织代码。只需声明函数。不要仅仅因为 ES6 特性或者它们使你的代码看起来像 Java 就使用 ES6 特性。
  • 在这种特殊情况下,创建逻辑属于属于类,我看不出有任何外包任何东西的理由。只需将其留在 static create 方法中即可。
【解决方案7】:

您可以在这里使用“范围安全构造函数” 观察这段代码:

function Student(name) {
  if(this instanceof Student) {
    this.name = name;
  } else {
    return new Student(name);
  }
}

现在您可以在不使用 new 的情况下创建 Student 对象,如下所示:

var stud1 = Student('Kia');

【讨论】:

    【解决方案8】:

    the draft中挖出这个

    使用类定义语法定义的构造函数在作为函数调用时抛出

    所以我猜这对于类是不可能的。

    【讨论】:

      【解决方案9】:

      在重构代码时手动调用类构造函数会很有用(部分代码在 ES6 中,其他部分是函数和原型定义)

      我最终得到了一个小而有用的样板,将构造函数分割成另一个函数。期间。

       class Foo {
        constructor() {
          //as i will not be able to call the constructor, just move everything to initialize
          this.initialize.apply(this, arguments)
        }
      
        initialize() {
          this.stuff = {};
          //whatever you want
        }
       }
      
        function Bar () {
          Foo.prototype.initialize.call(this); 
        } 
        Bar.prototype.stuff = function() {}
      

      【讨论】:

        【解决方案10】:

        我在扩展使用其他答案中提到的转换函数转换的类时遇到问题。问题似乎是该节点(从 v9.4.0 开始)不正确支持参数扩展运算符 ((...args) =>)。

        这个基于经典装饰器(在another answer 中提到)的转译输出的函数对我有用,不需要支持装饰器或参数扩展运算符。

        // function that calls `new` for you on class constructors, simply call
        // YourClass = bindNew(YourClass)
        function bindNew(Class) {
          function _Class() {
            for (
              var len = arguments.length, rest = Array(len), key = 0;
              key < len;
              key++
            ) {
              rest[key] = arguments[key];
            }
        
            return new (Function.prototype.bind.apply(Class, [null].concat(rest)))();
          }
          _Class.prototype = Class.prototype;
          return _Class;
        }
        

        用法:

        class X {}
        X = bindNew(X);
        
        // or
        
        const Y = bindNew(class Y {});
        
        const x = new X();
        const x2 = X(); // woohoo
        
        x instanceof X; // true
        x2 instanceof X; // true
        
        class Z extends X {} // works too
        

        作为奖励,TypeScript(带有“es5”输出)似乎适用于旧的instanceof 技巧(好吧,如果在没有new 的情况下使用它不会进行类型检查,但无论如何它仍然有效):

        class X {
          constructor() {
            if (!(this instanceof X)) {
              return new X();
            }
          }
        }
        

        因为它会将其编译为:

        var X = /** @class */ (function () {
            function X() {
                if (!(this instanceof X)) {
                    return new X();
                }
            }
            return X;
        }());
        

        【讨论】:

          【解决方案11】:

          好的,我这里有另一个答案,我认为这个答案很有创意。

          基本上,与 Naomik 的回答类似的问题在于,每次将方法链接在一起时都会创建函数。

          编辑:这个解决方案有同样的问题,但是,这个答案被保留用于教育目的。

          因此,我在这里提供了一种仅将新值绑定到您的方法的方法——这些方法基本上只是独立的函数。这提供了额外的好处,即能够将不同模块中的函数导入到新构建的对象中。

          好的,那就这样吧。

          const assoc = (prop, value, obj) => 
            Object.assign({},obj,{[prop]: value})
          
          const reducer = ( $values, accumulate, [key,val] ) => assoc( key, val.bind( undefined,...$values ), accumulate )
          
          const bindValuesToMethods = ( $methods, ...$values ) => 
            Object.entries( $methods ).reduce( reducer.bind( undefined, ...$values), {} )
          
          const prepareInstance = (instanceMethods, staticMethods = ({}) ) => Object.assign(
            bindValuesToMethods.bind( undefined, instanceMethods ),
            staticMethods
          )
          
          // Let's make our class-like function
          
          const RightInstanceMethods = ({
            chain: (x,f) => f(x),
            map: (x,f) => Right(f(x)),
            fold: (x,l,r) => r(x),
            inspect: (x) => `Right(${x})`
          })
          
          const RightStaticMethods = ({
            of: x => Right(x)
          })
          
          const Right = prepareInstance(RightInstanceMethods,RightStaticMethods)
          

          现在你可以做

          Right(4)
            .map(x=>x+1)
            .map(x=>x*2)
            .inspect()
          

          你也可以这样做

          Right.of(4)
            .map(x=>x+1)
            .map(x=>x*2)
            .inspect()
          

          您还可以从这样的模块中导出额外的好处

          export const Right = prepareInstance(RightInstanceMethods,RightStaticMethods)
          

          虽然您没有得到ClassInstance.constructor,但您确实有FunctorInstance.name(注意,您可能需要填充Function.name 和/或不使用箭头函数进行导出以使浏览器与Function.name 兼容)

          export function Right(...args){
            return prepareInstance(RightInstanceMethods,RightStaticMethods)(...args)
          }
          

          PS - 欢迎为 prepareInstance 提供新名称建议,请参阅要点。

          https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456

          【讨论】:

          • 我认为我看到了一个可修复的问题,但我可能是错的。每次我们应用 Right(例如Right(1)Right(2))时,都会调用Object.entries($methods).reduce 位。我想你打算只执行一次这个减少。对吗?
          • @naomik 谢谢!嗯...您仍然需要将仿函数容器中的新值绑定到您返回的仿函数上的方法吗?我只是通过将 reducer 放在 reduce 函数之外来优化代码,以防止每次调用都重新创建它。
          • Hmm 确实......但从根本上讲这是有道理的:只需使用map: (x,f) =&gt; Right(f(x)),如果x 将代表不同的值,@987654336 @ 必须用该值重新binded。重新绑定创建了一个新函数,所以我们又回到了同一条船上。
          • 我刚刚读了一些书——你说得对,我会更新我的答案——在一个绑定被优化为仅部分应用于函数而不重新创建它的世界中,也许这段代码将成为新时尚:-)
          • 我将尝试一下。每次我们构造一个新值时,您的编辑仍会调用Object.entries( $methods ).reduce(。绑定会延迟评估,因此您必须以不同的方式解决这个问题。感谢分享这个有趣的练习。
          【解决方案12】:

          正如你和其他人指出的那样

          Foo("world").hello();  
          

          因错误而失败,因为它是一个错误, 根据 ES6 语法规则。

          其他人指出

           (new Foo("world")).hello();
          

          有效但笨拙,因为

          • 它需要“新的”AND
          • 它需要额外的括号。

          我同意它很笨重。所以我经常使用 这个解决方案:

          1. 在你的类 Foo 中,创建一个静态方法 命名为“新”:

             static new (...args)
             { return new this (...args);
             } 
            
          2. 像这样使用它:

              Foo.new("world").hello();
            

          这样我隐藏了里面的“笨拙” 这个静态方法'new()'。

          注意这个方法 new() 是通用的, 它也可以正常工作 当继承到子类时。如果你需要 要在子类中自定义它,您可以先调用:

          super.new(...args) 
          

          然后在 子类中的方法,然后返回其结果。

          【讨论】:

            【解决方案13】:

            重述的适用于 ES6 的“单行”解决方案:解释

            上面Bergi的回答基本正确。

            TLDR;跳到最后 ? 单线解决方案

            Bergi 的答案在阅读时可能看起来不清楚。所以,这里有一个更扩展的代码示例,它说明了两个新的 ES6 特性来实现预期的目标。

            他们一起让单个函数 C(下) 提供 工厂new-able fn 的双重角色;它构造了一个派生自AB inst。

            B 构造函数利用super 处理来调用带有初始化参数的A 构造函数。 在我们由C 构建的最后 #3 - #4 示例中

            A 构造函数演示了new.target 伪变量的语义,以发现new 实际上是用B 调用的。

            首先,我们将使用 ES6 new.target psuedo-var,它为我们提供了 RHSnew RHS() 表达式。

            从技术上讲,我们可以得到 new.targetthis?.__proto__?.constructor;它们是等价的。

            其次,我们将使用 ES6 Reflect.construct这对于解决 ES6 类构造函数调用约束至关重要;如果我们有约束力并决心不使用new RHS(...)

            测试以下内容并亲自查看它的输出(也在下面的#1-4 中提供)。

            class A {
              constructor(...a) {
                const descendentType = new.target;
                console.log(`A's constructor seeing 'new' invoked on ${descendentType?.name} with args: %o`,a);
              }
            }
            class B extends A {
              constructor(...a) {
                super(...a);
              }
            }
            // C is our DUAL mode Factory
            function C(...a) {
              console.log(`C's new.target => ${new.target?.name}`);
              const inst = new.target ? Reflect.construct(B, a) : new B(...a);
              console.log(`C has constructed a ${inst.__proto__.constructor.name} inst`);
              return inst;
            }
            

            然后我们可以通过以下方式调用它:

            1. new A('NEW-A()')
              • 输出 => "A 的构造函数看到在 A 上使用 args 调用了 'new':['NEW-A()']"
            2. new B('NEW-B()')
              • 输出 => "A 的构造函数看到在 B 上调用了带有 args 的 'new':['NEW-B()']"
            3. new C('NEW-C()')
              • 输出 => "C's new.target => C"
              • 输出 => "A 的构造函数看到在 B 上调用了带有 args 的 'new':['NEW-C()']"
              • 输出 => "C 构造了一个 B inst"
            4. C('PLAIN-C()')
              • 输出 => "C 的 new.target => 未定义"
              • 输出 => "A 的构造函数看到在 B 上使用 args 调用的 'new':['PLAIN-C()']"
              • 输出 => "C 构造了一个 B inst"

            #3 和 #4 实现了最初期望的目标。

            简化的`C`看起来像:

            function C(...a) {return Reflect.construct(B, a);}
            

            OR - 如果Reflect.construct 的第三个参数未用于初始化。

            function C(...a) {return new B(...a);}
            

            注意:C 必须是 函数 而不是 class,这样既允许这样做,又可以在 new C() 调用上返回备用 this,等等。

            为了规避arguments.callee严格模式规则,还需要使用闭包(smalltalk-block。如下图所示:

            class B extends A {
              // embedding within a class and generically referencing it requires =>
              static C = (() => {
                const $class = this; return function(...a) {
                return Reflect.construct($class, a);}})();
              // Read more on `Reflect.construct` 3rd argument to see more capabilities
              // for why it does MORE than just `new $class(...a)` would do.
            }
            exports.C = B.C;
            

            ⛐⚠️⛐ 你可以做一些糟糕的事情,比如在结果 inst 上修改 __proto__ 并更改其 constructorname。这将使它看起来和感觉像B 的真正子类C,具体取决于您想要操作对象模型的程度。在getters/setterssuper# 私人事件中的微妙之处比比皆是。但在大部分情况下,您可以保持 ES6 清洁,并巧妙地使用 extends 并提供 template 超类扁平混合树;我在 efekt 中做了很多工作,以支持微小但完整的 µhtml 反应式自定义元素部件和相关的 PWA 应用程序模型和响应式动态即时版本化代码从 EdgeS ESS 后端服务器捆绑。如...const M = $class =&gt; class extends $class {...}

            我的动机...

            我发布这篇文章是为了帮助解释语义和一个有效的 ES6 解决方案,这是我用来支持子类化 Promise 以提供 FutureValue 更好的 workflow 处理能力我的 github efekt(EdgeS 前端工具包库)

            【讨论】:

              【解决方案14】:

              不能调用没有new 关键字的类构造函数。

              错误信息非常具体。

              2alityspec 上查看博客文章:

              However, you can only invoke a class via new, not via a function call (Sect. 9.2.2 in the spec):
              
                  > Point()
                  TypeError: Classes can’t be function-called
              

              【讨论】:

                【解决方案15】:

                我将此添加为对 naomik 评论的跟进,并利用 Tim 和 Bergi 说明的方法。我还将建议将of 函数用作一般情况。

                要以一种功能性的方式做到这一点并利用原型的效率(不是每次创建新实例时都重新创建所有方法),可以使用这种模式

                const Foo = function(x){ this._value = x ... }
                Foo.of = function(x){ return new Foo(x) }
                Foo.prototype = {
                  increment(){ return Foo.of(this._value + 1) },
                  ...
                }
                

                请注意,这与fantasy-land JS 规范一致

                https://github.com/fantasyland/fantasy-land#of-method

                个人觉得使用ES6类语法更简洁

                class Foo {
                  static of(x) { new Foo(x)}
                  constructor(x) { this._value = x }
                  increment() { Foo.of(this._value+1) }
                }
                

                现在可以将它封装在一个闭包中

                class Foo {
                  static of(x) { new _Foo(x)}
                  constructor(x) { this._value = x }
                  increment() { Foo.of(this._value+1) }
                }
                
                
                function FooOf (x) {
                
                    return Foo.of(x)
                
                }
                

                或者根据需要重命名FooOfFoo,即类可以是FooClass而函数只是Foo等。

                这比将类放在函数中要好,因为创建新实例也不会给我们带来创建新类的负担。

                另一种方法是创建一个of 函数

                const of = (classObj,...args) => (
                  classObj.of 
                    ? classObj.of(value) 
                    : new classObj(args)
                )
                

                然后执行of(Foo,5).increment()之类的操作

                【讨论】:

                • 在您的第三个示例中,我看到:static of(x) { new _Foo(x)} ... 下划线的目的是什么?抱歉,如果我在这里遗漏了一些明显的东西。谢谢你的例子!
                【解决方案16】:

                仍在寻找有趣的方法来使用 instanceof,而不依赖于 newclass 关键字。在这个示例程序中,我们在不到一秒的时间内计算出第 100,000 个斐波那契数。结果长度超过 20,000 位 -

                const fib = x =>
                  Loop                                // <- no `new`
                    ( (n, a, b) =>
                        n <= 0n
                          ? String(a)                 // <- no `new`
                          : Recur(n - 1n, b, a + b)   // <- no `new`
                    , BigInt(x)                       // <- no `new`
                    , 0n
                    , 1n
                    )
                  
                function Loop (f, ...init)
                { let r = f(...init)
                  while (r instanceof Recur) // <- instanceof works
                    r = f(...r)
                  return r
                }
                
                function Recur (...v)
                { return Object.create                // <- not a class, but works
                    ( Recur.prototype                 // <- set prototype
                    , { constructor: { value: Recur } // <- set constructor
                      , [Symbol.iterator]: { value: _ => v.values() } // <- whatever you want
                      }
                    )
                }
                
                document.body.textContent = fib(100000)
                body { overflow-wrap: anywhere; }

                我不知道为什么我以前没有想过这个 -

                function atom (T, v)
                { return Object.assign
                    ( Object.create
                        ( T.prototype
                        , { constructor: { value: T } }
                        )
                    , v
                    )
                }
                
                function pair (car, cdr)
                { return atom(pair, { car, cdr }) }
                
                const p = 
                  pair(1, 2)
                
                console.log(p)
                console.log(p instanceof pair)

                输出 -

                {
                  "car": 1,
                  "cdr": 2
                }
                true
                

                【讨论】:

                • 在 1998 年的 QKS Smalltalk 引擎 [我写的] 上(此后未更改)今天在 2.60 GHz 戴尔笔记本电脑上运行:=> [100_000 fibonacci] 毫秒ToRun => 286 ms => 100_000 fibonacci asString 大小 => 20899 仅供参考:V8 诞生于 Animorphic Smalltalk 1994 (Lars Bak) => Java HotSpot => V8 JavaScript。如果您想了解 GC 和 BigInt 系统在 JavaScript 上的表现如何,请尝试运行“30000 阶乘”。 (附言,我在 Microsoft 担任了多年的首席 JavaScript 架构师)。
                • 令人印象深刻!我一直想学习smalltalk,但从来没有进入它。您对在现代 macos 环境中启动和运行 smalltalk 有什么建议吗?
                【解决方案17】:

                我写了一个小辅助函数来解决这个问题。它有效地将 ES6 类转换为不受相同规则集约束的旧 ES5 构造函数。这样您就可以创建不需要new 的构造函数。您还可以通过与内置 NumberString 等类似的方式重载构造函数。

                function callableConstructor(c, f) {
                  function ret(...args) {
                    if(new.target) {
                      return new c(...args)
                    }
                    return f(...args)
                  }
                  ret.prototype = c.prototype
                  ret.prototype.constructor = ret
                  return ret
                }
                

                在下面测试它:

                function callableConstructor(c, f) {
                  function ret(...args) {
                    if(new.target) {
                      return new c(...args)
                    }
                    return f(...args)
                  }
                  ret.prototype = c.prototype
                  ret.prototype.constructor = ret
                  return ret
                }
                
                // Usage
                class Foo {
                  constructor(a, b) {
                    this.a = a
                    this.b = 2 * b
                  }
                  f() {
                    return this.a + this.b
                  }
                }
                
                Foo = callableConstructor(Foo, (...args) => new Foo(...args))
                
                let foo = new Foo(2, 3)
                console.log(foo)                // Foo { a: 2, b: 6 }
                console.log(foo.f())            // 8
                console.log(foo instanceof Foo) // true
                foo = Foo(2, 3)
                console.log(foo)                // Foo { a: 2, b: 6 }
                console.log(foo.f())            // 8
                console.log(foo instanceof Foo) // true

                【讨论】:

                【解决方案18】:

                我遇到了这个问题,因为我遇到了 no-new“不要使用 new 产生副作用”的 eslint 规则 - 结果证明,将 new 用于立即丢弃的对象是一种不好的做法。

                我仍然想使用类语法,因为我喜欢它,但我同意带有 new 关键字的常规类对于不产生对象的东西可能会造成混淆。

                对我来说解决方案很简单。在模块中定义一个未导出的类,并导出一个将其实例化的函数。

                class SideEffects {
                  constructor() {
                  
                  }
                  // ...
                }
                
                export function addSideEffects() {
                  // eslint-disable-next-line no-new
                  new SideEffects();
                }
                

                是的,我们仍在使用new 关键字,但它在模块内部使用,从读取模块文件可以明显看出它不是常规类 - 导出的函数也清楚地表明它不会创建一个对象。

                【讨论】:

                  【解决方案19】:

                  这可能有点做作,但确实有效

                  function Foo(x){
                  "use strict"
                  
                      class Bar {
                          constructor(x) {
                              if (!(this instanceof Bar)) return new Bar(x);
                              this.x = x;
                          }
                  
                          hello() {
                              return `hello ${this.x}`;
                          }
                      }
                  
                      return new Bar(x)
                  }
                  
                  Foo("world").hello()
                  

                  【讨论】:

                  • 我很困惑,你为什么要检查instanceof,因为你甚至没有公开课程?这个答案并没有真正解决主要问题。
                  • 这也不起作用,因为Foo('world') instanceof Foo 返回false
                  【解决方案20】:

                  你不能使用没有新构造函数的类,在我的例子中,我不想在任何时候使用new 构造函数,所以你可以做的就是将你的类包装为如下(在我的例子中,它是一个 Dates utils 库):

                  const defaultOptions = {
                    defaultFormatOptions: 'dd/MM/yyyy'
                  }
                  
                  class DatesClass {
                    constructor(date = new Date(), options) {
                      this.date = date
                      this.options = { ...defaultOptions, ...options }
                    }
                    get value() {
                      return this.date
                    }
                    add() {}
                    ...
                  }
                  
                  export default (date, options) => new DateClass(date, options)
                  
                  
                  // then you can use it as follow
                  import dates from 'path/to/yourClass/from/above'
                  dates(new Date()).add({ unit: 'day', qty: 2}).value
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-01-22
                    相关资源
                    最近更新 更多