0

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?

Costa Michailidis
  • 7,691
  • 15
  • 72
  • 124
  • 1
    You could always `var object2 = Object.assign(Object.create(object1), {c: 3, d: 4}));`? – loganfsmyth Dec 07 '16 at 23:00
  • 1
    Actually, this is one of the acceptable use cases for `Object.setPrototypeOf`, and rather easily optimisable. If it is too slow for you, you should file an issue with your browser vendor. – Bergi Dec 07 '16 at 23:32

4 Answers4

3

You can combine Object.create with Object.assign for this:

var object2 = Object.assign(Object.create(object1), {
    c: 3,
    d: 4
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
3

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
Oriol
  • 274,082
  • 63
  • 437
  • 513
1

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() );
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
1

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)
Sintrias
  • 456
  • 1
  • 9
  • 19