Javascript ES6:不使用 new 关键字调用类构造函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30689817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ES6: call class constructor without new keyword
提问by Thank you
Given a simple class
给定一个简单的类
class Foo {
constructor(x) {
if (!(this instanceof Foo)) return new Foo(x);
this.x = x;
}
hello() {
return `hello ${this.x}`;
}
}
Is it possible to call the class constructor without the newkeyword?
是否可以在没有new关键字的情况下调用类构造函数?
Usage should allow
使用应该允许
(new Foo("world")).hello(); // "hello world"
Or
或者
Foo("world").hello(); // "hello world"
But the latter fails with
但后者失败了
Cannot call a class as a function
回答by adeneo
Classes have a "class body" that is a constructor.
If you use an internal constructor()function, that function would be the same class body as well, and would be what is called when the class is called, hence a class is always a constructor.
类有一个“类体”,它是一个构造函数。
如果您使用内部constructor()函数,则该函数也将是相同的类主体,并且将是调用类时调用的函数,因此类始终是构造函数。
Constructors requires the use of the newoperator to create a new instance, as such invoking a class without the newoperator results in an error, as it's requiredfor the class constructor to create a new instance.
构造函数需要使用的new运营商没有创建一个新的实例,因此调用类new中的错误操作的结果,因为它需要的类的构造函数来创建一个新的实例。
The error message is also quite specific, and correct
错误信息也很具体,而且是正确的
TypeError: Class constructors cannot be invoked without 'new'
类型错误:没有“new”就不能调用类构造函数
You could;
你可以;
- either use a regular function instead of a class1.
- Always call the class with
new. - Call the class inside a wrapping regular function, always using
new, that way you get the benefits of classes, but the wrapping function can still be called with and without thenewoperator2.
- 要么使用常规函数而不是类1。
- 总是用 调用类
new。 - 在包装常规函数内调用类,始终使用
new,这样您就可以获得类的好处,但仍然可以使用和不使用new运算符2来调用包装函数。
1)
1)
function Foo(x) {
if (!(this instanceof Foo)) return new Foo(x);
this.x = x;
this.hello = function() {
return this.x;
}
}
2)
2)
class Foo {
constructor(x) {
this.x = x;
}
hello() {
return `hello ${this.x}`;
}
}
var _old = Foo;
Foo = function(...args) { return new _old(...args) };
回答by Adam Ples
As others have pointed out ES2015 spec strictly states that such call should throw TypeError, but at the same time it provides feature that can be used to achieve exactly the desired result, namely Proxies.
正如其他人指出的那样,ES2015 规范严格规定这样的调用应该抛出 TypeError,但同时它提供了可用于实现准确预期结果的功能,即Proxies。
Proxies allows us to virtualize over a concept of an object. For instance they can be used to change some behaviour of particular object without affecting anything else.
代理允许我们对对象的概念进行虚拟化。例如,它们可用于更改特定对象的某些行为而不影响其他任何内容。
In your specific use case class Foois Function objectwhich can be called -- this normally means that body of this function will be executed. But this can be changed with Proxy:
在您的特定用例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
(Note that _Foois now the class you want to expose, so identifiers should probably be the other way round)
(请注意,_Foo现在是您要公开的类,因此标识符可能应该是相反的)
If run by browser that support Proxies, calling _Foo(...)will now execute applytrap function instead of the orignal constructor.
如果由支持代理的浏览器运行,调用_Foo(...)现在将执行apply陷阱函数而不是原始构造函数。
At the same time this "new" _Fooclass is indistinguishable from original Foo(apart from being able to call it as a normal function). Similarily there is no difference by which you can tell object created with Fooand _Foo.
同时,这个“新”_Foo类与原始类没有区别Foo(除了能够将其作为普通函数调用之外)。类似地,您可以分辨出用Foo和创建的对象没有区别_Foo。
The biggest downside of this is that it cannot be transpilled or pollyfilled, but still its viable solution for having Scala-like class apply in JS in the future.
最大的缺点是它不能被 transpilled 或 pollyfilled,但它仍然是将来在 JS 中应用类 Scala 类的可行解决方案。
回答by Thank you
Here's a pattern I've come across that really helps me. It doesn't use a class, but it doesn't require the use of neweither. Win/Win.
这是我遇到的一个真正对我有帮助的模式。它不使用 a 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
回答by Bergi
No, this is not possible. Constructors that are created using the classkeyword can only be constructed with new, if they are [[call]]edwithout they always throwa TypeError1(and there's not even a way to detect this from the outside).
1: I'm not sure whether transpilers get this right
不,这是不可能的。正在使用所创建的构造class关键字只能与构造new,如果它们是[[呼叫]] ED而不他们总是throw一个TypeError1(和甚至有不从外部检测到该一个方式)。
1:我不确定转译器是否正确
You can use a normal function as a workaround, though:
不过,您可以使用普通函数作为解决方法:
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;
}
Disclaimer: instanceofand extending Foo.prototypework as normal, Foo.lengthdoes not, .constructorand static methods do not but can be fixed by adding Foo.prototype.constructor = Foo;and Object.setPrototypeOf(Foo, _Foo)if required.
免责声明:instanceof和扩展Foo.prototype工作正常,Foo.length不会,.constructor静态方法不会但可以通过添加Foo.prototype.constructor = Foo;和Object.setPrototypeOf(Foo, _Foo)如果需要来修复。
For subclassing Foo(not _Foo) with class Bar extends Foo …, you should use return Reflect.construct(_Foo, args, new.target)instead of the new _Foocall. Subclassing in ES5 style (with Foo.call(this, …)) is not possible.
对于使用 进行子类化Foo(不是_Foo)class Bar extends Foo …,您应该使用return Reflect.construct(_Foo, args, new.target)而不是new _Foo调用。ES5 风格的子类化(使用Foo.call(this, …))是不可能的。
回答by Tim
class MyClass {
constructor(param) {
// ...
}
static create(param) {
return new MyClass(param);
}
doSomething() {
// ...
}
}
MyClass.create('Hello World').doSomething();
Is that what you want?
那是你要的吗?
If you need some logic when creating a new instance of MyClass, it could be helpful to implement a "CreationStrategy", to outsorce the logic:
如果在创建 的新实例时需要一些逻辑MyClass,那么实现“CreationStrategy”以超出逻辑可能会有所帮助:
class MyClassCreationStrategy {
static create(param) {
let instance = new MyClass();
if (!param) {
// eg. handle empty param
}
instance.setParam(param);
return instance;
}
}
class DefaultCreationStrategy {
static create(classConstruct) {
return new classConstruct();
}
}
MyClassCreationStrategy.create(param).doSomething();
DefaultCreationStrategy.create(MyClass).doSomething();
回答by Fareed Alnamrouti
i just made this npm module for you ;)
我刚刚为你制作了这个 npm 模块;)
https://www.npmjs.com/package/classy-decorator
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
回答by Joseph
回答by user2922935
Here's a where you can use a 'scope safe constructor' Observe this code:
这是您可以使用“范围安全构造函数”的地方 观察此代码:
function Student(name) {
if(this instanceof Student) {
this.name = name;
} else {
return new Student(name);
}
}
Now you can create a Student object without using new as follows:
现在您可以不使用 new 创建一个 Student 对象,如下所示:
var stud1 = Student('Kia');
回答by kasbah
I had problems extending classes converted with the transformation function mentioned in some other answers. The issue seems to be that node (as of v9.4.0) doesn't properly support the argument spread operator ((...args) =>).
我在扩展使用其他一些答案中提到的转换函数转换的类时遇到了问题。问题似乎是该节点(从 v9.4.0 开始)没有正确支持参数扩展运算符 ( (...args) =>)。
This function based on the transpiled output of the classy-decorator (mentioned in another answer) works for me and doesn't require support for decorators or the argument spread operator.
这个基于 classy-decorator 的转译输出的函数(在另一个答案中提到)对我有用,不需要对装饰器或参数传播运算符的支持。
// 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;
}
Usage:
用法:
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
As a bonus, TypeScript (with "es5" output) seems to be fine with the old instanceoftrick (well, it won't typecheck if used without newbut it works anyhow):
作为奖励,TypeScript(带有“es5”输出)似乎适用于旧instanceof技巧(好吧,如果不使用new它就不会进行类型检查,但无论如何它都可以工作):
class X {
constructor() {
if (!(this instanceof X)) {
return new X();
}
}
}
because it compiles it down to:
因为它将其编译为:
var X = /** @class */ (function () {
function X() {
if (!(this instanceof X)) {
return new X();
}
}
return X;
}());
回答by Babakness
Alright I have another answer here, and I think this one is pretty innovative.
好吧,我在这里有另一个答案,我认为这个答案非常有创意。
Basically, the problem with doing something similar to Naomik's answer is that you create functions each and every time you chain methods together.
基本上,做类似于 Naomik 的回答的问题是每次将方法链接在一起时都创建函数。
EDIT: This solution shares the same problem, however, this answer is being left up for educational purposes.
编辑:这个解决方案有同样的问题,但是,这个答案是为了教育目的而留下的。
So here I'm offering a way to merely bind new values to your methods--which are basically just independent functions. This offer the additional benefit of being able to import functions from different modules into the newly constructed object.
因此,我在这里提供了一种方法来仅将新值绑定到您的方法——它们基本上只是独立的函数。这提供了能够将不同模块中的函数导入到新构造的对象中的额外好处。
Okay, so here it goes.
好的,就到这里了。
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)
Now you can do
现在你可以做
Right(4)
.map(x=>x+1)
.map(x=>x*2)
.inspect()
You can also do
你也可以这样做
Right.of(4)
.map(x=>x+1)
.map(x=>x*2)
.inspect()
You also have the added benefit of being able to export from modules as such
您还可以从模块中导出这样的额外好处
export const Right = prepareInstance(RightInstanceMethods,RightStaticMethods)
While you don't get ClassInstance.constructoryou do have FunctorInstance.name(note, you may need to polyfill Function.nameand/or not use an arrow function for export for browser compatibility with Function.namepurposes)
虽然您没有得到ClassInstance.constructor您确实拥有FunctorInstance.name(注意,您可能需要 polyfillFunction.name和/或不使用箭头函数进行导出以实现浏览器兼容性Function.name)
export function Right(...args){
return prepareInstance(RightInstanceMethods,RightStaticMethods)(...args)
}
PS - New name suggestions for prepareInstance welcomed, see Gist.
PS - 欢迎对 prepareInstance 的新名称建议,请参阅要点。
https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456
https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456

