159

What is a fastest way to clone a function in JavaScript (with or without its properties)?

Two options coming to mind are eval(func.toString()) and function() { return func.apply(..) }. But I am worried about performance of eval and wrapping will make stack worse and will probably degrade performance if applied a lot or applied to already wrapped.

new Function(args, body) looks nice, but how exactly can I reliable split existing function to args and body without a JS parser in JS?

Update: What I mean is being able to do

var funcB = funcA.clone(); // where clone() is my extension
funcB.newField = {...};    // without affecting funcA
4
  • Can you give an example showing what you mean. Commented Dec 2, 2009 at 15:25
  • Sure, added. (15charsrequired) Commented Dec 2, 2009 at 15:29
  • I'm not sure, but could copy = new your_function(); work? Commented Dec 2, 2009 at 15:33
  • 2
    I do not think so, it will create an instance using function as a constructor Commented Dec 2, 2009 at 15:47

17 Answers 17

149

Here is an updated answer

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

However .bind is a modern ( >=iE9 ) feature of JavaScript (with a compatibility workaround from MDN)

Notes

  1. It does not clone the function object additional attached properties, including the prototype property. Credit to @jchook

  2. The new function this variable is stuck with the argument given on bind(), even on new function apply() calls. Credit to @Kevin

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. Bound function object, instanceof treats newFunc/oldFunc as the same. Credit to @Christopher
(new newFunc()) instanceof oldFunc; //gives true
(new oldFunc()) instanceof newFunc; //gives true as well
newFunc == oldFunc; //gives false however
Sign up to request clarification or add additional context in comments.

6 Comments

Note that newFunc will NOT have its own prototype for new newFunc instances, while oldFunc will.
Image
Practical downside: instanceof won't be able to distinguish between newFunc and oldFunc
@ChristopherSwasey: It can actually be an upside as well, when extending functionalities. But alas, it will be confusing if not understood well (added to answer)
A big issue with this answer is that once you bind, you cannot bind a second time. Subsequent calls to apply also ignore the 'this' object passed. Example: var f = function() { console.log('hello ' + this.name) } when bound to {name: 'Bob'} prints 'hello Bob'. f.apply({name: 'Sam'}) will also print 'hello Bob', ignoring the 'this' object.
One other edge case to note: At least in V8 (and possibly other engines), this changes the behavior of Function.prototype.toString(). Calling .toString() on the bound function will give you a string like function () { [native code] } instead of the full function's contents.
|
64

try this:

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

4 Comments

Ok, so apply is the only way? I would improve on this a bit so that it does not wrap twice when called twice, but otherwise, ok.
apply is used to pass the arguments easily. also, this will work for instances where you want to clone a constructor.
yes, I wrote about apply in the original post. problem is that wrapping function like this destroys its name and will slow down after many clones.
There seems to be one way to at least affect the .name property like this: function fa () {} var fb = function() { fa.apply(this, arguments); }; Object.defineProperties(fb, { name: { value: 'fb' } });
24

Here's a slightly better version of Jared's answer. This one won't end up with deeply nested functions the more you clone. It always calls the original.

Function.prototype.clone = function() {
    const cloneTarget = Symbol.for("cloneTarget");
    const targetFn = this[cloneTarget] ?? this;

    function clone() {
      return targetFn.apply(this, arguments);
    };

    for (const key in targetFn) {
      clone[key] = this[key];
    }

    clone[cloneTarget] = targetFn;

    return clone;
};

Also, in response to the updated answer given by pico.creator, it is worth noting that the bind() function added in Javascript 1.8.5 has the same problem as Jared's answer - it will keep nesting causing slower and slower functions each time it is used.

4 Comments

in 2019+, probably better to use Symbol() instead of __properties.
Why remove the if (this.hasOwnProperty(key)) that was present in Jared's answer?
You sure this works? Looks like you're defining a new Symbol on every call.
@PedroA Good catch! You are correct. I updated the implementation recently and introduced that bug. Fixed now.
14

Being curious but still unable to find the answer to the performance topic of the question above, I wrote this gist for nodejs to test both the performance and reliability of all presented (and scored) solutions.

