2

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?

1
  • 2
    In ES2015 you can use Object.assign(thirdObject, secondObject) which will just copy own properties instead of inheriting them prototypically. Commented Mar 10, 2017 at 11:02

1 Answer 1

3

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.