I have a simple object (or hash) in Javascript:
var settings = {
link: 'http://example.com',
photo: 'http://photos.com/me.jpg'
};
I need a copy of it. Is there a settings.clone() type method that will give me another object with the same attributes? I'm using jQuery, so happy to use a jQuery utility method if one exists.
Yes, extend an empty object with the original one; that way, everything will simply be copied:
var clone = $.extend({}, settings);
Extending some filled object with another, e.g.:
$.extend({a:1}, {b:2})
will return:
{a:1, b:2}
With the same logic:
$.extend({}, {foo:'bar', test:123})
will return:
{foo:'bar', test:123}
i.e. effectively a clone.
In a non jQuery way.
var newObj = {};
Object.keys(settings).forEach(function(key) {
newObj[ key ] = settings[ key ];
});
This copies only the top-level properties. To copy hashes with nested objects as property values, you will need to use a recursive function.
NB: The Object.keys(settings) avoids the need for calling settings.hasOwnProperty(key).
var clone = $.extend(true, {}, settings);
Set first argument to true.
EDIT: First argument true signifies deep copy. For given example in original question there is no need for deep copy since there are simple immutable key-value pairs. For question in title - as a general case - use deep copy. Otherwise you get half-copy.
It sounds like you want jQuery extend, which can copy an object for you.
http://api.jquery.com/jQuery.extend/
My 2 cents:
function clone(hash) {
var json = JSON.stringify(hash);
var object = JSON.parse(json);
return object;
}
It may not be the most optimized option but it can be handy for some scenarios.
Underscore.js also has an extend function if you are not using jQuery:
extend _.extend(destination, *sources) Copy all of the properties in the source objects over to the destination object, and
return the destination object. It's in-order, so the last source will
override properties of the same name in previous arguments.
_.extend({name: 'moe'}, {age: 50});
=> {name: 'moe', age: 50}
Related
So I have been looking at Object.freeze() and Object.seal().
Object.freeze() - will make all existing properties non-writable, and will not allow any new properties to be added.
Object.seal() - "Sealing an object prevents new properties from being added and marks all existing properties as non-configurable."
I am looking for a way to make all existing properties "frozen" (non-writable), but allow new properties to be added.
Is there shorthand for doing that?
The manually way of doing what I want is:
let freezeExistingProps = obj => {
Object.keys(obj).forEach(k => {
Object.defineProperty(obj, k, {
writable: false
});
});
};
The above function works surprisingly well to freeze existing top-level properties on an object (it doesn't overwrite them, just changes them to non-writable), but I am hoping there might be a more official/quicker way to do the above.
You might do the following:
instance -> frozen static proto -> dynamic proto
Some sample:
function freeze(stat,dyn){
Object.setPrototypeOf(stat,dyn);
Object.freeze(stat);
}
var a={unchangeable:1};
var b={changeable:2}
freeze(a,b);
Now have a look at a and change some b props.
Well, if you want to do it in the manner of freeze, then freezing it immediately, and setting up to a prototype of another object might help, but it will return a copy (pointing to the original object as prototype), exactly in the form how you want. there are obviously some pros and cons, as the properties will not be the immediate properties, but we can find it out by its __proto__ if we need all the keys (assuming you have a dedicated use case)
So, just another try
function freezeExistingProps (obj){
var OBJECT = function(){};
Object.freeze(obj)
OBJECT.prototype = obj;
return new OBJECT();
}
You may want to consider cloning your object into a new one with extra attribute. It's also a very good practice (look for immutability).
An example:
const setAge = (person, age) => ({ ...person, age });
const person = {
firstName: 'Luke',
lastName: 'Skywalker',
};
const personWithAge = setAge(person, 24);
I'm trying to build a contructor which accepts an object and sets it to itself.
For instance:
var Somecontructor = function(object){
this = object;
}
Obviously, I cannot assign anything to "this" because it is immutable, but I'm trying to figure out a way to do this. Any ideas?
Edit: I don't know why this question deserves negative vote. All I want to do is, construct an object that creates properities using whatever that was passed as arguments. Sure, I can do this and I wanted to find the most efficient way to do it. If your answer doesn't work, please don't be a terrible human being by negative voting the question.
This will copy all of the properties of one object to the properties of a new object.
var SomeConstructor = function(obj) {
for(key in obj) {
this[key] = obj[key];
}
}
You seem to be wanting to clone an object. For that, you need not use a constructor function.
var cloneObj = function(obj){
return JSON.parse(JSON.stringify(obj));
}
console.log(cloneObj({
property1: "One",
property2: "Two",
}));
I have problem with javascript object(array) deep copy. I read many good way to deal with it. And I also know that jQuery has $.extend API to this problem. But my question is: Can I just using JSON stringify and parse method to solve this problem?
Here's my code:
function deepCopy(oldValue) {
var newValue
strValue = JSON.stringify(oldValue)
return newValue = JSON.parse(strValue)
}
var a = {
b: 'b',
c: [1,2,4],
d: null
}
copy = deepCopy(a)
console.log(a === copy) // false
console.log(a.c === copy.c) // false
PS: I've known that if no all objects are serializable, but the only situation I know is that when the object contains a property which is function. Any other situation?
If your object is "small" and contains exclusively serializable properties, a simple deepCopy hack using JSON serialization should be OK. But, if your object is large, you could run into problems. And if it contains unserializable properties, those'll go missing:
var o = {
a: 1,
b: 2,
sum: function() { return a + b; }
};
var o2 = JSON.parse(JSON.stringify(o));
console.log(o2);
Yields:
Object {a: 1, b: 2}
Interestingly enough, a fair number of deep-copy solutions in C# are similar serialization/deserialization tricks.
Addendum: Not sure what you're hoping for in terms of comparing the objects after the copy. But, for complex objects, you generally need to write your own Compare() and/or Equals() method for an accurate comparison.
Also notable, this sort of copy doesn't preserve type information.
JSON.parse(JSON.stringify(new A())) instanceof A === false
You can do it that way, but it's problematic for some of the reasons listed above:
I question the performance.
Do you have any non-serializable properties?
And the biggest: your clone is missing type information. Depending upon what you're doing, that could be significant. Did the implementor add methods to the prototype of your original objects? Those are gone. I'm not sure what else you'll lose.
I think what you looking for is something like this:
If you have a really nested object structure then to make a deep copy you can make use of JSON.stringify().
Please see below example:
var obj= {
'a':1,
'b':2,
'c': {
'd':{
'e' : 3
}
}
}
var newObj = {...obj};
var lastObj = JSON.parse(JSON.stringify(obj));
obj.c.d.e =19;
console.log('obj '+obj.c.d.e);
console.log('newObj '+obj.c.d.e);
console.log('lastObj'+lastObj.c.d.e);
Now lastObj is truly detached from obj while if you use ...(spread) operator than also it does not work in really complex objects.
Hope this helps!!
I am working in jquery with backbone.js and running into the case where i need to duplicate models, but i need to do a deep copy on them so no references exist between the copies. Models can have other models as attributes. Models can have anon functions as attributes.
So i'm working on creating an algorithm that will deep clone most backbone models. I’m expecting that all bindings should be removed (for the new instance) during this copy so I’m not worried about trying to keep them.
Goals:
Able to duplicate all simple variable (String, Int, float, etc) and store it into the new model, as the same name.
Done, using toJSON to create a new JSON object which can be passed to set(). This object only contains simple attributes, i.e. does not include attributes assigned to functions or other models.
Able to duplicate the anon functions some variables will be assigned to, without knowing function/attribute names beforehand.
If I know the name of the attribute which is assigned to a function I can copy it. But if the model is new or unknown I don’t have that information.
If an attribute is another backbone model, call the deep copy algorithm recursively on that attribute.
Not able to check if an attribute is a backbone model with native backbone methods, looking for a work around.
The simplified version of what I currently have is below:
/**
* Performs a deep copy of a backbone.js model
* All bindings for the copy are lost
* #param orgModel - the original model to copy
*/
function deepCopyModel(orgModel)
{
var dupModel = Backbone.Model.extend({});
var orgAttributes= orgModel.toJSON();
var keepAttr=_.keys(orgAttributes);
//remove any special cases
keepAttr=_.without( keepAttr , 'specialCase1', 'specialCase2' );
//or keepAttr=_.difference(keepAttr, ['specialCase1', 'specialCase2'] );
//remove undefined values
keepAttr=_.filter(keepAttr,function(key) {
return ( typeof(attributes[key])!="undefined" );
});
//grab the resulting list of attributes after filtering
var result=_.pick(attributes,keepAttr);
//assign attributes to the copy using set
dupModel.set(result);
//TODO: Implement deep copy of functions
//TODO: Implement deep copy of inner models
return dupModel;
}
Any help or insight you can give would be greatly appreciated. Thanks!
jQuery's extend method allows you to simply copy object properties from one to another.
Here's a contrived, but illustrative example. It even shows why you would not need to "deep" copy functions!
var someObj = {
a : "a",
b : 12345,
c : {
d : "d",
e : "e"
},
f : function() {
alert(this.a);
}
};
//copy from original to new empty object
var deepCopy = $.extend(true, {}, someObj);
deepCopy.a = "deepCopy.a";
deepCopy.c.d = "deepCopy.c.d";
alert("someObj is not affected when deep copying: " + someObj.c.d);
alert("deepCopy is entirely distinct when deep copying: " + deepCopy.c.d);
deepCopy.f();
someObj.f();
Here's a fiddle for your convenience: http://jsfiddle.net/S6p3F/3/
Running this code you will see that someObj and deepCopy are identical in structure but distinct objects.
As you can see, deep copying of functions is not required as the this reference is bound to whatever object the function is applied to. This is becausein javascript, calling a function as deepCopy.f() is functionally equivalent to deepCopy.f.call(deepCopy). A more illustrative example:
function someFunction() {
alert(this.someProperty);
}
var a = {
someProperty: "a's property"
},
b = {
someProperty: "b's property"
};
someFunction.call(a);
someFunction.call(b);
And a fiddle: http://jsfiddle.net/S6p3F/2/
If you are using Lo-Dash as Underscore drop-in replacement, you can also use _.cloneDeep
var newModel = new MyModel(_.cloneDeep(oldModel.toJSON());
In my script there is a need to create a hash table, and I searched in google for this. Most of the folks are recommending JavaScript object for this purpose. The The problem is some of the keys in the hash table have a "." in them. I am able to create these keys easily with the associative arrays.
I don't understand why associative arrays are bad. The first thing that is mentioned on the sites that I looked at is the length property.
I am coming from the Perl background, where I used hashes. Most common uses were to get the value from a key, check if a key exists, delete a key-value pair, and add a key-value pair. If these are my common uses, can I safely use an associative array?
In JavaScript, objects are associative arrays...there aren't separate concepts for them. You are also able to safely use '.' in a key name, but you can only access the value using the bracket notation:
var foo = {}
foo['bar'] = 'test';
foo['baz.bin'] = 'value';
alert(foo.bar); // Shows 'test'
alert(foo['baz.bin']); // Shows 'value'
If you're using them already and they work, you're safe.
In a JavaScript, an object and array are pretty much the same thing, with an array having a bit of magical functionality (autoupdating the length property and such) and prototype methods suitable for arrays. It is also much easier to construct an object than using an associative array:
var obj = {"my.key": "myValue"};
vs.
var obj = [];
obj["my.key"] = "myValue";
Therefore never use the array object for this, but just the regular object.
Some functionality:
var obj = {}; // Initialized empty object
Delete a key-value pair:
delete obj[key];
Check if a key exists:
key in obj;
Get the key value:
obj[key];
Add a key-value pair:
obj[key] = value;
Because there is no such thing as built-in associative arrays in JavaScript. That's why it's bad.
In fact, when you use something like:
theArray["a"] = "Hello, World!";
It simply creates a property called "a" and set its value to "Hello, World!". This is why the length is always 0, and why the output of alert(theArray) is empty.
Actually, an "associative array" is pretty much the same as an "array-like object" in ECMAScript. Even arrays are objects in ECMAScript, just with the exception to have numeric keys (which are still strings in the background) and a .length property, along with some inherited methods from Array.prototype.
So, a Perl hash and an ECMAScript object behave similarly. You might not know that you can access object properties not only via a dot, but also with brackets and strings, like
var myObj = { foo: 42 };
myObj.foo; // 42
myObj['foo']; // 42
Knowing that, you can also use keys with .
var myObj = { };
myObj['hello.foo.world'] = 42;
Of course, you can access that key only with the bracket notation.
You can use . in key names on JavaScript objects (AKA associative arrays) if you'd like; they're accepted without issue. The minor drawback is you can't use shortcut notations with the dotted keys, e.g.
var x = {};
x['hello'] = 'there';
alert(x.hello);
is perfectly acceptable and will pop up an alert with 'there' in it. But if you use a dotted name:
var x = {};
x['this.is'] = 'sparta';
alert(x.this.is);
will fail, as JavaScript will look for an attribute named this in the x object, which does not exist. There is only the this.is attribute.
There isn't an associative array. It's just an object.
foo.bar; // Equivalent to...
foo["bar"]; // Looks like associative array.
For the sake of convenience of using data, there should be no difference between an object and an array. You can think of it as an object or you can think of it as an associative array. At the end, you can just think of everything as data.
For PHP, [ ] accepts 0, 1, or more items(array), and it is called an associative array. It is JSON in PHP's coat:
$data = ["message"=>[ "id"=>405, "description"=>"Method not allowed.", "detail"=>[]], "object" => []];
For JavaScript, { } accepts 0, 1, or more items(array), and it is called an object. This data format is JSON:
data = {"message": { "id":405, "description":"Method not allowed.", "detail" : {}}, "object" : {}};
I just call them data. The simplest way to describe data is JSON, or its variants.