Reset object to empty object keeping references intact in non-linear time - javascript

Let's say I have obj1 with only enumerable properties:
var obj1 = { a: 1, b: 2, c: 3 };
I then create obj2 which holds a reference to obj1:
var obj2 = obj1;
obj1 === obj2; // true
At some point, I want to reset obj1 to an empty object:
for (key in obj1) {
delete obj1[key];
}
obj1 === obj2; // true
But I'd like to avoid having to iterater over all the properties:
obj1 = {};
obj1 === obj2; // false (this obviously doesn't work)
Is there another solution?

If you have flexibility on the data model, then store the actual properties in a child object:
var obj1 = {theData: { a: 1, b: 2, c: 3} };
then you can reset the properties with obj1.theData = {}.
Of course, that implies that access to any property will incur an additional "hop", so depending on how often you access the data (read or write it) vs. reset the object, you might be better off keeping the delete loop.
Other than that, I don't believe you can reset an object like you can an Array (via a.length=0).

Related

Extending object's properties without overwriting them

I'm trying to extend the keys/values in target object (with the keys/values) from source object, but without overwriting existing keys/values in the target object. Meaning:
var obj1 = {
a: 1,
b: 2
};
var obj2 = {
b: 4,
c: 3
};
extend(obj1, obj2);
console.log(obj1); // --> {a: 1, b: 2, c: 3}
Interestingly, I found Object.assign(obj1,obj2);, but it overwrites the keys/values.
I need to not overwrite them, if existent and add them if nonexistent.
Please help in plain JavaScript.
Just a simple loop. If you only want enumerable own properties, then:
Object.keys(obj2).forEach(function(key) {
if (!(key in obj1)) {
obj1[key] = obj2[key];
}
});
If you want all enumerable properties:
var key;
for (key in obj2) {
if (!(key in obj1)) {
obj1[key] = obj2[key];
}
}
The key (no pun) bit there is the in operator, which tells you whether an object has a property (of its own, or via inheritance).
There's also Object.prototype.hasOwnProperty which would tell you only if the object has its own (not inherited) property with a given name:
Object.keys(obj2).forEach(function(key) {
if (!obj1.hasOwnProperty(key)) {
obj1[key] = obj2[key];
}
});
More:
in operator
hasOwnProperty
There is no such built in functions available in JS to do that for you.
You have to write your own logic to do that,
var x = {a:10};
var y = {a:5, b: 20};
merge(x,y);
function merge(objSrc, objTarget){
return Object.keys(objTarget).reduce(function(src, prop){
if(!src.hasOwnProperty(prop)) src[prop] = objTarget[prop];
return src;
}, objSrc);
}
console.log(x); {a:10, b:20}
P.S The above code would do a merge over enumerable own properties since Object.keys() would return the same.

Shorthand for Object.create() with multiple properties

If I want to create an object in JavaScript that has a prototype link to another object, but has several of it's own properties how can I do this?
var object1 = {
a: 1,
b: 2
};
var object2 = Object.create( object1 );
object2.c = 3;
object2.d = 4;
console.log( object2 ); // my new object with object1 as it's prototype link
My challenge here is that I have to set object2's properties one at a time.
My other option is:
var object1 = {
a: 1,
b: 2
};
var object2 = {
c: 3,
d: 4
};
Object.setPrototypeOf( object2, object1 );
console.log( object2 );
My challenge above is that the performance is supposed to be terrible. Namely, setPrototypeOf is slow. https://jsperf.com/object-create-vs-object-setprototypeof
And then of course, there's the "shorthand" where you provide, writeable, enumerable and all that to Object.create(), but that's not really shorthand.
Any ideas?
You can combine Object.create with Object.assign for this:
var object2 = Object.assign(Object.create(object1), {
c: 3,
d: 4
});
As an alternative to Object.assign, remember Object.create accepts a second argument with the property descriptors you want to add to the object:
var object1 = {
a: 1,
b: 2
};
var object2 = Object.create(object1, {
c: {value: 3, enumerable: true},
d: {value: 4, enumerable: true}
});
console.log( object2 ); // my new object with object1 as it's prototype link
Note the default is non-configurable, non-writable and non-enumerable.
If that's a problem, ES2017 introduces Object.getOwnPropertyDescriptors.
var object1 = {
a: 1,
b: 2
};
var object2 = Object.create(object1, Object.getOwnPropertyDescriptors({
c: 3,
d: 4
}));
console.log( object2 ); // my new object with object1 as it's prototype link
A more elegant way of doing this would be by using spread syntax.
const obj1 = { a: 1, b: 2 }
const obj2 = { ...obj1, c: 3, d: 4 }
console.table(obj1)
console.table(obj2)
You can even add more properties to the same object in a similar way.
let obj = { a: 1, b: 2 }
obj = { ...obj, c: 3, d: 4 }
console.table(obj)
This also works for arrays.
let arr = [1, 2]
arr = [...arr, 3] // Equivalent to Array.push()
arr = [0, ...arr] // Equivalent to Array.unshift()
arr = [-1, ...arr, 4] // Equivalent to simultaneous Array.unshift() and Array.push()
console.table(arr)
Normally, when we talk about setting and swapping prototypes, we are talking about constructor functions that are instantiated into objects and not object literals themselves.
You can certainly, just manually switch the prototype yourself in this case (which is the basis for prototypical inheritance) and will cause you to inherit the right properties, but you also now have to deal with constructor issues when instances of your derived object get made.
But, this technique is fast as it only requires a new instance to be made and that reference is then set in the prototype property.
function object1(){
this.a = 1;
this.b = 2;
console.log("object1 has been invoked");
};
function object2(){
console.log("object2 has been invoked");
this.c = 3;
this.d = 4;
};
// It's very important that the prototype be set to a NEW instance
// of the super-object so that you don't wind up sharing a prototype
// with other unintended objects.
object2.prototype = new object1();
// object2.prototype.constructor was the function object2
// But now that object2 has had its prototype swapped out
// to object1, when new instances of object2 are made, the
// constructor for object1 will execute. To fix this, we
// just need to reset the constructor property of the new
// prototype that we just set. That's another reason we created
// a new instance of object1, so we could modify the constructor
// of that instance without screwing up other instances of object1
// that may be in use. object2 will use object1 as
// it's prototype, but that particular instance of object1
// will have object2 set as the constructor to use when instances
// are needed.
object2.prototype.constructor = object2;
console.log( new object2() );

