0

I have an object like

var Profile = Object.create(null);
Object.defineProperties(Profile, {
    id: {
        value: "",
        enumerable: true
    },
    name: {
        value: "",
        enumerable: true
    },
    active: {
        value: true,
        enumerable: true
    }
});

Now I want to create an Profile instance and give it id and name, and keep active default true, so I do this:

var p1 = Object.create(Profile, {
    id: {
        value: "123",
        enumerable: true
    },
    name: {
        value: "hello world",
        enumerable: true
    }
});

Then I got an object named p1, but I cannot find "active" in

Object.getOwnPropertyNames(p1);

Also I cannot use JSON.stringify(p1) to serialize property "active", but I need property "active" can be serializable.

Is this the wrong way I used Object.create? I just want to create a serializable "class" and get it's serializable "instance". How can I do this?

Sunny
  • 127
  • 7
  • your properties are non-configurable and non-writeable... – Elias Van Ootegem Sep 20 '13 at 12:20
  • Why not just `{ id: "123", name: "hello world" }` ? JavaScript is naturally dirty anyway... – Virus721 Sep 20 '13 at 12:22
  • @EliasVanOotegem But `p1.id` still become 123 and `p1.name` become "hello world", I don't know why.... – Sunny Sep 20 '13 at 14:03
  • 1
    @Virus721 Because I want to have a template like "class" to make objects, and it has some default value, I just need change some value which different to the default value then get a "complete" object. Like here, active needn't be changed so I just write id and name, I hope `JSON.stringify()` can serialize the property "active" but it doesn't. – Sunny Sep 20 '13 at 14:09
  • @Sunny: Simply because the property `active` does not exist in your `p1` object. [You have to understand how JS resolves properties to their respective values](http://stackoverflow.com/a/15174505/1230836). You call `JSON.stringify` on `p1`, the object is unaware of any `active` propery. It's not until you try and access `p1.active` JS will check the _"parent"_ object prior to the `Object.prototype`, and attempt to resolve the expression `p1.active` to a value. – Elias Van Ootegem Sep 20 '13 at 14:26

2 Answers2

1

From the MDN page on getOwnPropertyNames:

Returns an array of all properties (enumerable or not) found directly upon a given object.

The active property is on the proto object of p1, not on p1.

You can iterate over all properties of p1 (including those on the prototype chain) using the for in construct. To create a flat object, you can do this:

for (var key in obj) {
  if (!obj.hasOwnProperty(key)) {
    obj[key] = obj[key];
  }
}

Object.getOwnPropertyNames: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames

for in: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in

Object.create: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

Tibos
  • 27,507
  • 4
  • 50
  • 64
  • Seems a good way, thanks. By the way, I find a way which use new function to create an object, like: `var Profile = function(syntax){this.id="";this.name="";this.active=false;for(var i in syntax){this[i]=syntax[i];}}` `new Profile({id:123, name: "abc"})`, is Object.create() better than new Function ? – Sunny Sep 20 '13 at 14:23
1

Because of your usage of Object.create, you're not really cloning/copying anything into the resulting object (you call p1). First, you have to understand how JS resolves object properties, upon accessing them:

J [     p1.active     ]<=========================================================\ \
S p1[active] ==> JS checks instance for property divide                           | |
O  /\ ||                                                                          | |
N  || || --> property found @instance? return value-------------------------------| |
=  || ||                                                                          | |
   || ===========> p1.prototype is found: Profile, check that object              | |
N  ||      ||                                                                     | |
O  ||      ||--> Profile.active found @Profile instance, return-------------------| |
T  ||      ||                                                                     | |
   ||      ==========> Profile.prototype.active (== Object.prototype.active)      | |
J  ||          ||                                                                 | |
S  ||          ||--> property found @Object.prototype, return---------------------|_|
O  ||          ||                                                                 |=|
N  ||          =======>prototype is null, return "undefined.active"~~~~~~~~~~~~~~~|X|
   ||                                                                             \ /
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~< TypeError can't read property 'x' of undefined

That's basically, what can happen. Now upon calling JSON.stringify(p1), JS will just turn to the p1 object, and process its properties (the ones defined @instance level). The p1 object is unaware of there being an active property defined on Profile.
JSON.stringify only checks the steps I've marked with JSON (the first cycle), all other checks (prototype-chain) is of no interest to JSON. 9/10, this is just the Object.prototype anyway, and you don't want to stringify that, now would you?

To be brutally honest, for what you (seemingly) want to accomplish, the easiest solution would be this:

p1 = JSON.parse(JSON.stringify(Profile));
p1.id = 123;
p1.name = 'hello world';
console.log(JSON.stringify(p1));//will show active property!

Check the link in my last comment, for more details on this matter

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • Thanks for your explanation and I understand what happened in Object.create() and how js find "active". And I think what I need is "instance" but not "subclass", but Object.create() can only create "subclass" and if I want to get an "instance" I should use new Profile() like I comment in Tibos answer? – Sunny Sep 20 '13 at 15:18
  • 1
    @Sunny: Yes, either create a new instance, or use my last snippet as an example: `JSON.parse(JSON.stringify(Profile))`, this effectively copies the object (not the methods, nor its prototype chain, but you will end up with a new object, that has all properties that were defined @instance level, including the `active` property) – Elias Van Ootegem Sep 20 '13 at 15:26
  • Mighty perty graph ye have der. Thanks, this actually was something I recently had trouble with and your explanation cleared that right up :) – Chris Cirefice Oct 29 '13 at 23:14