जावास्क्रिप्ट: एक फ़ंक्शन क्लोन


115

जावास्क्रिप्ट में किसी फ़ंक्शन को क्लोन करने का सबसे तेज़ तरीका क्या है (इसके गुणों के साथ या इसके बिना)?

दो विकल्प दिमाग में आ रहे हैं eval(func.toString())और function() { return func.apply(..) }। लेकिन मैं eval के प्रदर्शन के बारे में चिंतित हूं और रैपिंग से स्टैक खराब हो जाएगा और संभवत: प्रदर्शन को नीचा दिखाएगा यदि बहुत अधिक लगाया जाए या पहले से ही लपेटा जाए।

new Function(args, body) अच्छा लग रहा है, लेकिन जेएस पार्सर के बिना जेएस और बॉडी के मौजूदा फ़ंक्शन को मैं वास्तव में कैसे विभाजित कर सकता हूं?

अग्रिम में धन्यवाद।

अद्यतन: मेरा मतलब है कि क्या करने में सक्षम है

var funcB = funcA.clone(); // where clone() is my extension
funcB.newField = {...};    // without affecting funcA

क्या आप इसका मतलब दिखा सकते हैं कि आप क्या उदाहरण दे सकते हैं।
जोशबर्के डे 2'09

ज़रूर जोड़ा गया। (15charsrequired)
एंड्री

मुझे यकीन नहीं है, लेकिन आप अपने new_function () को कॉपी कर सकते हैं; काम?
सेवजमैन

1
मुझे ऐसा नहीं लगता, यह एक निर्माता के रूप में फ़ंक्शन का उपयोग करके एक उदाहरण बनाएगा
एंड्री शेककिन

जवाबों:


54

इसे इस्तेमाल करे:

var x = function() {
    return 1;
};

var t = function(a,b,c) {
    return a+b+c;
};


Function.prototype.clone = function() {
    var that = this;
    var temp = function temporary() { return that.apply(this, arguments); };
    for(var key in this) {
        if (this.hasOwnProperty(key)) {
            temp[key] = this[key];
        }
    }
    return temp;
};

alert(x === x.clone());
alert(x() === x.clone()());

alert(t === t.clone());
alert(t(1,1,1) === t.clone()(1,1,1));
alert(t.clone()(1,1,1));

ठीक है, तो लागू एकमात्र तरीका है? मैं इस पर थोड़ा सुधार करूंगा ताकि दो बार कॉल करने पर यह दो बार लपेटे नहीं, लेकिन ठीक है।
एंड्री शेकिन

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

6
हां, मैंने मूल पोस्ट में आवेदन करने के बारे में लिखा है। समस्या यह है कि इस तरह के रैपिंग फ़ंक्शन इसका नाम नष्ट कर देता है और कई क्लोन के बाद धीमा हो जाएगा।
एंड्री शेककिन

ऐसा लगता है कि कम से कम .name संपत्ति को कम से कम प्रभावित करने का एक तरीका है: फ़ंक्शन fa () {} var fb = function () {fa.apply (यह, तर्क); }; Object.defineProperties (fb, {नाम: {value: 'fb'}});
किलरॉय

109

यहाँ एक अद्यतन जवाब है

var newFunc = oldFunc.bind({}); //clones the function with '{}' acting as it's new 'this' parameter

हालाँकि .bindजावास्क्रिप्ट की एक आधुनिक (> = iE9) विशेषता है ( MDN से अनुकूलता वर्कअराउंड के साथ )

टिप्पणियाँ

  1. यह क्लोन नहीं करता समारोह वस्तु अतिरिक्त संलग्न गुण , सहित प्रोटोटाइप संपत्ति। @Jchook को श्रेय

  2. नया फ़ंक्शन यह चर बाइंड () पर दिए गए तर्क के साथ अटक गया है, यहां तक ​​कि नए फ़ंक्शन लागू () कॉल पर भी। क्रेडिट @ केविन को

function oldFunc() {
  console.log(this.msg);
}
var newFunc = oldFunc.bind({ msg: "You shall not pass!" }); // this object is binded
newFunc.apply({ msg: "hello world" }); //logs "You shall not pass!" instead
  1. बाउंड फंक्शन ऑब्जेक्ट, इंस्टोफ न्यूफ़नक / ओल्डफंक को समान मानता है। श्रेय @Christopher को
(new newFunc()) instanceof oldFunc; //gives true
(new oldFunc()) instanceof newFunc; //gives true as well
newFunc == oldFunc; //gives false however