I've compared the wall times of a clone function creation and the execution of a clone. The results together with assertion errors are included in the gist's comment.

Plus my two cents (based on the author's suggestion):

clone0 cent (faster but uglier):

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

clone4 cent (slower but for those who dislike eval() for purposes known only to them and their ancestors):

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

As for the performance, if eval/new Function is slower than wrapper solution (and it really depends on the function body size), it gives you bare function clone (and I mean the true shallow clone with properties but unshared state) without unnecessary fuzz with hidden properties, wrapper functions and problems with stack.

Plus there is always one important factor you need to take into consideration: the less code, the less places for mistakes.

The downside of using the eval/new Function is that the clone and the original function will operate in different scopes. It won't work well with functions that are using scoped variables. The solutions using bind-like wrapping are scope independent.

3 Comments

Beware that eval and new Function are not equivalent. eval opers on local scope, but Function doesn't. This could lead to problems tying to access other variables from inside the function code. See perfectionkills.com/global-eval-what-are-the-options for an extensive explanation.
Right and by using either eval or new Function you can't clone the function together with its original scope.
As a matter of fact: once you add Object.assign(newfun.prototype, this.prototype); before the return statement (clean version), your method is the best answer.
10

It was pretty exciting to make this method work, so it makes a clone of a function using Function call.

Some limitations about closures described at MDN Function Reference

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

Enjoy.

Comments

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

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

1 Comment

although this works for simple cases, for example here function a(){ }; a.b = function b(){console.log(this)} it would not work for cloning a or for cloning b. (the copy of a would not have the variable b, and the copy of b would print the wrong value for this)
6

Short and simple:

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

4 Comments

Also, it uses a variant of eval under the hood, which is best avoided for a variety of reasons (won't get into that here, it's covered in 1000s of other places).
this solution has its place (when you're cloning a user function and don't care that eval is used)
This also loses function scope. The new function may refer to outer scope vars that no longer exist in the new scope.
this won't work with hermes engine of react native
4
const clonedFunction = Object.assign(() => {}, originalFunction);

1 Comment

Note that this is incomplete. This will copy the properties from originalFunction, but won't actually execute it when you run clonedFunction, which is unexpected.
4
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

This clone function:

  1. Preserves context.
  2. Is a wrapper, and runs the original function.
  3. Copies over function properties.

Note that this version only performs a shallow copy. If your function has objects as properties, the reference to the original object is preserved (same behavior as Object spread or Object.assign). This means that changing deep properties in the cloned function will affect the object referenced in the original function!

Comments

3

Here's a vanilla ES5 solution (that even works for classes).

Functions and classes retain their original names, you can clone clones of clones without any binding issues, and no need for eval.

(the first solution must be declared globally; the second solution is more verbose, but can be declared in any scope) ((both functions only work when cloning functions that reference globally-reachable content))

function dirtyClone(class_or_function){
    
  if(typeof class_or_function !== "function"){

    console.log("wrong input type");

    return false;
  }


  let stringVersion = class_or_function.toString();

  let newFunction = 'dirtyClone.arr.push(' + stringVersion + ')';


  let funScript = document.createElement("SCRIPT");

  funScript.text = newFunction;

  document.body.append(funScript);

  funScript.remove();


  let last = dirtyClone.arr.length-1;

  dirtyClone.arr[last].prototype = class_or_function.prototype;

  return dirtyClone.arr[last];
}
dirtyClone.arr = [];



// TESTS
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

function aFunc(x){console.log(x);}

let newFunc = dirtyClone(aFunc);
newFunc("y");

let newAni = dirtyClone(Animal);
let nA = new newAni("person");
nA.speak();

let newDog = dirtyClone(Dog);
let nD = new newDog("mutt");
nD.speak();

console.log({newFunc});
console.log({newAni});
console.log({newDog});

Just in case there are properties on your original function, here's a solution that'll deeply handle those too:

let dirtyDeepClone = (function(){
    // Create a non-colliding variable name  
    // for an array that will hold functions.
    let alfUUID = "alf_" + makeUUID();
    
    // Create a new script element.
    let scriptEl = document.createElement('SCRIPT');
    
    // Add a non-colliding, object declaration 
    // to that new script element's text.
    scriptEl.text = alfUUID + " = [];";
    
    // Append the new script element to the document's body
    document.body.append(scriptEl);
                

    // The function that does the magic
    function dirtyDeepClone(class_or_function){
      
        if(typeof class_or_function !== "function"){

            console.log("wrong input type");

            return false;
        }

        
        let stringVersion = class_or_function.toString();
        
        let newFunction = alfUUID + '.push(' + stringVersion + ')';
    
        
        let funScript = document.createElement("SCRIPT");

        funScript.text = newFunction;
        
        document.body.append(funScript);
    
        funScript.remove();
        
        
        let last = window[alfUUID].length-1;
        
        window[alfUUID][last] = extras(true, class_or_function, window[alfUUID][last]);
      
        window[alfUUID][last].prototype = class_or_function.prototype;
        
        return window[alfUUID][last];
    }



    ////////////////////////////////////////////////
    // SUPPORT FUNCTIONS FOR dirtyDeepClone FUNCTION
    function makeUUID(){
        
        // uuid adapted from: https://stackoverflow.com/a/21963136
        var lut = []; 
        
        for (var i=0; i<256; i++)
            lut[i] = (i<16?'0':'')+(i).toString(16);
        
        
        var d0 = Math.random()*0xffffffff|0;
        var d1 = Math.random()*0xffffffff|0;
        var d2 = Math.random()*0xffffffff|0;
        var d3 = Math.random()*0xffffffff|0;
        
        
        var UUID = lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'_'+
        lut[d1&0xff]+lut[d1>>8&0xff]+'_'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'_'+
        lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'_'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
        lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
        
        return UUID;
    }
  
  
  // Support variables for extras function
    var errorConstructor = {
        "Error":true,
        "EvalError":true,
        "RangeError":true,
        "ReferenceError":true,
        "SyntaxError":true,
        "TypeError":true,
        "URIError":true
    };
    var filledConstructor = {
        "Boolean":true,
        "Date":true,
        "String":true,
        "Number":true,
        "RegExp":true
    };
    var arrayConstructorsES5 = {
        "Array":true,
        "BigInt64Array":true,
        "BigUint64Array":true,
        "Float32Array":true,
        "Float64Array":true,
        "Int8Array":true,
        "Int16Array":true,
        "Int32Array":true,
        "Uint8Array":true,
        "Uint8ClampedArray":true,
        "Uint16Array":true,
        "Uint32Array":true,
    };
    var filledConstructorES6 = {
        "BigInt":true,
        "Symbol":true
    };


    function extras(top, from, to){
        
        // determine if obj is truthy 
        // and if obj is an object.
        if(from !== null && (typeof from === "object" || top) && !from.isActiveClone){
            
            // stifle further functions from entering this conditional
            // (initially, top === true because we are expecting that to is a function)
            top = false; 
            
            // if object was constructed
            // handle inheritance,
            // or utilize built-in constructors
            if(from.constructor && !to){

                let oType = from.constructor.name;


                if(filledConstructor[oType])
                    to = new from.constructor(from);

                else if(filledConstructorES6[oType])
                    to = from.constructor(from);

                else if(from.cloneNode)
                    to = from.cloneNode(true);

                else if(arrayConstructorsES5[oType])
                    to = new from.constructor(from.length);

                else if ( errorConstructor[oType] ){

                    if(from.stack){

                        to = new from.constructor(from.message);

                        to.stack = from.stack;
                    }

                    else
                        to = new Error(from.message + " INACCURATE OR MISSING STACK-TRACE");
                    
                }

                else // troublesome if constructor is poorly formed
                    to = new from.constructor(); 
                
            }
            
            else // loses cross-frame magic
                to = Object.create(null); 

            
            
            
            let props = Object.getOwnPropertyNames(from);

            let descriptor;


            for(let i in props){

                descriptor = Object.getOwnPropertyDescriptor( from, props[i] );
                prop = props[i];

                // recurse into descriptor, if necessary
                // and assign prop to from
                if(descriptor.value){

                    if(
                      descriptor.value !== null && 
                      typeof descriptor.value === "object" &&
                      typeof descriptor.value.constructor !== "function"
                    ){
                          from.isActiveClone = true;
                          to[prop] = extras(false, from[prop]);
                          delete from.isActiveClone;
                        
                    }
                  else
                        to[prop] = from[prop];
                }
                else
                    Object.defineProperty( to, prop, descriptor );
            }
        }
      
        else if(typeof from === "function")
            return dirtyDeepClone(from);
        
        return from;
    }
    
    return dirtyDeepClone;
})();



// TESTS
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

function aFunc(x){console.log(x);}
aFunc.g = "h";
aFunc.Fun = function(){this.a = "b";}

let newFunc = dirtyDeepClone(aFunc);
newFunc("y");
let deepNewFunc = new newFunc.Fun();
console.log(deepNewFunc);

let newAni = dirtyDeepClone(Animal);
let nA = new newAni("person");
nA.speak();

let newDog = dirtyDeepClone(Dog);
let nD = new newDog("mutt");
nD.speak();

console.log({newFunc});
console.log({newAni});
console.log({newDog});

Comments

2

I've impoved Jared's answer in my own manner:

    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) now it supports cloning of constructors (can call with new); in that case takes only 10 arguments (you can vary it) - due to impossibility of passing all arguments in original constructor

