2011-09-24 15 views
47
// Don't break the function prototype. 
// pd - https://github.com/Raynos/pd 
var proto = Object.create(Function.prototype, pd({ 
    "prop": 42 
})); 

var f = function() { return "is a function"; }; 
f.__proto__ = proto; 

console.log(f.hasOwnProperty("prop")); // false 
console.log(f.prop); // 42 
console.log(f()); // "is a function" 

.__proto__ गैर-मानक और बहिष्कृत है।मैं जावास्क्रिप्ट कार्यों का वारिस कैसे करूं?

मुझे प्रोटोटाइपिक रूप से ऑब्जेक्ट बनाने का उत्तराधिकारी कैसे प्राप्त करना चाहिए, लेकिन यह ऑब्जेक्ट एक कार्य हो रहा है।

Object.create ऑब्जेक्ट को कोई फंक्शन नहीं देता है।

new Constructor नहीं एक वस्तु एक समारोह देता है।

प्रेरणा: - एक क्रॉस-ब्राउज़र finherit

var finherit = function (parent, child) { 
    var f = function() { 
     parent.apply(this, arguments); 
     child.apply(this, arguments); 
    }; 
    f.__proto__ = parent; 
    Object.keys(child).forEach(function _copy(key) { 
     f[key] = child[key]; 
    }); 
    return f; 
}; 

मैं नहीं मानता कि यह संभव है, तो हम शायद करने के लिए एक प्रस्ताव पेश करने चाहिए Function.create एस-पर चर्चा मेलिंग सूची

/* 
    Creates a new function whose prototype is proto. 
    The function body is the same as the function fbody. 
    The hash of propertydescriptors props is passed to defineproperties just like 
    Object.create does. 
*/ 
Function.create = (function() { 
    var functionBody = function _getFunctionBody(f) { 
    return f.toString().replace(/.+\{/, "").replace(/\}$/, ""); 
    }; 
    var letters = "abcdefghijklmnopqrstuvwxyz".split(""); 

    return function _create(proto, fbody, props) { 
    var parameters = letters.slice(0, fbody.length); 
    parameters.push(functionBody(fbody)); 
    var f = Function.apply(this, parameters); 
    f.__proto__ = proto; 
    Object.defineProperties(f, props); 
    return f; 
    }; 
})(); 

Related es-discuss mail

में उल्लेख किया es-पर चर्चा धागा वहां मौजूद एक ईएस: स्ट्रॉमैन <| प्रोटोटाइप ऑपरेटर जो इसके लिए अनुमति देगा।

की क्या यह <|

var f1 = function() { 
    console.log("do things"); 
}; 

f1.method = function() { return 42; }; 

var f2 = f1 <| function() { 
    super(); 
    console.log("do more things"); 
} 
console.log(f1.isPrototypeOf(f2)); // true 
console.log(f2()); // do things do more things 
console.log(f2.hasOwnProperty("method")); // false 
console.log(f2.method()); // 42 
+0

'console.log (f.apply);' 'अपरिभाषित 'वापस आ जाएगा ... तो इस तरह से किसी भी तरह से इष्टतम नहीं है ... अभी भी एक विकल्प के बारे में सोच रहा है। –

+0

@ फ़ेलिक्सक्लिंग मैं भूल गया कि मुझे 'फंक्शन.प्रोटोटाइप' से उत्तराधिकारी होना चाहिए – Raynos

+0