2
ध्यान दें कि उदाहरण के newFuncलिए अपना स्वयं का प्रोटोटाइप नहीं होगा new newFunc, जबकि oldFuncहोगा।
jchook

1
प्रैक्टिकल नकारात्मक: Instof नए और पुराने के बीच अंतर करने में सक्षम नहीं होगाFunc
क्रिस्टोफर

1
@ChristopherSwasey: यह वास्तव में एक उल्टा भी हो सकता है, जब कार्यात्मकता का विस्तार हो। लेकिन अफसोस, यह भ्रामक होगा अगर अच्छी तरह से समझा नहीं गया (जवाब में जोड़ा गया)
पिकोक्रैचर

इस उत्तर के साथ एक बड़ा मुद्दा यह है कि एक बार बाँधने के बाद, आप दूसरी बार बाँध नहीं सकते। बाद में लागू करने के लिए कॉल 'इस' वस्तु को नजरअंदाज कर दिया। उदाहरण: var f = function() { console.log('hello ' + this.name) }जब {name: 'Bob'}'हैलो बॉब' को प्रिंट करने के लिए बाध्य किया जाता है । f.apply({name: 'Sam'})'हैलो' को भी प्रिंट करेगा, 'इस' ऑब्जेक्ट को नजरअंदाज करना।
केविन मूनी

1
नोट करने के लिए एक अन्य किनारे का मामला: कम से कम V8 में (और संभवतः अन्य इंजनों में), यह फंक्शन के व्यवहार को बदल देता है। बाध्य फ़ंक्शन पर .toString () कॉल करने से आपको function () { [native code] }पूर्ण फ़ंक्शन की सामग्री के बजाय एक स्ट्रिंग मिलेगी ।
ग्लैडस्टोन

19

यहां जेरेड के उत्तर का थोड़ा बेहतर संस्करण है। यह एक गहरी नेस्टेड फ़ंक्शन के साथ समाप्त नहीं होगा जितना अधिक आप क्लोन करते हैं। यह हमेशा मूल कहता है।

Function.prototype.clone = function() {
    var cloneObj = this;
    if(this.__isClone) {
      cloneObj = this.__clonedFrom;
    }

    var temp = function() { return cloneObj.apply(this, arguments); };
    for(var key in this) {
        temp[key] = this[key];
    }

    temp.__isClone = true;
    temp.__clonedFrom = cloneObj;

    return temp;
};

इसके अलावा, pico.creator द्वारा दिए गए अद्यतन जवाब के जवाब में, यह ध्यान देने योग्य है कि bind()जावास्क्रिप्ट 1.8.5 में जोड़े गए फ़ंक्शन में जेरेड के उत्तर के रूप में एक ही समस्या है - यह हर बार उपयोग किए जाने वाले धीमे और धीमे कार्यों के कारण घोंसला बनाए रखेगा।


2019+ में, शायद __properties के बजाय प्रतीक () का उपयोग करना बेहतर होगा।
अलेक्जेंडर मिल्स

10

जिज्ञासु, लेकिन अभी भी उपरोक्त प्रश्न के प्रदर्शन के विषय से उत्तर नहीं मिलता होने के नाते, मैं यह लिखा सार परीक्षण करने के लिए NodeJS दोनों प्रदर्शन और सभी प्रस्तुत (और रन बनाए) समाधान की विश्वसनीयता के लिए।

मैंने एक क्लोन फ़ंक्शन निर्माण की दीवार के समय और क्लोन के निष्पादन की तुलना की है। परिणाम की त्रुटियों के साथ मिलकर परिणाम जिस्ट की टिप्पणी में शामिल हैं।

प्लस मेरे दो सेंट (लेखक के सुझाव के आधार पर):

क्लोन 0 प्रतिशत (तेज लेकिन बदसूरत):