2) everything is in correct closures

2 Comments

instead of arguments[0], arguments[1] /*[...]*/ why don't you simply use ...arguments ? 1)There's no dependency regarding the amount of arguments (here limited to 10) 2)shorter
With the use of the spread operator, this would definitely be my OG cloning method for functions, thx a lot.
2

This answer is for people who see cloning a function as the answer to their desired usage, but who many not actually need to clone a function, because what they really want is simply to be able to attach different properties to the same function, but only declare that function one time.

Do this by creating a function-creating function:

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

This is not exactly the same as you have outlined, however, it depends on how you want to use the function you're wishing to clone. This also uses more memory because it actually creates multiple copies of the function, once per invocation. However, this technique may solve some people's use case without the need for a complicated clone function.

Comments

1

Just wondering - why would you want to clone a function when you have prototypes AND can set the scope of a function call to anything you wish?

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

3 Comments

If there is a reason to change fields of function itself (self-contained cache, 'static' properties), then there are situation when I want to clone a function and modify it without affecting the original one.
I mean the properties of function itself.
functions can have properties, like any object, that's why
1

If you want to create a clone using the Function constructor, something like this should work:

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

A simple test:

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

These clones will lose their names and scope for any closed over variables though.

Comments

0

in a single expression (binds a generic function call to the function, instead of wasting a this for an arbitrary direct bind, or re-evaluating its serializations which is not even possible with eg. native functions):