वहां [यह सवाल] था (http://stackoverflow.com/questions/6564481/javascript-polymorphic-callable-objects) मुझे थोड़ी देर पहले सामना करना पड़ा पूछताछ का संदर्भ [इस आलेख] (http://ajaxian.com/archives/javascript-tips-for-rookies-and-gurus) जो * "पॉलिमॉर्फिक कॉल करने योग्य ऑब्जेक्ट्स" * बनाने के लिए एक पैटर्न दिखाता है। सुनिश्चित नहीं है कि आप इसके बाद क्या कर रहे हैं, लेकिन एक लायक हो सकता है। – user113716

उत्तर

9

मुझे आशा है कि मैं यह सही समझ रहा हूँ का उपयोग कर की तरह लग रहे हैं देखते हैं।

मेरा मानना ​​है कि आप एक functor दोनों एक पूर्वनिर्धारित प्रोटोटाइप (हाँ, एक वर्ग, न कि केवल एक क्लासिक वर्ग) के साथ ही सीधे प्रतिदेय का एक उदाहरण है कि करना चाहते हैं? सही? यदि ऐसा है, तो यह सही समझ में आता है और बहुत शक्तिशाली और लचीला है (विशेष रूप से जावास्क्रिप्ट जैसे अत्यधिक असीमित वातावरण में)। दुख की बात है कि इसमें __proto__ जोड़ तोड़ के बिना सुंदर ढंग से जावास्क्रिप्ट में करने के लिए कोई रास्ता नहीं है। आप एक अज्ञात फ़ंक्शन को फैक्टर करके और प्रॉक्सी क्लास के रूप में कार्य करने के लिए सभी विधियों (जो कि आप जिस दिशा में जा रहे थे) के सभी संदर्भों की प्रतिलिपि बनाकर कर सकते हैं। इसके लिए डाउनसाइड्स हैं ...

  1. रनटाइम के मामले में यह बहुत महंगा है।
  2. (functorObj instanceof MyClass) कभी भी true नहीं होगा।
  3. गुण सीधे पहुंच योग्य नहीं होंगे (यदि वे सभी संदर्भ द्वारा असाइन किए गए थे तो यह एक अलग कहानी होगी, लेकिन प्राइमेटिव्स को मूल्य द्वारा असाइन किया जाता है)। इसे defineProperty के माध्यम से एक्सेसर्स के साथ हल किया जा सकता है या यदि आवश्यक हो तो बस एक्सेसर विधियों का नाम दिया जा सकता है (ऐसा लगता है कि आप यही चाहते हैं, बस फंक्चर में सभी गुणों को defineProperty के साथ केवल फंक्शंस के बजाय गेटर्स/सेटर्स के माध्यम से जोड़ें। क्रॉस-इंजन समर्थन/पिछड़ा संगतता की आवश्यकता है)।
  4. आप किनारे के मामलों में भाग लेने की संभावना रखते हैं जहां अंतिम देशी प्रोटोटाइप (जैसे ऑब्जेक्ट.प्रोटोटाइप या ऐरे.प्रोटोटाइप [यदि आप इसे विरासत में रखते हैं]) उम्मीद के अनुसार काम नहीं कर सकते हैं।
  5. functorObj(someArg) कॉलिंग हमेशा कर देगा this संदर्भ वस्तु हो, अगर यह functorObj.call(someOtherObj, someArg) कहा जाता है की परवाह किए बिना (यह स्थिति नहीं है विधि हालांकि कॉल के लिए)
  6. क्योंकि functor वस्तु अनुरोध समय में बनाई गई है, यह हो जाएगा समय में बंद और प्रारंभिक प्रोटोटाइप में हेरफेर करने से आवंटित मज़ेदार वस्तुओं को प्रभावित नहीं किया जाएगा जैसे सामान्य वस्तु प्रभावित होगी (MyClass.prototype को संशोधित करना किसी भी मज़ेदार वस्तुओं को प्रभावित नहीं करेगा और विपरीत भी सत्य है)।

यदि आप इसे धीरे-धीरे उपयोग करते हैं, तो इनमें से कोई भी बड़ा सौदा नहीं होना चाहिए।

अपने वर्ग के अपने प्रोटोटाइप में की तरह कुछ को परिभाषित ...

// This is you're emulated "overloaded" call() operator. 
MyClass.prototype.execute = function() { 
    alert('I have been called like a function but have (semi-)proper access to this!'); 
}; 

MyClass.prototype.asFunctor = function(/* templateFunction */) { 
    if ((typeof arguments[0] !== 'function') && (typeof this.execute !== 'function')) 
     throw new TypeError('You really should define the calling operator for a functor shouldn\'t you?'); 
    // This is both the resulting functor proxy object as well as the proxy call function 
    var res = function() { 
     var ret; 
     if (res.templateFunction !== null) 
     // the this context here could be res.asObject, or res, or whatever your goal is here 
     ret = res.templateFunction.call(this, arguments); 
     if (typeof res.asObject.execute === 'function') 
     ret = res.asObject.execute.apply(res.asObject, arguments); 
     return ret; 
    }; 
    res.asObject = this; 
    res.templateFunction = (typeof arguments[0] === 'function') ? arguments[0] : null; 
    for (var k in this) { 
     if (typeof this[k] === 'function') { 
     res[k] = (function(reference) { 
      var m = function() { 
       return m.proxyReference.apply((this === res) ? res.asObject : this, arguments); 
      }; 
      m.proxyReference = reference; 
      return m; 
     })(this.asObject[k]); 
     } 
    } 
    return res; 
}; 

उपयोग परिणामस्वरूप कुछ ऐसा दिखाई देगा ...

var aobj = new MyClass(); 
var afunctor = aobj.asFunctor(); 
aobj.someMethodOfMine(); // << works 
afunctor.someMethodOfMine(); // << works exactly like the previous call (including the this context). 
afunctor('hello'); // << works by calling aobj.execute('hello'); 

(aobj instanceof MyClass) // << true 
(afunctor instanceof MyClass) // << false 
(afunctor.asObject === aobj) // << true 

// to bind with a previous function... 
var afunctor = (new MyClass()).asFunctor(function() { alert('I am the original call'); }); 
afunctor() // << first calls the original, then execute(); 
// To simply wrap a previous function, don't define execute() in the prototype. 

तुम भी श्रृंखला अनगिनत अन्य वस्तुओं/कार्यों के लिए बाध्य कर सकता है/आदि जब तक गायों घर नहीं आया। प्रॉक्सी कॉल को थोड़ा सा रिफैक्टर करें।

उम्मीद है कि मदद करता है। ओह, और निश्चित रूप से आप कारखाने के प्रवाह को बदल सकते हैं ताकि new ऑपरेटर के बिना बुलाया गया एक कन्स्ट्रक्टर फिर एक नई वस्तु को तुरंत चालू कर देता है और मज़ेदार वस्तु को वापस कर देता है। हालांकि आप पसंद करते हैं (आप निश्चित रूप से इसे अन्य तरीकों से भी कर सकते हैं)।

अंत में, किसी भी समारोह एक तरह की और अधिक सुरुचिपूर्ण कुछ ही देर में एक functor के लिए निष्पादन ऑपरेटर बन सिर्फ प्रॉक्सी समारोह Function.prototype की एक विधि बनाने के लिए और यह वस्तु पारित रैप करने के लिए अगर आप की तरह कुछ (क्या करना चाहते करने के लिए आप (डुप्लिकेट) प्रश्न this और पाठ्यक्रम) के तर्क के साथ this साथ templateFunction स्वैप करने के लिए ...

var functor = (function() { /* something */ }).asFunctor(aobj); 
+3

कूल। मैंने कभी फंतासी के बारे में नहीं सुना होगा। मैं निश्चित रूप से अपने अगले कुत्ते के मजेदार नाम देने वाला हूँ। – Ben

+0

यह वास्तव में एक छद्म मज़ेदार (हाँ, हास्यास्पद नाम) सब कुछ proxying है। यह ऐसा करने के लिए एक बेवकूफ तरीका है। कुछ भाषाएं इस मूल रूप से समर्थन करती हैं। यदि ईएमसीएस्क्रिप्ट 5 ने '__proto__' हेरफेर (या समकक्ष वैकल्पिक मानक), ** या ** ऑपरेटर अधिभार की अनुमति दी है, तो जावास्क्रिप्ट होगा। दुख की बात यह नहीं है। इसके लिए यह शक्ति और लचीलापन है ... श्वास। –

+0

@ mr.stobbe क्या आप इसके बजाय ऐसा करने के लिए ES6 प्रॉक्सी का उपयोग करने में एक स्टैब कर सकते हैं? (क्रोम और एफएफ में काम करता है)। वैकल्पिक रूप से आप धोखा दे सकते हैं और '<|' मौजूद हो सकते हैं ('protoOf = function (proto, thing) {thing .__ proto__ = proto;}') – Raynos

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