Function.prototype.clone = function() {
  var newfun;
  eval('newfun=' + this.toString());
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

क्लोन 4 प्रतिशत (धीमे लेकिन उन लोगों के लिए जो नापसंद () केवल उनके और उनके पूर्वजों के लिए ज्ञात उद्देश्यों के लिए):

Function.prototype.clone = function() {
  var newfun = new Function('return ' + this.toString())();
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

प्रदर्शन के लिए, यदि eval / नया फ़ंक्शन आवरण समाधान की तुलना में धीमा है (और यह वास्तव में फ़ंक्शन बॉडी के आकार पर निर्भर करता है), तो यह आपको नंगे फ़ंक्शन क्लोन देता है (और मेरा मतलब है कि गुणों के साथ असली उथले क्लोन) लेकिन अनावश्यक गूंज के बिना) छिपे हुए गुणों, आवरण कार्यों और स्टैक के साथ समस्याओं के साथ।

इसके अलावा हमेशा एक महत्वपूर्ण कारक है जिसे आपको ध्यान में रखना चाहिए: कम कोड, गलतियों के लिए कम स्थान।

Eval / new फ़ंक्शन का उपयोग करने का नकारात्मक पक्ष यह है कि क्लोन और मूल फ़ंक्शन अलग-अलग स्कोप में काम करेंगे। यह उन कार्यों के साथ अच्छी तरह से काम नहीं करेगा जो स्कूप किए गए चर का उपयोग कर रहे हैं। बाइंड-जैसे रैपिंग का उपयोग करने वाले समाधान स्वतंत्र हैं।


इस बात से सावधान रहें कि eval और नए फंक्शन समतुल्य नहीं हैं। eval opers स्थानीय दायरे पर, लेकिन फ़ंक्शन नहीं करता है। यह फ़ंक्शन कोड के अंदर से अन्य चर तक पहुँचने के लिए बांधने में समस्याएं पैदा कर सकता है। विस्तृत विवरण के लिए perfectionkills.com/global-eval-what-are-the-options देखें ।
पियरे

सही और नए या नए फंक्शन का उपयोग करके आप फ़ंक्शन को उसके मूल दायरे के साथ एक साथ क्लोन नहीं कर सकते।
शाही

तथ्य की बात के रूप में: एक बार जब आप Object.assign(newfun.prototype, this.prototype);रिटर्न स्टेटमेंट (स्वच्छ संस्करण) से पहले जोड़ते हैं , तो आपका तरीका सबसे अच्छा जवाब है।
विविक जूल

9

इस पद्धति को काम करना बहुत रोमांचक था, इसलिए यह फ़ंक्शन कॉल का उपयोग करके फ़ंक्शन का एक क्लोन बनाता है।

MDN फ़ंक्शन संदर्भ में वर्णित क्लोजर के बारे में कुछ सीमाएँ

function cloneFunc( func ) {
  var reFn = /^function\s*([^\s(]*)\s*\(([^)]*)\)[^{]*\{([^]*)\}$/gi
    , s = func.toString().replace(/^\s|\s$/g, '')
    , m = reFn.exec(s);
  if (!m || !m.length) return; 
  var conf = {
      name : m[1] || '',
      args : m[2].replace(/\s+/g,'').split(','),
      body : m[3] || ''
  }
  var clone = Function.prototype.constructor.apply(this, [].concat(conf.args, conf.body));
  return clone;
}

का आनंद लें।


5

लघु और सरल:

Function.prototype.clone = function() {
  return new Function('return ' + this.toString())();
};

1
इसके अलावा, यह हुड के तहत एक प्रकार के निष्कासन का उपयोग करता है, जो विभिन्न कारणों से सबसे अच्छी तरह से बचा जाता है (यहां नहीं मिलेगा, यह अन्य स्थानों में शामिल है)।
एंड्रयू फॉल्कनर

2
इस समाधान का अपना स्थान है (जब आप किसी उपयोगकर्ता फ़ंक्शन को क्लोन कर रहे हैं और ध्यान नहीं देते हैं कि eval का उपयोग किया जाता है)
लॉयड

2
यह फ़ंक्शन स्कोप भी खो देता है। नया फ़ंक्शन बाहरी स्कोप वैरियों को संदर्भित कर सकता है जो अब नए स्कोप में मौजूद नहीं हैं।
trusktr

4
const oldFunction = params => {
  // do something
};

const clonedFunction = (...args) => oldFunction(...args);

3
const clonedFunction = Object.assign(() => {}, originalFunction);

ध्यान दें कि यह अधूरा है। यह गुणों को कॉपी करेगा originalFunction, लेकिन आपके द्वारा चलाए जाने पर वास्तव में इसे निष्पादित नहीं करेगा clonedFunction, जो कि अप्रत्याशित है।
डेविड कैलहॉन

2

यह उत्तर उन लोगों के लिए है जो किसी फ़ंक्शन को उनके इच्छित उपयोग के उत्तर के रूप में देखते हैं, लेकिन जिन्हें वास्तव में किसी फ़ंक्शन को क्लोन करने की आवश्यकता नहीं है , क्योंकि वे वास्तव में जो चाहते हैं वह एक ही फ़ंक्शन के लिए विभिन्न गुणों को संलग्न करने में सक्षम है, लेकिन केवल उस फ़ंक्शन को एक बार घोषित करें।

एक फंक्शन बनाने वाला फंक्शन बनाकर ऐसा करें:

function createFunction(param1, param2) {
   function doSomething() {
      console.log('in the function!');
   }
   // Assign properties to `doSomething` if desired, perhaps based
   // on the arguments passed into `param1` and `param2`. Or,
   // even return a different function from among a group of them.
   return doSomething;
};

let a = createFunction();
a.something = 1;
let b = createFunction();
b.something = 2; // does not overwrite a.something
console.log(a.something);
a();
b();

यह बिल्कुल वैसा नहीं है जैसा कि आपने उल्लिखित किया है, हालांकि, यह इस बात पर निर्भर करता है कि आप उस फ़ंक्शन का उपयोग कैसे करना चाहते हैं जिसे आप क्लोन करना चाहते हैं। यह अधिक मेमोरी का उपयोग भी करता है क्योंकि यह वास्तव में फ़ंक्शन की कई प्रतियाँ बनाता है, एक बार मंगलाचरण। हालांकि, यह तकनीक जटिल cloneफ़ंक्शन की आवश्यकता के बिना कुछ लोगों के उपयोग के मामले को हल कर सकती है।


1

बस सोच रहा था - जब आप प्रोटोटाइप हैं तो आप किसी फ़ंक्शन को क्लोन क्यों करना चाहते हैं और फ़ंक्शन कॉल के दायरे को अपनी इच्छा के अनुसार सेट कर सकते हैं?

 var funcA = {};
 funcA.data = 'something';
 funcA.changeData = function(d){ this.data = d; }

 var funcB = {};
 funcB.data = 'else';

 funcA.changeData.call(funcB.data);

 alert(funcA.data + ' ' + funcB.data);

1
यदि फ़ंक्शन के क्षेत्रों को स्वयं बदलने का कारण है (स्व-निहित कैश, 'स्थिर' गुण), तो ऐसी स्थिति होती है जब मैं किसी फ़ंक्शन को क्लोन करना चाहता हूं और मूल को प्रभावित किए बिना इसे संशोधित करना चाहता हूं।
एंड्रे शेकिन

मेरा मतलब फंक्शन के गुणों से है।
एंड्री शेकिन

1
कार्यों में किसी भी वस्तु की तरह गुण हो सकते हैं, इसीलिए
राडु सिमियोनेस्कु

1

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

_cloneFunction = function(_function){
    var _arguments, _body, _result;
    var _regexFunction = /^function[\s]+[\w]*\(([\w\s,_\$]*)?\)\{(.*)\}$/;
    var _regexArguments = /((?!=^|,)([\w\$_]))+/g;
    var _matches = _function.toString().match(_regexFunction)
    if(_matches){
        if(_matches[1]){
            _result = _matches[1].match(_regexArguments);
        }else{
            _result = [];
        }
        _result.push(_matches[2]);
    }else{
        _result = [];
    }
    var _clone = Function.apply(Function, _result);
    // if you want to add attached properties
    for(var _key in _function){
        _clone[_key] = _function[_key];
    }
    return _clone;
}

एक साधारण परीक्षण:

(function(){
    var _clone, _functions, _key, _subKey;
    _functions = [
        function(){ return 'anonymous function'; }
        ,function Foo(){ return 'named function'; }
        ,function Bar(){ var a = function(){ return 'function with internal function declaration'; }; return a; }
        ,function Biz(a,boo,c){ return 'function with parameters'; }
    ];
    _functions[0].a = 'a';
    _functions[0].b = 'b';
    _functions[1].b = 'b';
    for(_key in _functions){
        _clone = window._cloneFunction(_functions[_key]);
        console.log(_clone.toString(), _clone);
        console.log('keys:');
        for(_subKey in _clone){
            console.log('\t', _subKey, ': ', _clone[_subKey]);
        }
    }
})()

हालांकि ये क्लोन किसी भी बंद चर पर अपना नाम और गुंजाइश खो देंगे।


1

मैंने अपने तरीके से जेरेड के उत्तर को प्रभावित किया है:

    Function.prototype.clone = function() {
        var that = this;
        function newThat() {
            return (new that(
                arguments[0],
                arguments[1],
                arguments[2],
                arguments[3],
                arguments[4],
                arguments[5],
                arguments[6],
                arguments[7],
                arguments[8],
                arguments[9]
            ));
        }
        function __clone__() {
            if (this instanceof __clone__) {
                return newThat.apply(null, arguments);
            }
            return that.apply(this, arguments);
        }
        for(var key in this ) {
            if (this.hasOwnProperty(key)) {
                __clone__[key] = this[key];
            }
        }
        return __clone__;
    };

1) अब यह कंस्ट्रक्टर्स के क्लोनिंग का समर्थन करता है (नए के साथ कॉल कर सकता है); उस मामले में केवल 10 तर्क लगते हैं (आप इसे अलग-अलग कर सकते हैं) - मूल कंस्ट्रक्टर में सभी तर्कों को पारित करने की असंभवता के कारण

2) सब कुछ सही बंद में है


