'new'연산자와 함께 .apply () 사용. 이게 가능해?
JavaScript에서는 new
연산자 를 통해 객체 인스턴스를 만들고 싶지만 생성자에 임의의 수의 인수를 전달합니다. 이게 가능해?
내가하고 싶은 것은 다음과 같습니다 (그러나 아래 코드는 작동하지 않습니다).
function Something(){
// init stuff
}
function createSomething(){
return new Something.apply(null, arguments);
}
var s = createSomething(a,b,c); // 's' is an instance of Something
대답
여기의 응답에서 운영자 .apply()
와 통화 할 수있는 기본 제공 방법이 없다는 것이 분명해졌습니다 new
. 그러나 사람들은이 문제에 대해 정말 흥미로운 해결책을 제시했습니다.
내가 선호하는 솔루션은 Matthew Crumley의 솔루션입니다 ( arguments
속성 을 전달하도록 수정했습니다 ).
var createSomething = (function() {
function F(args) {
return Something.apply(this, args);
}
F.prototype = Something.prototype;
return function() {
return new F(arguments);
}
})();
ECMAScript5를 사용하면 Function.prototype.bind
상황이 매우 깨끗해집니다.
function newCall(Cls) {
return new (Function.prototype.bind.apply(Cls, arguments));
// or even
// return new (Cls.bind.apply(Cls, arguments));
// if you know that Cls.bind has not been overwritten
}
다음과 같이 사용할 수 있습니다.
var s = newCall(Something, a, b, c);
또는 직접 :
var s = new (Function.prototype.bind.call(Something, null, a, b, c));
var s = new (Function.prototype.bind.apply(Something, [null, a, b, c]));
이 솔루션 과 평가 기반 솔루션 은 다음과 같은 특수 생성자에서도 항상 작동하는 유일한 솔루션 입니다 Date
.
var date = newCall(Date, 2012, 1);
console.log(date instanceof Date); // true
편집하다
약간의 설명 : new
제한된 수의 인수를 취하는 함수 에서 실행해야합니다 . 이 bind
방법을 사용하면 다음과 같이 할 수 있습니다.
var f = Cls.bind(anything, arg1, arg2, ...);
result = new f();
anything
매개 변수는 있기 때문에,별로 중요하지 않습니다 new
키워드 재설정 f
의 컨텍스트. 그러나 구문상의 이유로 필요합니다. 이제 bind
전화 : 가변 개수의 인수를 전달해야하므로 다음과 같은 트릭이 수행됩니다.
var f = Cls.bind.apply(Cls, [anything, arg1, arg2, ...]);
result = new f();
그것을 함수로 감싸 자. Cls
arugment 0으로 전달되므로 우리가 anything
됩니다.
function newCall(Cls /*, arg1, arg2, ... */) {
var f = Cls.bind.apply(Cls, arguments);
return new f();
}
실제로 임시 f
변수는 전혀 필요하지 않습니다.
function newCall(Cls /*, arg1, arg2, ... */) {
return new (Cls.bind.apply(Cls, arguments))();
}
마지막으로, 이것이 bind
실제로 필요한 것인지 확인 해야합니다. ( Cls.bind
덮어 졌을 수 있습니다). 따라서로 바꾸면 Function.prototype.bind
위와 같이 최종 결과가 나타납니다.
여기 (기능, 등이라고 할 때 다르게 동작 기본 생성자를 제외한 모든 생성자를 호출 할 수있는 일반화 된 솔루션입니다 String
, Number
, Date
인수의 배열 등) :
function construct(constructor, args) {
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
호출하여 생성 된 객체는로 생성 된 construct(Class, [1, 2, 3])
객체와 동일합니다 new Class(1, 2, 3)
.
더 구체적인 버전을 만들 수도 있으므로 매번 생성자를 전달할 필요가 없습니다. 내부 함수를 호출 할 때마다 새 인스턴스를 만들 필요가 없기 때문에 약간 더 효율적입니다.
var createSomething = (function() {
function F(args) {
return Something.apply(this, args);
}
F.prototype = Something.prototype;
return function(args) {
return new F(args);
}
})();
외부 익명 함수를 작성하고 호출하는 이유는 함수 F
가 글로벌 네임 스페이스를 오염시키지 않도록하기 위함입니다 . 때로는 모듈 패턴이라고도합니다.
[최신 정보]
TypeScript에서 이것을 사용하려는 사람들에게는 TS F
가 아무것도 반환 하면 오류가 발생하기 때문에 :
function construct(constructor, args) {
function F() : void {
constructor.apply(this, args);
}
F.prototype = constructor.prototype;
return new F();
}
환경이 ECMA Script 2015의 스프레드 연산자 ( ...
)를 지원하는 경우 다음 과 같이 간단히 사용할 수 있습니다.
function Something() {
// init stuff
}
function createSomething() {
return new Something(...arguments);
}
참고 : 이제 ECMA Script 2015의 사양이 게시되고 대부분의 JavaScript 엔진이이를 적극적으로 구현하고 있으므로이를 선호하는 방법입니다.
여기 에서 몇 가지 주요 환경에서 스프레드 연산자의 지원을 확인할 수 있습니다 .
던지는 모든 인수를 철회하는 Items 생성자가 있다고 가정 해보십시오.
function Items () {
this.elems = [].slice.call(arguments);
}
Items.prototype.sum = function () {
return this.elems.reduce(function (sum, x) { return sum + x }, 0);
};
Object.create ()로 인스턴스를 만든 다음 해당 인스턴스로 .apply ()를 만들 수 있습니다.
var items = Object.create(Items.prototype);
Items.apply(items, [ 1, 2, 3, 4 ]);
console.log(items.sum());
1 + 2 + 3 + 4 == 10 이후 10을 인쇄 할 때 :
$ node t.js
10
ES6에서는 Reflect.construct()
매우 편리합니다.
Reflect.construct(F, args)
@ Matthew 생성자 속성도 수정하는 것이 좋습니다.
// Invoke new operator with arbitrary arguments
// Holy Grail pattern
function invoke(constructor, args) {
var f;
function F() {
// constructor returns **this**
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
f = new F();
f.constructor = constructor;
return f;
}
@Matthew의 답변의 향상된 버전. 이 형식은 임시 클래스를 클로저에 저장함으로써 얻을 수있는 약간의 성능 이점과 모든 클래스를 만드는 데 사용할 수있는 하나의 함수의 유연성을 갖습니다.
var applyCtor = function(){
var tempCtor = function() {};
return function(ctor, args){
tempCtor.prototype = ctor.prototype;
var instance = new tempCtor();
ctor.prototype.constructor.apply(instance,args);
return instance;
}
}();
이것은 전화로 사용됩니다 applyCtor(class, [arg1, arg2, argn]);
init 항목을 별도 Something
의 프로토 타입 방법으로 옮길 수 있습니다 .
function Something() {
// Do nothing
}
Something.prototype.init = function() {
// Do init stuff
};
function createSomething() {
var s = new Something();
s.init.apply(s, arguments);
return s;
}
var s = createSomething(a,b,c); // 's' is an instance of Something
이 답변은 약간 늦었지만 이것을 보는 사람이 그것을 사용할 수 있다고 생각했습니다. apply를 사용하여 새 객체를 반환하는 방법이 있습니다. 객체 선언에 약간의 변경이 필요하지만.
function testNew() {
if (!( this instanceof arguments.callee ))
return arguments.callee.apply( new arguments.callee(), arguments );
this.arg = Array.prototype.slice.call( arguments );
return this;
}
testNew.prototype.addThem = function() {
var newVal = 0,
i = 0;
for ( ; i < this.arg.length; i++ ) {
newVal += this.arg[i];
}
return newVal;
}
testNew( 4, 8 ) === { arg : [ 4, 8 ] };
testNew( 1, 2, 3, 4, 5 ).addThem() === 15;
첫 번째의 경우 if
에있는 일에 문 testNew
당신이 return this;
함수의 맨 아래에. 코드를 예로 들어 보겠습니다.
function Something() {
// init stuff
return this;
}
function createSomething() {
return Something.apply( new Something(), arguments );
}
var s = createSomething( a, b, c );
업데이트 : 첫 번째 예제를 두 개가 아닌 여러 인수로 합산하도록 변경했습니다.
방금이 문제를 겪었고 다음과 같이 해결했습니다.
function instantiate(ctor) {
switch (arguments.length) {
case 1: return new ctor();
case 2: return new ctor(arguments[1]);
case 3: return new ctor(arguments[1], arguments[2]);
case 4: return new ctor(arguments[1], arguments[2], arguments[3]);
//...
default: throw new Error('instantiate: too many parameters');
}
}
function Thing(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}
var thing = instantiate(Thing, 'abc', 123, {x:5});
예, 조금 추악하지만 문제를 해결하고 간단합니다.
평가 기반 솔루션에 관심이 있다면
function createSomething() {
var q = [];
for(var i = 0; i < arguments.length; i++)
q.push("arguments[" + i + "]");
return eval("new Something(" + q.join(",") + ")");
}
작동합니다!
var cls = Array; //eval('Array'); dynamically
var data = [2];
new cls(...data);
CoffeeScript가 수행하는 방법도 참조하십시오.
s = new Something([a,b,c]...)
된다 :
var s;
s = (function(func, args, ctor) {
ctor.prototype = func.prototype;
var child = new ctor, result = func.apply(child, args);
return Object(result) === result ? result : child;
})(Something, [a, b, c], function(){});
이 생성자 방식은 new
키워드 유무에 관계없이 작동합니다 .
function Something(foo, bar){
if (!(this instanceof Something)){
var obj = Object.create(Something.prototype);
return Something.apply(obj, arguments);
}
this.foo = foo;
this.bar = bar;
return this;
}
지원한다고 가정 Object.create
하지만 이전 브라우저를 지원하는 경우 항상 폴리 필 할 수 있습니다. 여기에서 MDN의 지원 테이블을 참조하십시오 .
다음 은 console output과 함께 작동 하는 JSBin 입니다.
ES6 또는 폴리 필이 없는 솔루션 :
var obj = _new(Demo).apply(["X", "Y", "Z"]);
function _new(constr)
{
function createNamedFunction(name)
{
return (new Function("return function " + name + "() { };"))();
}
var func = createNamedFunction(constr.name);
func.prototype = constr.prototype;
var self = new func();
return { apply: function(args) {
constr.apply(self, args);
return self;
} };
}
function Demo()
{
for(var index in arguments)
{
this['arg' + (parseInt(index) + 1)] = arguments[index];
}
}
Demo.prototype.tagged = true;
console.log(obj);
console.log(obj.tagged);
출력
데모 {arg1 : "X", arg2 : "Y", arg3 : "Z"}
... 또는 "더 짧은"방법 :
var func = new Function("return function " + Demo.name + "() { };")();
func.prototype = Demo.prototype;
var obj = new func();
Demo.apply(obj, ["X", "Y", "Z"]);
편집 :
이것이 좋은 해결책이라고 생각합니다.
this.forConstructor = function(constr)
{
return { apply: function(args)
{
let name = constr.name.replace('-', '_');
let func = (new Function('args', name + '_', " return function " + name + "() { " + name + "_.apply(this, args); }"))(args, constr);
func.constructor = constr;
func.prototype = constr.prototype;
return new func(args);
}};
}
new
연산자를 사용하여 원하는 개수의 인수로 생성자를 호출 할 수 없습니다 .
당신이 할 수있는 일은 생성자를 약간 변경하는 것입니다. 대신에:
function Something() {
// deal with the "arguments" array
}
var obj = new Something.apply(null, [0, 0]); // doesn't work!
대신이 작업을 수행하십시오.
function Something(args) {
// shorter, but will substitute a default if args.x is 0, false, "" etc.
this.x = args.x || SOME_DEFAULT_VALUE;
// longer, but will only put in a default if args.x is not supplied
this.x = (args.x !== undefined) ? args.x : SOME_DEFAULT_VALUE;
}
var obj = new Something({x: 0, y: 0});
또는 배열을 사용해야하는 경우 :
function Something(args) {
var x = args[0];
var y = args[1];
}
var obj = new Something([0, 0]);
CoffeeScript에서 Matthew Crumley의 솔루션 :
construct = (constructor, args) ->
F = -> constructor.apply this, args
F.prototype = constructor.prototype
new F
또는
createSomething = (->
F = (args) -> Something.apply this, args
F.prototype = Something.prototype
return -> new Something arguments
)()
function createSomething() {
var args = Array.prototype.concat.apply([null], arguments);
return new (Function.prototype.bind.apply(Something, args));
}
대상 브라우저가 ECMAScript 5를 지원하지 않으면 Function.prototype.bind
코드가 작동하지 않습니다. 그러나 호환성 테이블을 참조하십시오 .
@Matthew 답변을 수정했습니다. 여기서는 평소와 같이 기능하기 위해 여러 개의 매개 변수를 전달할 수 있습니다 (배열이 아님). 또한 '무언가'는 다음과 같이 하드 코딩되지 않습니다.
function createObject( constr ) {
var args = arguments;
var wrapper = function() {
return constr.apply( this, Array.prototype.slice.call(args, 1) );
}
wrapper.prototype = constr.prototype;
return new wrapper();
}
function Something() {
// init stuff
};
var obj1 = createObject( Something, 1, 2, 3 );
var same = new Something( 1, 2, 3 );
이 한 줄짜리가해야합니다.
new (Function.prototype.bind.apply(Something, [null].concat(arguments)));
다른 접근 방식은 실행 가능하지만 지나치게 복잡합니다. Clojure에서는 일반적으로 유형 / 레코드를 인스턴스화하고 해당 함수를 인스턴스화 메커니즘으로 사용하는 함수를 작성합니다. 이것을 JavaScript로 번역 :
function Person(surname, name){
this.surname = surname;
this.name = name;
}
function person(surname, name){
return new Person(surname, name);
}
이 방법을 사용 new
하면 위에서 설명한 것 이외 의 사용을 피할 수 있습니다 . 물론이 기능 apply
은 다른 기능적 프로그래밍 기능을 사용하는 데 문제가 없습니다 .
var doe = _.partial(person, "Doe");
var john = doe("John");
var jane = doe("Jane");
이 접근 방식을 사용하면 모든 유형 생성자 (예 Person
:)가 바닐라, 아무것도하지 않는 생성자입니다. 인수를 전달하고 동일한 이름의 특성에 지정하면됩니다. 털이 많은 세부 사항은 생성자 함수 (예 :)에 person
있습니다.
어쨌든 좋은 연습이기 때문에 이러한 추가 생성자 함수를 만들 필요가 거의 없습니다. 뉘앙스가 다른 여러 생성자 함수를 잠재적으로 가질 수 있기 때문에 편리합니다.
또한 임시 F()
생성자 를 재사용하는 문제가 arguments.callee
작성자 / 팩토리 함수 자체 인 http://www.dhtmlkitchen.com/?category=/JavaScript/&date=2008/05/11 을 사용하여 어떻게 해결되었는지 확인하는 것도 모욕적입니다 . / & entry = 데코레이터-공장-종료
모든 함수 (생성자조차도)는 다양한 개수의 인수를 사용할 수 있습니다. 각 함수에는 "arguments"변수가 있으며이를 사용하여 배열로 캐스트 할 수 있습니다 [].slice.call(arguments)
.
function Something(){
this.options = [].slice.call(arguments);
this.toString = function (){
return this.options.toString();
};
}
var s = new Something(1, 2, 3, 4);
console.log( 's.options === "1,2,3,4":', (s.options == '1,2,3,4') );
var z = new Something(9, 10, 11);
console.log( 'z.options === "9,10,11":', (z.options == '9,10,11') );
위의 테스트는 다음과 같은 출력을 생성합니다.
s.options === "1,2,3,4": true
z.options === "9,10,11": true
function FooFactory() {
var prototype, F = function(){};
function Foo() {
var args = Array.prototype.slice.call(arguments),
i;
for (i = 0, this.args = {}; i < args.length; i +=1) {
this.args[i] = args[i];
}
this.bar = 'baz';
this.print();
return this;
}
prototype = Foo.prototype;
prototype.print = function () {
console.log(this.bar);
};
F.prototype = prototype;
return Foo.apply(new F(), Array.prototype.slice.call(arguments));
}
var foo = FooFactory('a', 'b', 'c', 'd', {}, function (){});
console.log('foo:',foo);
foo.print();
내 버전은 다음과 같습니다 createSomething
.
function createSomething() {
var obj = {};
obj = Something.apply(obj, arguments) || obj;
obj.__proto__ = Something.prototype; //Object.setPrototypeOf(obj, Something.prototype);
return o;
}
이를 바탕으로 new
JavaScript 키워드 를 시뮬레이션하려고 시도했습니다 .
//JavaScript 'new' keyword simulation
function new2() {
var obj = {}, args = Array.prototype.slice.call(arguments), fn = args.shift();
obj = fn.apply(obj, args) || obj;
Object.setPrototypeOf(obj, fn.prototype); //or: obj.__proto__ = fn.prototype;
return obj;
}
나는 그것을 테스트했으며 모든 시나리오에서 완벽하게 작동하는 것 같습니다. 또한 같은 기본 생성자에서도 작동합니다 Date
. 다음은 몇 가지 테스트입니다.
//test
new2(Something);
new2(Something, 1, 2);
new2(Date); //"Tue May 13 2014 01:01:09 GMT-0700" == new Date()
new2(Array); //[] == new Array()
new2(Array, 3); //[undefined × 3] == new Array(3)
new2(Object); //Object {} == new Object()
new2(Object, 2); //Number {} == new Object(2)
new2(Object, "s"); //String {0: "s", length: 1} == new Object("s")
new2(Object, true); //Boolean {} == new Object(true)
그렇습니다, 우리는 할 수 있습니다 prototype inheritance
.
function Actor(name, age){
this.name = name;
this.age = age;
}
Actor.prototype.name = "unknown";
Actor.prototype.age = "unknown";
Actor.prototype.getName = function() {
return this.name;
};
Actor.prototype.getAge = function() {
return this.age;
};
" new
"로 객체를 만들면 생성 된 객체 INHERITS getAge
()가 있지만 apply(...) or call(...)
Actor를 호출하는 경우 객체를 전달 "this"
하지만 전달하는 객체 WON'T
는Actor.prototype
apply 또는 call Actor.prototype을 직접 전달하지 않으면 "this"는 "Actor.prototype"을 가리키며 this.name은 다음과 같이 쓰여집니다 Actor.prototype.name
. 따라서 Actor...
인스턴스가 아닌 프로토 타입을 덮어 쓰기 때문에 생성 된 다른 모든 객체에 영향을줍니다.
var rajini = new Actor('Rajinikanth', 31);
console.log(rajini);
console.log(rajini.getName());
console.log(rajini.getAge());
var kamal = new Actor('kamal', 18);
console.log(kamal);
console.log(kamal.getName());
console.log(kamal.getAge());
함께 해보자 apply
var vijay = Actor.apply(null, ["pandaram", 33]);
if (vijay === undefined) {
console.log("Actor(....) didn't return anything
since we didn't call it with new");
}
var ajith = {};
Actor.apply(ajith, ['ajith', 25]);
console.log(ajith); //Object {name: "ajith", age: 25}
try {
ajith.getName();
} catch (E) {
console.log("Error since we didn't inherit ajith.prototype");
}
console.log(Actor.prototype.age); //Unknown
console.log(Actor.prototype.name); //Unknown
통과시킴으로써 Actor.prototype
를 Actor.call()
액터 () 함수는 실행, RAN 인 경우, 첫 번째 인수로 this.name=name
이후 "이"를 가리 Actor.prototype
,this.name=name; means Actor.prototype.name=name;
var simbhu = Actor.apply(Actor.prototype, ['simbhu', 28]);
if (simbhu === undefined) {
console.log("Still undefined since the function didn't return anything.");
}
console.log(Actor.prototype.age); //simbhu
console.log(Actor.prototype.name); //28
var copy = Actor.prototype;
var dhanush = Actor.apply(copy, ["dhanush", 11]);
console.log(dhanush);
console.log("But now we've corrupted Parent.prototype in order to inherit");
console.log(Actor.prototype.age); //11
console.log(Actor.prototype.name); //dhanush
사용 방법에 대한 원래의 질문으로 돌아와서 new operator with apply
여기에 내 테이크가 있습니다 ....
Function.prototype.new = function(){
var constructor = this;
function fn() {return constructor.apply(this, args)}
var args = Array.prototype.slice.call(arguments);
fn.prototype = this.prototype;
return new fn
};
var thalaivar = Actor.new.apply(Parent, ["Thalaivar", 30]);
console.log(thalaivar);
ES6부터는 Spread 연산자를 통해 가능합니다. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Apply_for_new를 참조 하십시오.
이 답변은 이미 https://stackoverflow.com/a/42027742/7049810 에 나와 있지만 대부분의 사람들이 놓친 것 같습니다
실제로 가장 간단한 방법은 다음과 같습니다.
function Something (a, b) {
this.a = a;
this.b = b;
}
function createSomething(){
return Something;
}
s = new (createSomething())(1, 2);
// s == Something {a: 1, b: 2}
@jordancpaul의 답변에서 수정 된 솔루션.
var applyCtor = function(ctor, args)
{
var instance = new ctor();
ctor.prototype.constructor.apply(instance, args);
return instance;
};
익명 프로토 타입 Something
을 만들고 인수를 사용하여 프로토 타입을 적용한 다음 해당 익명 프로토 타입의 새 인스턴스를 만듭니다. 이것의 단점 중 하나는 s instanceof Something
확인을 통과하지 못한다는 것입니다 . 비록 동일하지만 기본적으로 클론의 인스턴스입니다.
function Something(){
// init stuff
}
function createSomething(){
return new (function(){Something.apply(this, arguments)});
}
var s = createSomething(a,b,c); // 's' is an instance of Something
참고 URL : https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
'Programing' 카테고리의 다른 글
유형에 대해 JSON.NET 오류 자체 참조 루프가 발견되었습니다. (0) | 2020.02.17 |
---|---|
여러 파일의 확장자를 어떻게 바꾸나요? (0) | 2020.02.17 |
Git에서 HEAD, 작업 트리 및 인덱스의 차이점은 무엇입니까? (0) | 2020.02.17 |
서버에 요청을 보낼 때 curl에 의해 작성된 요청 헤더를 어떻게 볼 수 있습니까? (0) | 2020.02.17 |
MySQL에서 마지막으로 실행 된 쿼리를 표시하는 방법은 무엇입니까? (0) | 2020.02.17 |