2009-05-01 2 views
10

क्या यह संभव है? मैं विभिन्न प्रकार के कारखानों को चलाने के लिए एक एकल आधार कारखाना समारोह बना रहा हूं (लेकिन कुछ समानताएं हैं) और मैं आधार कारखाने के लिए एक सरणी के रूप में तर्क पारित करने में सक्षम होना चाहता हूं, जो संभवतः एक नई वस्तु का उदाहरण बनाता है जो तर्कों को पॉप्युलेट करता है एक सरणी के माध्यम से प्रासंगिक वर्ग के निर्माता।जावास्क्रिप्ट में उन्हें सूचीबद्ध करने के बजाय पैरामीटर के मानों की सरणी का उपयोग करके मैं ऑब्जेक्ट कैसे बना सकता हूं?

जावास्क्रिप्ट में यह विधि लागू का उपयोग करके एक सरणी का उपयोग करने के लिए कई तर्क के साथ एक समारोह कॉल करने के लिए संभव है:

namespace.myFunc = function(arg1, arg2) { //do something; } 
var result = namespace.myFunc("arg1","arg2"); 
//this is the same as above: 
var r = [ "arg1","arg2" ]; 
var result = myFunc.apply(namespace, r); 

यह वहाँ वैसे भी रूप में अगर एक वस्तु लागू का उपयोग करने का एक उदाहरण बनाने के लिए प्रतीत नहीं होता हालांकि, वहाँ है?

कुछ की तरह (यह काम नहीं करता):

var instance = new MyClass.apply(namespace, r); 
+0

मैं OOP शब्दावली से परिचित नहीं हूँ ... आप, का एक नया उदाहरण बनाने के लिए संक्षेप में कोशिश कर रहे हैं, namespace.MyClass जब MyClass नहीं है नाम स्थान? – Anonymous

+0

मैं नेमस्पेस का एक नया उदाहरण बनाने की कोशिश कर रहा हूं। MyClass, लेकिन नेमस्पेस के बारे में भूल जाओ जो हमेशा समान होता है। मैं केवल उदाहरण बनाते समय कन्स्ट्रक्टर को सरणी के रूप में तर्क पारित करने में सक्षम होने का लाभ उठाने का प्रयास कर रहा हूं। –

+0

आह! इससे यह और स्पष्ट हो जाता है। मुझे अभी भी पता नहीं है कि आवेदन का उपयोग यहां किया जा सकता है - यह हो सकता है - लेकिन मैंने सूची समझ के बारे में कुछ रोचक बातें पढ़ी हैं और जैसे मोज़िला से रक्तस्राव-किनारे जेएस में। – Anonymous

उत्तर

15

इस प्रयास करें:

var instance = {}; 
MyClass.apply(instance, r); 

सभी कीवर्ड 'नई' करता है निर्माता जो तब हो जाता है करने के लिए एक नई वस्तु में पास होना जरूरी है कन्स्ट्रक्टर फ़ंक्शन के अंदर यह चर।

कैसे निर्माता लिखा गया था पर निर्भर करता है, तो आप ऐसा करने के लिए हो सकता है:

var instance = {}; 
var returned = MyClass.apply(instance, args); 
if(returned != null) { 
    instance = returned; 
} 

अद्यतन: एक टिप्पणी कहते हैं, अगर वहाँ एक प्रोटोटाइप है यह काम नहीं करता। इसे इस्तेमाल करे।

function newApply(class, args) { 
    function F() { 
     return class.apply(this, args); 
    } 
    F.prototype = class.prototype; 
    return new F(); 
} 

newApply(MyClass, args); 
+0

यह बहुत चालाक है, लेकिन यह काम नहीं कर रहा है। कम से कम फ़ायरफ़ॉक्स में नहीं, यह मुझे कुछ विचार दे रहा है हालांकि। Hrmm। –

+0

FWIW, Array.apply ({}, [2,4,6]) के सरल मामले और संख्या .apply ({}, ["3"]) सही ढंग से काम करने लगते हैं। – Anonymous

+0

यह ऑब्जेक्ट प्रोटोटाइप को MyClass.prototype पर सेट नहीं करेगा, जो आवश्यक हो सकता है। –

0

एक कामकाज के बारे में क्या?

function MyClass(arg1, arg2) { 

    this.init = function(arg1, arg2){ 
     //if(arg1 and arg2 not null) do stuff with args 
    } 

    init(arg1, arg2); 
} 

तो आप कर सकते हैं:

var obj = new MyClass(); 
obj.apply(obj, args); 
+0

तो, न केवल आपको मौजूदा कन्स्ट्रक्टर फ़ंक्शंस को संशोधित करना होगा, लेकिन अब इसे संभालने के लिए कोड की दो पंक्तियां लेती हैं। दुनिया में सबसे बड़ा समाधान नहीं है ... यहां तक ​​कि सभ्य भी नहीं, IMNSHO। :) –

