2009-12-24 13 views
24

this question पर अद्भुत प्रतिक्रियाओं के लिए धन्यवाद, मैं समझता हूं कि वर्गार्ग के साथ जावास्क्रिप्ट कार्यों को कैसे कॉल करें।जावास्क्रिप्ट कन्स्ट्रक्टर पर लागू होता है, "खराब प्रारूपित पैरामीटर" फेंकने

अब मैं उपयोग करने के लिए मैं कुछ रोचक जानकारी on this post पाया एक निर्माता

के साथ लागू देख रहा हूँ।

लेकिन मेरे कोड त्रुटियों

फेंक रहा है प्रयास 1:

var mid_parser = new Parser.apply(null, mid_patterns); 

त्रुटि:

TypeError: Function.prototype.apply called on incompatible [object Object] 

प्रयास 2: प्रयास 1:

var mid_parser = new Parser.prototype.apply(null, mid_patterns); 

त्रुटि:

TypeError: Function.prototype.apply called on incompatible [object Object] 

प्रयास 2:

function Parser() 
{ 
    this.comparemanager = new CompareManager(arguments); 
} 

mid_patterns = [objA,objB,objC] 
var mid_parser = new Parser(); 
Parser.constructor.apply(mid_parser, mid_patterns); 

त्रुटि:

syntax_model.js:91: SyntaxError: malformed formal parameter 

प्रयास 3:

var mid_parser = Parser.apply(null, mid_patterns); 

त्रुटि:

TypeError: this.init is undefined // init is a function of Parser.prototype 

मैं समय से किया जा रहा के लिए एक समाधान है:

function Parser() 
{ 
    if(arguments.length) this.init.call(this,arguments); // call init only if arguments 
} 
Parser.prototype = { 
    //... 
    init: function() 
    { 
     this.comparemanager = new CompareManager(arguments); 
    } 
    //... 
} 

var normal parser = new Parser(objA,objB,objC); 

mid_patterns = [objA,objB,objC] 
var dyn_parser = new Parser(); 
dyn_parser.init.apply(dyn_parser, mid_patterns); 

इस बहुत अच्छी तरह से काम करता है, लेकिन यह साफ है और सार्वभौमिक रूप में मैं चाहता हूँ नहीं है।

जावास्क्रिप्ट में varargs के साथ एक कन्स्ट्रक्टर को कॉल करना संभव है?

उत्तर

17

आप लागू इस्तेमाल कर सकते हैं और this तर्क के रूप में एक खाली वस्तु पारित:

var mid_parser = {}; 
Parser.apply(mid_parser, mid_patterns); 

लेकिन उस समाधान प्रोटोटाइप श्रृंखला के बारे में ध्यान नहीं ले जाएगा।

आप एक Parser वस्तु बना सकते हैं, new ऑपरेटर का उपयोग, लेकिन तर्क गुजर बिना, और फिर apply का उपयोग निर्माता समारोह को फिर से चलाने के लिए:

var mid_parser = new Parser(); 
Parser.apply(mid_parser, mid_patterns); 
+0

+1, शानदार धन्यवाद –

+0

बहुत रोचक। मैंने इस के साथ थोड़ा सा खेला है और यह कुछ मामलों में * बहुत अच्छी तरह से काम करता है *। सभी रचनाकार इस दृष्टिकोण का समर्थन नहीं करते हैं। 'दिनांक', उदाहरण के लिए:' var date = new date; Date.call (तिथि, 1 9 84, 3, 26) '। – davidchambers

+1

फ़ंक्शन.प्ली (नया फ़ंक्शन(), arg) /* एक लाइन समाधान है */ – TERMtm

7

आप इस तथ्य दोहन कर सकते हैं कि आप श्रृंखला कंस्ट्रक्टर्स कर सकते हैं इसे प्राप्त करने के लिए apply(...) का उपयोग करके, हालांकि इसके लिए प्रॉक्सी क्लास के निर्माण की आवश्यकता है।

var f1 = construct(Foo, [2, 3]); 
// which is more or less equivalent to 
var f2 = new Foo(2, 3); 

construct() समारोह:

function construct(klass, args) { 

    function F() { 
    return klass.apply(this, arguments[0]); 
    }; 

    F.prototype = klass.prototype; 

    return new F(args); 

} 

कुछ नमूना कोड का उपयोग करता है यह:

function Foo(a, b) { 
    this.a = a; this.b = b; 
} 

Foo.prototype.dump = function() { 
    console.log("a = ", this.a); 
    console.log("b = ", this.b); 
}; 

var f = construct(Foo, [7, 9]); 

f.dump(); 
+1

यह सभी मामलों को काफी संभाल नहीं करता है। आपको f() में klass.apply से मान वापस करने की आवश्यकता है: 'फ़ंक्शन F() {वापसी klass.apply (यह, तर्क [0]); } '। यह उन मामलों को संभालता है जहां कन्स्ट्रक्टर फ़ंक्शन उदाहरण के रूप में उपयोग किए जाने वाले मान को वापस कर देता है। –