इसके बजाय arguments[0], arguments[1] /*[...]*/आप बस का उपयोग क्यों नहीं करते ...arguments? 1) तर्क की राशि के बारे में कोई निर्भरता नहीं है (यहां 10 तक सीमित है) 2
विक्की

प्रसार ऑपरेटर के उपयोग के साथ, यह निश्चित रूप से कार्यों के लिए मेरी ओजी क्लोनिंग विधि होगी, बहुत कुछ।
विविक जूल

0
function cloneFunction(Func, ...args) {
  function newThat(...args2) {
    return new Func(...args2);
  }
  function clone() {
    if (this instanceof clone) {
      return newThat(...args);
    }
    return Func.apply(this, args);
  }
  for (const key in Func) {
    if (Func.hasOwnProperty(key)) {
      clone[key] = Func[key];
    }
  }
  Object.defineProperty(clone, 'name', { value: Func.name, configurable: true })
  return clone
};

function myFunction() {
  console.log('Called Function')
}

myFunction.value = 'something';

const newFunction = cloneFunction(myFunction);

newFunction.another = 'somethingelse';

console.log('Equal? ', newFunction === myFunction);
console.log('Names: ', myFunction.name, newFunction.name);
console.log(myFunction);
console.log(newFunction);
console.log('InstanceOf? ', newFunction instanceof myFunction);

