altayseyhan
altayseyhan

Reputation: 755

Javascript selective inheritance while object creation

Lets say I have an object relations like that;

var firstObject = {     prop1:"prop1",  prop2:"prop2" };

var secondObject = Object.create(firstObject);

secondObject.prop3 = "prop3";

secondObject.prop4 = "prop4";

var thirdObject =  Object.create(secondObject);

At the above code when I have created thirdObject it will also inherit firstObject properties which is expected. What I wonder is , are there any elegant (I mean without iterating over secondObject properties with hasOwnProperty property) ways to inherit just secondObject properties while creating thirdObject?

Upvotes: 2

Views: 219

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074138

are there any elegant (I mean without iterating over secondObject properties with hasOwnProperty property) ways to inherit just secondObject properties while creating thirdObject?

No, and in fact, there's no (reasonable*) inelegant way to do it either; making thirdObject inherit from secondObject means it will inherit all of secondObject's properties and the ones from its prototype(s).

If you want thirdObject not to have firstObject's properties, thirdObject shouldn't inherit (indirectly) from firstObject.

Three options for you (and an "unreasonable" fourth later):

Copy the properties

You could use Object.assign to copy secondObject's own properties to thirdObject:

var thirdObject = Object.assign({}, secondObject);

...but it wouldn't be inheritance, just a snapshot. But that might be for the best. (Object.assign was added in ES2015 [aka "ES6"], but can be polyfilled for older JavaScript engines.)

Mimic inheritance with getters/setters

Alternately, and this does require iterating secondObject's own properties, you could give thirdObject matching properties with getters and setters:

var firstObject = { prop1:"prop1", prop2:"prop2" };

var secondObject = Object.create(firstObject);

secondObject.prop3 = "prop3";

secondObject.prop4 = "prop4";

var thirdObject = {};
Object.keys(secondObject).forEach(function(key) {
  Object.defineProperty(thirdObject, key, {
    get: function() {
      return secondObject[key];
    },
    set: function(value) {
      delete this[key];  // Release the getter/setter for this
      this[key] = value; // ...and make it an own property
    },
    configurable: true,
    enumerable: true
  });
});

console.log("prop1" in firstObject);  // true
console.log("prop1" in secondObject); // true
console.log("prop1" in thirdObject);  // false
console.log("--");
console.log("prop3" in firstObject);  // false
console.log("prop3" in secondObject); // true
console.log("prop3" in thirdObject);  // true
console.log("--");
console.log(firstObject.prop1);  // prop1
console.log(secondObject.prop1); // prop1
console.log(thirdObject.prop1);  // undefined
console.log("--");
console.log(firstObject.prop3);  // undefined
console.log(secondObject.prop3); // prop3
console.log(thirdObject.prop3);  // prop3
.as-console-wrapper {
  max-height: 100% !important;
}

There, we mimic inheritance by having thirdObject get the value from secondObject until/unless something assigns a value to it, in which case we make it a standard data property.

Use inheritance with an intermediary with getters

Or you could make thirdObject inherit from an intermediary that defers to secondObject's own properties instead (a bit simpler, and then the "own" flags are right):

var firstObject = { prop1:"prop1", prop2:"prop2" };

var secondObject = Object.create(firstObject);

secondObject.prop3 = "prop3";

secondObject.prop4 = "prop4";

var thirdProto = {};
Object.keys(secondObject).forEach(function(key) {
  Object.defineProperty(thirdProto, key, {
    get: function() {
      return secondObject[key];
    },
    configurable: true,
    enumerable: true
  });
});
var thirdObject = Object.create(thirdProto);

console.log("prop1" in firstObject);  // true
console.log("prop1" in secondObject); // true
console.log("prop1" in thirdObject);  // false
console.log("--");
console.log("prop3" in firstObject);  // false
console.log("prop3" in secondObject); // true
console.log("prop3" in thirdObject);  // true
console.log("--");
console.log(firstObject.prop1);  // prop1
console.log(secondObject.prop1); // prop1
console.log(thirdObject.prop1);  // undefined
console.log("--");
console.log(firstObject.prop3);  // undefined
console.log(secondObject.prop3); // prop3
console.log(thirdObject.prop3);  // prop3
.as-console-wrapper {
  max-height: 100% !important;
}


* The unreasonable way would be to use a Proxy (ES2015+, not polyfillable):

var firstObject = { prop1:"prop1", prop2:"prop2" };

var secondObject = Object.create(firstObject);

secondObject.prop3 = "prop3";

secondObject.prop4 = "prop4";

var thirdObject = Object.create(new Proxy(secondObject, {
    has: function(target, prop) {
        return target.hasOwnProperty(prop) || secondObject.hasOwnProperty(prop);
    },
    get: function(target, prop) {
        return target.hasOwnProperty(prop) || secondObject.hasOwnProperty(prop) ? target[prop] : undefined;
    },
    set: function(target, prop, value) {
        target[prop] = value;
        return true;
    }
}));

console.log("prop1" in firstObject);  // true
console.log("prop1" in secondObject); // true
console.log("prop1" in thirdObject);  // false
console.log("--");
console.log("prop3" in firstObject);  // false
console.log("prop3" in secondObject); // true
console.log("prop3" in thirdObject);  // true
console.log("--");
console.log(firstObject.prop1);  // prop1
console.log(secondObject.prop1); // prop1
console.log(thirdObject.prop1);  // undefined
console.log("--");
console.log(firstObject.prop3);  // undefined
console.log(secondObject.prop3); // prop3
console.log(thirdObject.prop3);  // prop3
.as-console-wrapper {
  max-height: 100% !important;
}
Note: Requires a browser with <code>Proxy</code> support.

...but I can't imagine you want a proxy object in your inheritance chain, not least because of the effect it would have on performance. :-) So I wouldn't do that. (And I'm sure the example above is incomplete.)

Upvotes: 3

Related Questions