How do you create an object equal to another object in JavaScript unbound

I want to create an object from another object but not have the first object change when I change the second object.
I hav tried:
var ojb2 = new Object(obj1); // obj1 is still bound to obj2
Var obj2 = Object.create(obj1); // does not create object at all
Object.create actually creates the object, but then puts the first object in as a prototype for the new object. This way you'll be able to use the properties and functions defined in the first object, but you can also define properties with the same names, without overwriting the first object:
var obj1 = {a: 20};
var obj2 = Object.create(obj1);
obj2.a = 10;
// obj2 = {
// a: 10,
// __proto__: {
// a: 20
// }
//}
I'm not 100% sure that understand your question right, but you want to create an Object from another Object, right? And when you change the second one it will not change the original one? If I understand you right you can use the new Object().
Here is an example what's happened if you use it:
var obj1 = {
asd : "asd",
asd2 : "asd2"
};
var obj2 = new Object(obj1);
console.log(obj1); //{ asd: 'asd', asd2: 'asd2' }
console.log(obj2); //{ asd: 'asd', asd2: 'asd2' }
obj2 = {
aasd : "aasd",
aasd2 : "aasd2"
};
console.log(obj1); //{ asd: 'asd', asd2: 'asd2' }
console.log(obj2); //{ aasd: 'aasd', aasd2: 'aasd2' }

indexOf but for objects? [duplicate]

