[javascript] 'नया' ऑपरेटर के साथ .apply () का उपयोग करें। क्या यह संभव है?



Answers

यहां एक सामान्यीकृत समाधान है जो किसी भी कन्स्ट्रक्टर को कॉल कर सकता है (मूल रचनाकारों को छोड़कर, जो 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 को रखना है। इसे कभी-कभी मॉड्यूल पैटर्न भी कहा जाता है।

[अद्यतन करें]

उन लोगों के लिए जो टाइपस्क्रिप्ट में इसका उपयोग करना चाहते हैं, क्योंकि टीएस कुछ त्रुटि देता है तो टीएस एक त्रुटि देता है:

function construct(constructor, args) {
    function F() : void {
        constructor.apply(this, args);
    }
    F.prototype = constructor.prototype;
    return new F();
}
Question

जावास्क्रिप्ट में, मैं ऑब्जेक्ट इंस्टेंस ( 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

उत्तर

यहां प्रतिक्रियाओं से, यह स्पष्ट हो गया कि new ऑपरेटर के साथ .apply() को कॉल करने का कोई अंतर्निहित तरीका नहीं है। हालांकि, लोगों ने समस्या के कई वास्तव में दिलचस्प समाधान सुझाए।

मैथ्यू क्रूमली से मेरा पसंदीदा समाधान यह था (मैंने arguments संपत्ति को पारित करने के लिए इसे संशोधित किया है):

var createSomething = (function() {
    function F(args) {
        return Something.apply(this, args);
    }
    F.prototype = Something.prototype;

    return function() {
        return new F(arguments);
    }
})();



@ मैथ्यू मुझे लगता है कि कन्स्ट्रक्टर संपत्ति को ठीक करना बेहतर है।

// 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;
}



मान लीजिए कि आपके पास एक आइटम कन्स्ट्रक्टर है जो आपके द्वारा फेंकने वाले सभी तर्कों को फिसलता है:

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



बनाने का मेरा संस्करण यहां कुछ है:

function createSomething() {
    var obj = {};
    obj = Something.apply(obj, arguments) || obj;
    obj.__proto__ = Something.prototype; //Object.setPrototypeOf(obj, Something.prototype); 
    return o;
}

उस पर आधारित, मैंने जावास्क्रिप्ट के new कीवर्ड को अनुकरण करने की कोशिश की:

//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)



ईएस 6 या पॉलीफिल के बिना समाधान:

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: "एक्स", arg2: "वाई", 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);
    }};
}



यह भी देखें कि कॉफीस्क्रिप्ट कैसे करता है।

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(){});



संशोधित @ मैथ्यू उत्तर। यहां मैं सामान्य रूप से कार्य करने के लिए किसी भी पैरामीटर को पास कर सकता हूं (सरणी नहीं)। इसके अलावा 'कुछ' में हार्डकोड नहीं किया गया है:

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 );



मैं बस इस समस्या में आया, और मैंने इसे इस तरह हल किया:

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 Something (a, b) {
  this.a = a;
  this.b = b;
}
function createSomething(){
    return Something;
}
s = new (createSomething())(1, 2); 
// s == Something {a: 1, b: 2}



कोई भी फ़ंक्शन (यहां तक ​​कि एक कन्स्ट्रक्टर) तर्कों की एक चर संख्या ले सकता है। प्रत्येक फ़ंक्शन में "तर्क" चर होता है जिसे [].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



हां हम कर सकते हैं, जावास्क्रिप्ट प्रकृति में 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(...) का उपयोग करते हैं, तो हम "this" लिए ऑब्जेक्ट पास कर रहे हैं लेकिन जिस वस्तु को हम पास करते हैं वह Actor.prototype से प्राप्त WON'T

जब तक, हम सीधे एक्टोर.प्रोटोटाइप को लागू या कॉल नहीं करते हैं, लेकिन फिर .... "यह" "अभिनेता.प्रोटोटाइप" को इंगित करेगा और यह .name इस पर लिखेंगे: Actor.prototype.name । इस प्रकार Actor... के साथ बनाई गई सभी अन्य वस्तुओं को प्रभावित करता है 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 साथ प्रयास 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() को पहले तर्क के रूप में पास करके, जब अभिनेता () फ़ंक्शन चलाया जाता है, तो यह इस this.name=name निष्पादित करता है, क्योंकि "यह" Actor.prototype को इंगित करेगा। Actor.prototype , this.name=name; means Actor.prototype.name=name; 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);



आप इनिट सामान को 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



यह काम!

var cls = Array; //eval('Array'); dynamically
var data = [2];
new cls(...data);



यहां पोस्ट करने के लिए धन्यवाद मैंने इसे इस तरह इस्तेमाल किया है:

SomeClass = function(arg1, arg2) {
    // ...
}

ReflectUtil.newInstance('SomeClass', 5, 7);

और कार्यान्वयन:

/**
 * @param strClass:
 *          class name
 * @param optionals:
 *          constructor arguments
 */
ReflectUtil.newInstance = function(strClass) {
    var args = Array.prototype.slice.call(arguments, 1);
    var clsClass = eval(strClass);
    function F() {
        return clsClass.apply(this, args);
    }
    F.prototype = clsClass.prototype;
    return new F();
};



Related