Shorthand for Object.create() with multiple properties - javascript

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

Related

In ES6, how do I add methods to a class or object that isn't created or maintained by me?

Say, if I want to add a method "myAssign" to Object class, how should I do this in ES?
Do I have to add it to the Object or Object's prototype, which does not seem to be a good way?
let obj1 = {a: 1}
let obj2 = {b: 2}
// It's OK but not good
Object.prototype.myAssign = function myAssign() {
return {}
}
// What I expect
Object.myAssign(obj1, obj2) // returns {}
Background:
I'm using node express, and try to get values from req.query and use them to create two objects. "req.query" might has property a or b, or like {}.
let aObj = new A(req.query);
let bObj = new B(req.query);
hope to get
aObj = {a: 1} or {},
bObj = {b: 2} or {}, not
aObj = {a: undefined}.
So I want to create my own assign method.

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`

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

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

jquery extend vs angular extend

What is the difference between these two extend functions?
angular.extend(a,b);
$.extend(a,b);
While the jquery.extend is well documented the angular.extend lacks details and the comments there provide no answers. (https://docs.angularjs.org/api/ng/function/angular.extend).
Does angular.extend also provide deep copy?
angular.extend and jQuery.extend are very similar. They both do a shallow property copy from one or more source objects to a destination object. So for instance:
var src = {foo: "bar", baz: {}};
var dst = {};
whatever.extend(dst, src);
console.log(dst.foo); // "bar"
console.log(dst.baz === src.baz); // "true", it's a shallow copy, both
// point to same object
angular.copy provides a deep copy:
var src = {foo: "bar", baz: {}};
var dst = angular.copy(src);
console.log(dst.baz === src.baz); // "false", it's a deep copy, they point
// to different objects.
Getting back to extend: I only see one significant difference, which is that jQuery's extend allows you to specify just one object, in which case jQuery itself is the target.
Things in common:
It's a shallow copy. So if src has a property p that refers to an object, dst will get a property p that refers to the same object (not a copy of the object).
They both return the destination object.
They both support multiple source objects.
They both do the multiple source objects in order, and so the last source object will "win" in case more than one source object has the same property name.
Test page: Live Copy | Live Source
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
<meta charset=utf-8 />
<title>Extend!</title>
</head>
<body>
<script>
(function() {
"use strict";
var src1, src2, dst, rv;
src1 = {
a: "I'm a in src1",
b: {name: "I'm the name property in b"},
c: "I'm c in src1"
};
src2 = {
c: "I'm c in src2"
};
// Shallow copy test
dst = {};
angular.extend(dst, src1);
display("angular shallow copy? " + (dst.b === src1.b));
dst = {};
jQuery.extend(dst, src1);
display("jQuery shallow copy? " + (dst.b === src1.b));
$("<hr>").appendTo(document.body);
// Return value test
dst = {};
rv = angular.extend(dst, src1);
display("angular returns dst? " + (rv === dst));
dst = {};
rv = jQuery.extend(dst, src1);
display("jQuery returns dst? " + (rv === dst));
$("<hr>").appendTo(document.body);
// Multiple source test
dst = {};
rv = angular.extend(dst, src1, src2);
display("angular does multiple in order? " +
(dst.c === src2.c));
dst = {};
rv = jQuery.extend(dst, src1, src2);
display("jQuery does multiple in order? " +
(dst.c === src2.c));
function display(msg) {
$("<p>").html(String(msg)).appendTo(document.body);
}
})();
</script>
</body>
</html>
There is one subtle difference between the two which was not mentioned in previous answers.
jQuery's .extend() allows you to conditionally add key,value pairs, only if the value is defined. So in jQuery, this: $.extend({}, {'a': x ? x : undefined}); will return {} in case x is undefined.
In Angular's .extend() however, this: angular.extend({}, {'a': x ? x : undefined}); will return {'a': undefined}, even if x is undefined. So the key will be there, no matter what.
This could be a good or a bad thing, depending on what you need. Anyway this is a difference in behavior between the two libraries.
The 1.0.7 angularjs build states that the extend & copy methods no longer copy over the angularjs internal $$hashKey values.
See release notes # https://github.com/angular/angular.js/blob/master/CHANGELOG.md
angular.copy/angular.extend: do not copy $$hashKey in copy/extend functions. (6d0b325f, #1875)
A quick test of the angular.copy in Chomre dev tools method shows that it does do a deep copy.
x = {p: 3, y: {x: 5}}
Object {p: 3, y: Object}
x
Object {p: 3, y: Object}
z = angular.copy(x);
Object {p: 3, y: Object}
z
Object {p: 3, y: Object}
x
Object {p: 3, y: Object}
z.y.x = 1000
1000
x
Object {p: 3, y: Object}
p: 3
y: Object
x: 5
__proto__: Object
__proto__: Object
z
Object {p: 3, y: Object}
p: 3
y: Object
x: 1000
__proto__: Object
__proto__: Object
angular.extend on the other hand does a shallow copy.
.extend() in AngularJS works similarly to jQuery's .extend()
http://jsfiddle.net/Troop4Christ/sR3Nj/
var o1 = {
a: 1,
b: 2,
c: {
d:3,
e:4
}
},
o2 = {
b: {
f:{
g:5
}
}
};
console.log(angular.extend({}, o1, o2));
console.log(o1);
console.log(o2);

Categories

Resources