0

एक संभावना यह एक सामान्य समारोह कॉल के रूप में काम करने के लिए निर्माता है।

function MyClass(arg1, arg2) { 
    if (!(this instanceof MyClass)) { 
     return new MyClass(arg1, arg2); 
    } 

    // normal constructor here 
} 

if बयान पर हालत सच हो सकता है अगर आप एक सामान्य समारोह के रूप में MyClass फोन (जब तक call/apply साथ सहित के रूप में this तर्क एक MyClass object नहीं है)।

अब इन सब बराबर हैं:

new MyClass(arg1, arg2); 
MyClass(arg1, arg2); 
MyClass.call(null, arg1, arg2); 
MyClass.apply(null, [arg1, arg2]); 
+1

मैं उम्मीद कर रहा था कि फैक्ट्री कक्षाओं में बदलाव की आवश्यकता से बचने की उम्मीद है, लेकिन शायद मुझे करना होगा। –

+0

मूल कन्स्ट्रक्टर फ़ंक्शंस को बदलने के लिए इस विशेष समस्या को हल करने का एक खराब तरीका है। –

+0

मुझे नहीं पता कि मैं केवल इस उद्देश्य के लिए कन्स्ट्रक्टर को बदलने की सिफारिश करता हूं, लेकिन वर्ग के उपयोग के आधार पर इसका अन्य लाभ हो सकते हैं, जैसे कि [] .map के साथ सरणी तत्वों को परिवर्तित करने में सक्षम होना (जैसा कि बनाया गया है -इन संख्या, बूलियन, और स्ट्रिंग निर्माता काम करते हैं)। –

1

हैक्स हैक्स हैक्स कर रहे हैं, लेकिन शायद यह एक में थोड़ा और अधिक दूसरों की तुलना में खूबसूरत है, के बाद से वाक्य रचना बुला और तुम क्या चाहते करने के समान होगा

var instance1 = MyClass.build(["arg1","arg2"]); 
var instance2 = new MyClass("arg1","arg2"); 
: एक वस्तु का निर्माण करने के

Function.prototype.build = function(parameterArray) { 
    var functionNameResults = (/function (.{1,})\(/).exec(this.toString()); 
    var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : ""; 
    var builtObject = null; 
    if(constructorName != "") { 
     var parameterNameValues = {}, parameterNames = []; 
     for(var i = 0; i < parameterArray.length; i++) { 
     var parameterName = ("p_" + i); 
     parameterNameValues[parameterName] = parameterArray[i]; 
     parameterNames.push(("parameterNameValues." + parameterName)); 
     } 
     builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues); 
    } 
    return builtObject; 
}; 

अब आप इनमें से किसी कर सकते हैं: आप बिल्कुल मूल वर्गों को संशोधित करने की जरूरत नहीं होगी 0

दी, कुछ समारोह वस्तु के प्रोटोटाइप को संशोधित करने की तरह नहीं हो सकता है, तो आप इसे इस तरह से करते हैं और बदले में एक समारोह के रूप में उपयोग कर सकते हैं:

function build(constructorFunction, parameterArray) { 
    var functionNameResults = (/function (.{1,})\(/).exec(constructorFunction.toString()); 
    var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : ""; 
    var builtObject = null; 
    if(constructorName != "") { 
     var parameterNameValues = {}, parameterNames = []; 
     for(var i = 0; i < parameterArray.length; i++) { 
     var parameterName = ("p_" + i); 
     parameterNameValues[parameterName] = parameterArray[i]; 
     parameterNames.push(("parameterNameValues." + parameterName)); 
     } 
     builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues); 
    } 
    return builtObject; 
}; 

और फिर आप यह इतना की तरह कहेंगे:

var instance1 = build(MyClass, ["arg1","arg2"]); 