This question already has answers here:
What's the correct way to test for existence of a property on a JavaScript Object?
(3 answers)
Closed 6 years ago.
I want to look through an object and assign each of it's existent properties to a variable.
There are 4 possible properties. Some of the objects have all 4. Some might only have two.
How can I check if a particular property exists? Is there an equivalent of indexOf() for arrays but for objects instead?
Use the in keyword:
"key" in object
which returns true or false, depending if the object, or anything in its prototype chain, has that property.
You can also use object.hasOwnProperty("key"), which will only be true if the object has key as a property of itself, not its prototype. Example:
var object = {};
"toString" in object; // true
object.hasOwnProperty("toString"); // false
Note (as per #dandavis's comment) that if object has a custom property called hasOwnProperty, this gets thwarted; to work around this, use hasOwnProperty.call(object, "key"). Example:
var a = {hasOwnProperty: Boolean};
a.hasOwnProperty('name'); // true
hasOwnProperty.call(a, 'name'); // false
If you are only interested in properties set directly on the object (not accessible via the prototype chain) then hasOwnProperty will provide a boolean value, true, if an object has the specified property.
For example: testObject.hasOwnProperty('propertyToCheckFor') would return true if testObject.propertyToCheckFor exists, otherwise it would be false.
See the following code for a more expanded example:
var obj1 = {
a: 1
};
var obj2 = {
a: 1,
b: 2
};
var obj3 = {
b: 2,
c: 3
};
var obj4 = {
a: 1,
b: 2,
c: 3
};
// For dispaly purposes
document.write('<pre>' + JSON.stringify({
obj1: {
hasA: obj1.hasOwnProperty('a'),
hasB: obj1.hasOwnProperty('b'),
hasC: obj1.hasOwnProperty('c')
},
obj2: {
hasA: obj2.hasOwnProperty('a'),
hasB: obj2.hasOwnProperty('b'),
hasC: obj2.hasOwnProperty('c')
},
obj3: {
hasA: obj3.hasOwnProperty('a'),
hasB: obj3.hasOwnProperty('b'),
hasC: obj3.hasOwnProperty('c')
},
obj4: {
hasA: obj4.hasOwnProperty('a'),
hasB: obj4.hasOwnProperty('b'),
hasC: obj4.hasOwnProperty('c')
}
}, null, 2) + '</pre>');
var obj = {
foo: 1,
bar: 2,
baz: 3
}
Object.keys(obj).forEach(function(key) {
window[key] = obj[key]
})
console.log(foo, bar, baz)
Or in ES2015
const obj = {
foo: 1,
bar: 2,
baz: 3
}
function assignPrivateVars() {
let {foo, bar, baz} = obj;
console.log(foo, bar, baz);
}
assignPrivateVars();
You can use destructuring assignment. If value is not defined, variable will be set to undefined. You can also check if variable is defined after destructuring then delete variable by reference.
var data = {a:1, b:2, c:3};
var {a, b, c, d} = data; // `d`: `undefined`

Copied object generated by Object.Assign() function has side effect?

I have an object named result which is composed of two objects like :
const a = {bar: {baz: 2}};
var b = {foo: 1};
var result = Object.assign({}, a, b);
console.log(result, a, b);
// result -> {bar: {baz: 2}, foo: 1}
// a -> {bar: {baz: 2}}
// b -> {foo: 1}
Now, I am changing the bar property of the result object like:
result.bar.baz = 3;
result.foo = 4;
console.log(result, a, b);
// result -> {bar: {baz: 3}, foo: 4}
// a -> {bar: {baz: 3}} intresting part
// b -> {foo: 1} intresting, too!
(You can copy and paste code to javascript console in order to see the result for both cases by the way)
There are two weird things here. First one is that I am changing the resulting object's property, but constant object a's property changes, too. Even if first one is the case with Object.assign function, how can I change the constant variable? Let's say this is the case despite const variable mutation, then why the change in property foo does not reflect to the object b?
I came with that because I generally use Object.assign to copy objects, but this is pretty weird issue with that function I guess. Any ideas about the case? Thank you.
Declaring a variable with const only prevents it from being changed to another value. It doesn't prevent the data referenced by that value to change.
const foo = {prop: 'bar'};
foo.prop = 'baz'; // Works well
foo = 'baz'; // TypeError: invalid assignment to const `foo'
If you want to prevent an object from being changed, you can freeze it.
Object.freeze(foo);
foo.prop = 'buz'; // TypeError: "prop" is read-only
However, that will only affect own properties. If the value of one of these is another object, it won't become frozen.
This is what happens with Object.assign too. It only copies own properties, and if their value is an object, it won't "clone" it. That is, the references will still be the same, and changes will be reflected.
If you want to deeply clone an object, see What is the most efficient way to clone an object?
Object.assign will work but with the added gotcha that if any of the properties you are assigning from contain an object as a value it does not create a copy of that object, so the references do not change, the property in the created object will point to that same nested object.
Also constant in javascript can be deceiving, you can add and remove properties from a 'const' object as long as you don't try to reassign it to a new object or a different primitive.
Same will occur with arrays, you can create a 'const' array but push pop off of it.
https://jsfiddle.net/eu9yg37s/6/ just something I was messing around in to attempt to display what I mean.
const a = {bar: {baz: 2}};
var b = {foo: 1};
// Example of customizer function that may take into account nested objects and properly copy them using lodash 4.x
var customizer = function(objValue, srcValue) {
if (typeof srcValue === 'object' && typeof srcValue !== null) {
return _.assignWith({}, srcValue, customizer);
}
return _.isUndefined(objValue) ? srcValue : objValue;
}
// This calls assign, but will invoke the customizer function
var result = _.assignWith({}, a, b, customizer);
result.bar.baz = 3;
result.foo = 4;
console.log(result, a, b);
// 'Constant' Array Example
const hi = [true, false, 'hi'];
hi[2] = 23;
console.log('hi.pop', hi.pop());
console.log('hi', hi);
// These will error, uncomment out to see it errors on any of these attempts
//hi = 3;
//hi = 'no';
hi = [true, false, 23];
//hi = false;
//hi = {};
The change doesn't reflect in b, because it wasn't a nested object during the assign operation so the property foo in our created object is pointing to a new primitive 1

Categories

Resources