+0

@MonroeThomas अच्छा बिंदु, अद्यतन किया गया है। – mjs

11

एक बेहतर समाधान के लिए एक अस्थायी निर्माता समारोह बनाने के लिए है construct() समारोह नीचे आप अपना काम करने दिया , उस वर्ग के प्रोटोटाइप को लागू करें जो आप चाहते हैं (प्रोटोटाइप श्रृंखला सुनिश्चित करने के लिए) और उसके बाद निर्माता को मैन्युअल रूप से लागू करें। यह निर्माता दो बार अनावश्यक रूप से बुला ...

applySecond = function(){ 
    function tempCtor() {}; 
    return function(ctor, args){ 
     tempCtor.prototype = ctor.prototype; 
     var instance = new tempCtor(); 
     ctor.apply(instance,args); 
     return instance; 
    } 
}(); 

मैं प्रदर्शन परीक्षण किया और पाया कि इस विधि वास्तव में, एक बिट बहुत सरल मामले में धीमी होने से बचाता है। हालांकि, यह अधिक कुशल होने के लिए केवल कन्स्ट्रक्टर में एक ही दिनांक() ऑब्जेक्ट का निर्माण लेता है। साथ ही, यह न भूलें कि कुछ नियंत्रक अपवाद फेंक सकते हैं यदि कोई पैरामीटर पास नहीं हुआ है, तो यह भी अधिक सही है।

मेरे सत्यापन कोड:

var ExpensiveClass = function(arg0,arg1){ 
    this.arg0 = arg0; 
    this.arg1 = arg1; 
    this.dat = new Date(); 
} 

var CheapClass = function(arg0,arg1){ 
    this.arg0 = arg0; 
    this.arg1 = arg1; 
} 

applyFirst = function(ctor, args){ 
    var instance = new ctor(); 
    ctor.apply(instance, args); 
    return instance; 
} 

applySecond = function(){ 
    function tempCtor() {}; 
    return function(ctor, args){ 
     tempCtor.prototype = ctor.prototype; 
     var instance = new tempCtor(); 
     ctor.apply(instance,args); 
     return instance; 
    } 
}(); 

console.time('first Expensive'); 
for(var i = 0; i < 10000; i++){ 
    test = applyFirst(ExpensiveClass ,['arg0','arg1']); 
} 
console.timeEnd('first Expensive'); 

console.time('second Expensive'); 
for(var i = 0; i < 10000; i++){ 
    test = applySecond(ExpensiveClass ,['arg0','arg1']); 
} 
console.timeEnd('second Expensive'); 

console.time('first Cheap'); 
for(var i = 0; i < 10000; i++){ 
    test = applyFirst(CheapClass,['arg0','arg1']); 
} 
console.timeEnd('first Cheap'); 

console.time('second Cheap'); 
for(var i = 0; i < 10000; i++){ 
    test = applySecond(CheapClass,['arg0','arg1']); 
} 
console.timeEnd('second Cheap'); 

परिणाम:

first Expensive: 76ms 
second Expensive: 66ms 
first Cheap: 52ms 
second Cheap: 52ms 
+0

मैं यह प्रमाणित करता हूं कि केवल "सीटीओआर" समारोह महंगा है। कारण यह है कि इस प्रत्यारोपण में दो कार्यों को भी कॉल करना शामिल है, बस tempCoror एक खाली कार्य है। Ctor (ctor.apply) पर लागू कॉल सामान्य कार्य के रूप में निष्पादित होगा, केवल नए कीवर्ड के साथ यह एक नई वस्तु बनाने का ओवरहेड होगा। –

+0

मैंने कुछ पोस्ट शामिल करने के लिए अपनी पोस्ट अपडेट की। हालांकि यह सच है कि बहुत ही सरल रचनाकारों के साथ, यह विधि धीमी है, इस विधि को वारंट करने के लिए कन्स्ट्रक्टर "महंगा" बनाने के लिए तुच्छ है। साथ ही, जैसा कि मेरे अपडेट में बताया गया है, यह बेहतर तरीका रचनाकारों को संभालता है जो अपवाद फेंकते हैं जहां तर्क प्रदान नहीं किए जाते हैं या गलत प्रकार के होते हैं। – jordancpaul

+0

ने मेरी पोस्ट को एक और बार अपडेट किया। यह अब साधारण मामले में समान रूप से तेज़ है, और महंगे मामले में भी तेज़ है। – jordancpaul

0

समाधान @CMS को पूरा करने और प्रोटोटाइप श्रृंखला की रक्षा करने के लिए, आप यह कर सकते हैं:

var mid_parser = {}; 
mid_parser.__proto__ = Parser.prototype; 
Parser.apply(mid_parser, mid_patterns); 

एक के रूप में साइड नोट, यह आईई 8- के साथ काम नहीं करेगा।

संबंधित मुद्दे