ES6 : 새 키워드없이 클래스 생성자 호출
간단한 수업이 주어짐
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
클래스에는 생성자 인 "클래스 본문"이 있습니다.
내부 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) };
다른 사람들이 지적했듯이 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
.
이것의 가장 큰 단점은 transpilled 또는 pollyfilled 할 수 없지만 앞으로도 Scala와 같은 클래스를 JS에 적용 할 수있는 실행 가능한 솔루션입니다.
여기 저에게 정말로 도움이되는 패턴이 있습니다. 를 사용 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
아니요, 불가능합니다. 사용하여 만든 생성자 class
키워드에서만 구성 할 수 new
가있는 경우, [[전화]] 에드 그들은 항상하지 않고 1 (심지어 외부에서이를 감지 할 수있는 방법이 아니다). 1 : 트랜스 파일러가이게 옳은지 잘 모르겠습니다throw
TypeError
하지만 일반 함수를 해결 방법으로 사용할 수 있습니다.
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)
필요한 경우.
을 사용 하여 Foo
(아님 _Foo
) 서브 클래 싱 하려면 호출 대신을 class Bar extends Foo …
사용해야 return Reflect.construct(_Foo, args, new.target)
합니다 new _Foo
. ES5 스타일 (사용 Foo.call(this, …)
)의 서브 클래 싱 은 불가능합니다.
class MyClass {
constructor(param) {
// ...
}
static create(param) {
return new MyClass(param);
}
doSomething() {
// ...
}
}
MyClass.create('Hello World').doSomething();
너가 원하는게 그거야?
의 새 인스턴스를 만들 때 몇 가지 논리가 필요한 경우 논리 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();
나는 당신을 위해이 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
초안 에서 이걸 파내
클래스 정의 구문을 사용하여 정의 된 생성자는 함수로 호출 될 때 발생합니다.
그래서 나는 그것이 수업에서는 불가능하다고 생각합니다.
수동으로 클래스 생성자를 호출하면 코드를 리팩토링 할 때 유용 할 수 있습니다 (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() {}
다음은 '범위 안전 생성자'를 사용할 수있는 곳입니다.이 코드를 관찰하십시오.
function Student(name) {
if(this instanceof Student) {
this.name = name;
} else {
return new Student(name);
}
}
이제 다음과 같이 new를 사용하지 않고 Student 개체를 만들 수 있습니다.
var stud1 = Student('Kia');
다른 답변에서 언급 한 변환 함수로 변환 된 클래스를 확장하는 데 문제가 있습니다. 문제는 노드 (v9.4.0 기준)가 인수 확산 연산자 ( (...args) =>
)를 제대로 지원하지 않는 것 같습니다 .
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;
}
용법:
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
트릭 으로 괜찮아 보입니다 (음,없이 사용하면 typecheck는 아니지만 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;
}());
여기에 또 다른 답이 있습니다.이 질문은 매우 혁신적이라고 생각합니다.
기본적으로 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
위해 내보내기를 위해 polyfill 및 / 또는 화살표 기능을 사용하지 않아야 할 수 있습니다 )
export function Right(...args){
return prepareInstance(RightInstanceMethods,RightStaticMethods)(...args)
}
추신-prepareInstance에 대한 새로운 이름 제안이 환영되었습니다.
https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456
new
키워드 없이 클래스 생성자를 호출 할 수 없습니다.
오류 메시지는 매우 구체적입니다.
2ality 및 사양 에 대한 블로그 게시물을 참조하십시오 .
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
나는 이것을 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)
}
또는 이름을 변경 FooOf
하고 Foo
원하는대로, 즉 클래스가 될 수 FooClass
와 기능 단지 Foo
등
새 인스턴스를 만드는 것은 새 클래스를 만드는 데 부담을주지 않기 때문에 함수에 클래스를 배치하는 것보다 낫습니다.
또 다른 방법은 of
함수 를 만드는 것입니다.
const of = (classObj,...args) => (
classObj.of
? classObj.of(value)
: new classObj(args)
)
그리고 다음과 같이하십시오. of(Foo,5).increment()
이것은 약간의 인위적 일 수 있지만 작동합니다.
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()
새 생성자 없이는 클래스를 사용할 수 없습니다. 제 경우에는 클래스를 사용하고 싶을 때 new
생성자 를 사용하고 싶지 않았기 때문에 다음과 같이 클래스를 래핑하면됩니다 (제 경우에는 날짜 유틸리티 라이브러리) :
참고URL : https://stackoverflow.com/questions/30689817/es6-call-class-constructor-without-new-keyword
'Nice programing' 카테고리의 다른 글
ConEmu에서 열기 오른쪽 클릭 메뉴 창 7 (0) | 2020.11.14 |
---|---|
스크립트의 ggplot 플롯이 Rstudio에 표시되지 않습니다. (0) | 2020.11.14 |
두 문자열이 상호 교환 가능한 경우 두 문자열이있는 구조에 대해 GetHashCode를 어떻게 구현합니까? (0) | 2020.11.14 |
목록에 추가하는 것이 왜 나쁜가요? (0) | 2020.11.14 |
VB.net에서 C # '내부'란 무엇입니까? (0) | 2020.11.14 |