तो, मुझे उम्मीद है कि वे किसी के लिए उपयोगी हैं - वे आपको मूल कन्स्ट्रक्टर फ़ंक्शंस अकेले छोड़ने और कोड की एक साधारण पंक्ति में बाद में प्राप्त करने की अनुमति देते हैं (वर्तमान में चयनित समाधान/वर्कअराउंड के लिए आपको आवश्यक दो लाइनों के विपरीत ।

प्रतिक्रिया का स्वागत है और सराहना की जाती है।


अद्यतन: नोट करने के लिए एक दूसरी बात - इन विभिन्न तरीकों के साथ एक ही प्रकार के उदाहरण बनाने और फिर अगर उनके निर्माता गुण ही कर रहे हैं देखने के लिए जाँच करने का प्रयास - आपको लगता है कि अगर आप कभी भी मामला हो सकते हैं किसी ऑब्जेक्ट के प्रकार की जांच करने की आवश्यकता है। क्या मेरा मतलब है सबसे अच्छा निम्नलिखित कोड के रूप में रेखांकित किया गया है:

function Person(firstName, lastName) { 
    this.FirstName = firstName; 
    this.LastName = lastName; 
} 

var p1 = new Person("John", "Doe"); 
var p2 = Person.build(["Sara", "Lee"]); 

var areSameType = (p1.constructor == p2.constructor); 

अन्य हैक्स से कुछ के साथ कि कोशिश करो और देखो क्या होता है। आदर्श रूप में, आप उन्हें एक ही प्रकार के बनना चाहते हैं।


चेतावनी: जैसा कि टिप्पणी में बताया गया है, यह उन निर्माता कार्यों कि अज्ञात फ़ंक्शन सिंटैक्स का उपयोग बनाई गई हैं के लिए काम नहीं करेगा, यानी

MyNamespace.SomeClass = function() { /*...*/ }; 

जब तक आप उन्हें इस प्रकार बना:

MyNamespace.SomeClass = function SomeClass() { /*...*/ }; 

ऊपर प्रदान किया गया समाधान आपके लिए उपयोगी हो सकता है या नहीं भी हो सकता है, आपको समझना होगा कि आप ar के लिए क्या कर रहे हैं अपनी विशेष जरूरतों के लिए सबसे अच्छे समाधान पर चले जाओ, और आपको मेरा समाधान "काम" करने के लिए क्या हो रहा है इसके बारे में जानने की आवश्यकता है। यदि आप समझ नहीं पाते कि मेरा समाधान कैसे काम करता है, तो इसे समझने के लिए समय व्यतीत करें।


वैकल्पिक समाधान: नहीं एक अन्य विकल्प की अनदेखी, यहाँ अन्य तरीकों से आप (ऊपर दृष्टिकोण के समान कैविएट्स के साथ) इस बिल्ली त्वचा सकता है में से एक, यह एक थोड़ा और गूढ़ है:

function partial(func/*, 0..n args */) { 
    var args = Array.prototype.slice.call(arguments, 1); 
    return function() { 
     var allArguments = args.concat(Array.prototype.slice.call(arguments)); 
     return func.apply(this, allArguments); 
    }; 
} 

Function.prototype.build = function(args) { 
    var constructor = this; 
    for(var i = 0; i < args.length; i++) { 
     constructor = partial(constructor, args[i]); 
    } 
    constructor.prototype = this.prototype; 
    var builtObject = new constructor(); 
    builtObject.constructor = this; 
    return builtObject; 
}; 

आनंद लें!

+0

वाह। मैंने यह भी नहीं सोचा था कि एक फंक्शन कन्स्ट्रक्टर था और यह फंक्शन बॉडी के रूप में एक स्ट्रिंग ले लिया! केवल मेरी टिप्पणी है कि मुझे लगता है कि मेरा कन्स्ट्रक्टरनाम रेगेक्सपी फ़ंक्शन नाम और खोलने के बीच कोई स्थान नहीं लेता है (- जो ठीक करना आसान है।: पी –

+0

हाँ, भाषा के लिए कई रहस्य हैं, मुझे लगता है। :) मेरे regex के लिए, आप शायद tweaking की आवश्यकता के बारे में सही हैं, यह ठीक काम करता है-अगर आप इसका उपयोग फ़ायरफ़ॉक्स के लिए कर रहे हैं क्योंकि क्योंकि जब आप किसी फ़ंक्शन पर ToString() को कॉल करते हैं, तो कम से कम मेरे सभी परीक्षणों से, यह स्ट्रिंग लौटाता है, नहीं नाम और उद्घाटन माता-पिता के बीच की जगह - बेशक, अगर हम केवल इतनी सरल दुनिया में रह सकें, है ना? मैं खुशी से मदद कर सकता है! –

+2

यह एक गैर-नामित कक्षाओं के साथ काम नहीं करता है। उदाहरण के लिए MyNamespace.MyClass = function() {...}। –

2

ध्यान दें कि

  • new myClass() 
    
    किसी भी तर्क के बिना

    , विफल हो सकता है के बाद से निर्माता समारोह तर्क के अस्तित्व पर भरोसा कर सकते हैं।

  • myClass.apply(something, args) 
    

    कई मामलों में असफल हो जायेगी, खासकर यदि तिथि या संख्या की तरह देशी वर्गों पर कहा जाता है।

मुझे पता है कि "eval बुराई है", लेकिन इस मामले में आप निम्नलिखित की कोशिश करना चाहते हो सकता है:

function newApply(Cls, args) { 
    var argsWrapper = []; 
    for (var i = 0; i < args.length; i++) { 
     argsWrapper.push('args[' + i + ']'); 
    } 
    eval('var inst = new Cls(' + argsWrapper.join(',') + ');'); 
    return inst; 
} 

कि के रूप में सरल।

(यह वही काम करता है this blog post में Instance.New के रूप में)

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

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