Javascript: Clone a Function

JavaScript: clone a function

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

How do I correctly clone a JavaScript object?

2022 update

There's a new JS standard called structured cloning. It works in many browsers (see Can I Use).

const clone = structuredClone(object);

Old answer

To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone method to Object.prototype, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype, or other intermediate prototypes, that you don't know about? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty method.

In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype is a hidden property of a function. Also, an object's prototype is referenced with the attribute __proto__, which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes. I think __proto__ might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.

Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object's prototype is Object, then simply creating a new general object with {} will work, but if the source's prototype is some descendant of Object, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty filter, or which were in the prototype, but weren't enumerable in the first place. One solution might be to call the source object's constructor property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes. For example, a Date object stores its data as a hidden member:

function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

The date string for d1 will be 5 seconds behind that of d2. A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class. I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!

When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object, Array, Date, String, Number, or Boolean. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list. This can be accomplished with code like the following:

function clone(obj) {
var copy;

// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;

// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}

// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}

// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}

throw new Error("Unable to copy obj! Its type isn't supported.");
}

The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn't more than one reference to the same data in the object. For example:

// This would be cloneable:
var tree = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"right" : null,
"data" : 8
};

// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;

It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.

Can I copy/clone a function in JavaScript?

Storing a reference to the function
externally before calling addMethod
and calling the default validator via
that reference makes no difference.

That's exactly what should work.

jQuery.validator.methods.oldRequired = jQuery.validator.methods.required;

jQuery.validator.addMethod("required", function(value, element, param) {
// handle comboboxes with empty guids
if (someTest(element)) {
return myRequired(value, element, param);
}
return jQuery.validator.methods.oldRequired(value, element, param);
}, jQuery.validator.messages.required);

This should work too: (And the problem with this is solved)

var oldRequired = jQuery.validator.methods.required;
jQuery.validator.addMethod("required", function(value, element, param) {
// handle comboboxes with empty guids
if (someTest(element)) {
return myRequired(value, element, param);
}
return oldRequired.call(this, value, element, param);
// return jQuery.oldRequired.apply(this, arguments);
}, jQuery.validator.messages.required);

clone object with functions inside

It's not possible to stringify functions using JSON.stringify. Only a subset of primitives are valid JSON. For completeness, function source code can be retrieved with toString and rebuilt with the Function constructor, but that's messy and unnecessary.

You can use Object.assign or spread syntax:

var original = {};
original.a = true;
original.b = null;
original.c = 82;
original.d = "a string LOL";
original.e = function() {
console.log("everything does not work?");
}
original.f = [0, "a", true, false];

var copy = Object.assign({}, original);
var spread = {...original};
copy.f = [42, 45];
spread.f = [1, 2];
copy.e();
console.log(original.f, copy.f, spread.f);

What is the most efficient way to deep clone an object in JavaScript?

Native deep cloning

There's now a JS standard called "structured cloning", that works experimentally in Node 11 and later, will land in browsers, and which has polyfills for existing systems.

structuredClone(value)

If needed, loading the polyfill first:

import structuredClone from '@ungap/structured-clone';

See this answer for more details.

Older answers

Fast cloning with data loss - JSON.parse/stringify

If you do not use Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is:

JSON.parse(JSON.stringify(object))

const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
re: /.*/, // lost
}
console.log(a);
console.log(typeof a.date); // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date); // result of .toISOString()

Javascript - Cloning a prototype function

I don't think you need to clone the function, just keep a reference to it. Or more accurately, create a copy of the function with the correct this binding.

var oldFunction = anObject.aFunction.bind(anObject);
anObject.aFunction = function(a, b, c) { ... };

bind creates a copy of the given function with this specified by the first argument. It can also be used to bind arguments as well.

Clone javascript function

it will call the original function, no recursion will occur

function do_it_maker_wrapper() {
var do_it_obj = new do_it_maker();

// in this line you save/keep the reference to 'do_it' original function on a
// completely new reference.
do_it_obj.do_it_original = do_it_obj.do_it;

// this line replaces the reference to original 'do_it' with a reference to a
// new function that we create here
do_it_obj.do_it = function() {

// this still references the original 'do_it' function so that is called
do_it_obj.do_it_original();
/ * smth_else */
}
}.

var do_it_obj = new do_it_maker_wrapper();

// this property references the function that we created as a replacement for the original 'do_it'
do_it_obj.do_it();

EDIT:
with ES6 being available pretty much everywhere now you do not need to jump the hoops like that anymore and just use 'class' and 'extend' like you would in any other OO language. It actually even helps runtimes like v8 to optimize your code better and it is as fast as creating a simple hash with curly brackets '{}'

clone function is not working on JavaScript

lastChild is the last child node of any kind, including text nodes. By moving the closing </div> tag, you're adding a text node.

You could use lastElementChild instead to get only the last element in the list.

Here's a simplified example:

console.log(document.getElementById("container1").childNodes.length); // 2
console.log(document.getElementById("container2").childNodes.length); // 3
<div id="container1">
<div>x</div></div>
<div id="container2">
<div>x</div>
</div>


Related Topics



Leave a reply



Submit