let scope=boundFunction?thisObject:undefined;
let funcB=Function.call.bind(funcA,scope);

for example:

let log=Function.call.bind(console.log,undefined);
let map=Function.call.bind(Array.prototype.map,[1,2,3]);
let dynamicmap=Function.call.bind(Array.prototype.map);
map(a=>a*2);
dynamicmap([1,2,3],a=>a*2);

2 Comments

This one doesn't seem to work. In my Firefox console I tried let f = x => x+2; let g = Function.call.bind(f); g(2) and got NaN, not 4 as expected.
@GlenWhitney oh arrow functions don't operate on the this value (scope), which is the first argument of Function.call, so indeed a caveat is to pass the scope first, or include it in the bind, which in an arrow function is always null/undefined: let g=Function.call.bind(f,null). i'll update the answer to disclose this.
0
const funcB = Object.assign(
  (...args) => funcA(...args),
  funcA,
  { newField: {...} },
);

This will achieve what the OP was asking for — a new function identical to the original, but with additional properties and its own identity.

Comments

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

While I would never recommend using this, I thought it would be an interesting little challenge to come up with a more precise clone by taking some of the practices that seemed to be the best and fixing it up a bit. Heres the result of the logs:

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

1 Comment

As far as I can see, the way this one is written, the supposed "clone" will always use the arguments supplied at the time of the cloneFunction call and will ignore any arguments actually supplied in their later invocations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.