myFunction();
newFunction();

हालांकि मैं इसका उपयोग करने की कभी भी सिफारिश नहीं करूंगा, लेकिन मैंने सोचा कि यह कुछ ऐसी प्रथाओं को ले कर एक और अधिक सटीक क्लोन के साथ आने के लिए एक दिलचस्प छोटी चुनौती होगी जो सबसे अच्छा लग रहा था और इसे थोड़ा ठीक कर रहा था। लॉग्स का परिणाम:

Equal?  false
Names:  myFunction myFunction
{ [Function: myFunction] value: 'something' }
{ [Function: myFunction] value: 'something', another: 'somethingelse' }
InstanceOf?  false
Called Function
Called Function

0
const clone = (fn, context = this) => {
  // Creates a new function, optionally preserving desired context.
  const newFn = fn.bind(context);

  // Shallow copies over function properties, if any.
  return Object.assign(newFn, fn);
}

// Usage:

// Setup the function to copy from.
const log = (...args) => console.log(...args);
log.testProperty = 1;

// Clone and make sure the function and properties are intact.
const log2 = clone(log);
log2('foo');
// -> 'foo'
log2.testProperty;
// -> 1

// Make sure tweaks to the clone function's properties don't affect the original function properties.
log2.testProperty = 2;
log2.testProperty;
// -> 2
log.testProperty;
// -> 1

यह क्लोन फ़ंक्शन:

  1. संदर्भ को संरक्षित करता है।
  2. एक आवरण है, और मूल कार्य चलाता है।
  3. फ़ंक्शन गुणों पर प्रतियां।

ध्यान दें कि यह संस्करण केवल एक उथली प्रतिलिपि करता है। यदि आपके फ़ंक्शन में ऑब्जेक्ट्स गुण के रूप में हैं, तो मूल ऑब्जेक्ट का संदर्भ संरक्षित है (ऑब्जेक्ट स्प्रेड या ऑब्जेक्ट.सिग्न के समान व्यवहार)। इसका मतलब यह है कि क्लोन फ़ंक्शन में गहरे गुण बदलने से मूल फ़ंक्शन में संदर्भित ऑब्जेक्ट प्रभावित होगा!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.