1

I tested this snippet and execute with node.js

function Person(name) {
  this.name = name;
}


john = Object.create(Person);
john.name = 'John';
console.log(john.name);

It returns Person. Why doesn't it return 'John' ?

Update: my question is not about new vs create, I know perfectly how new and create should be used. My question is why I cannot set name property value as usual.

user310291
  • 36,946
  • 82
  • 271
  • 487
  • 2
    FYI If you replace `john = Object.create(Person);` with `john = new Person;` you will get the expected result. – Popnoodles Jul 19 '14 at 12:26
  • 2
    possible duplicate of [Understanding the difference between Object.create() and new SomeFunction() in JavaScript](http://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction-in-j) – Popnoodles Jul 19 '14 at 12:29
  • 1
    @Popnoodles I know new, my question is not about new vs create, it's why I cannot overload name property as usual. – user310291 Jul 19 '14 at 13:34

1 Answers1

4

It returns Person. Why doesn't it return 'John' ?

Because that Object.create line creates an object with the Person function as its prototype, and the Person function object has a surprising definition of the name property that prevents your writing to it. (The definition makes sense once you know about it, but it's surprising at first.)

What you're creating when you do

john = Object.create(Person);

...is an object that uses the Person function as its underlying prototype. This doesn't create a Person instance (which would use Person.prototype as its prototype), it creates an object that actually uses the function object Person itself as the prototype.

The Person function has a non-writable property called name which is the name of the function (this is not yet in the standard, but it will be in ES6 [currently it's defined in §9.2.11 of the draft spec] and V8 does it already). Because that property is not writable, john.name = 'John' doesn't do anything. (More on non-writable properties below.)

If your goal was to create a new object with Person.prototype as the object's underlying prototype, you'd do:

john = new Person();

or

john = Object.create(Person.prototype);

And since Person accepts an argument, you'd probably do

john = new Person('John');

...rather than assigning to name afterward.


More on non-writable properties: If you haven't run into them yet, they were defined as part of the 5th edition spec a while back. Here's an example:

var parent = {};
Object.defineProperty(parent, "foo", {
    writable: false,
    value: "original"
});

The parent object has an non-writable property, foo:

console.log(parent.foo); // "original"
parent.foo = "bar";
console.log(parent.foo); // "original"

If we use parent as a prototype, we still can't write to foo even on the child object:

var child = Object.create(parent);
console.log(child.foo); // "original"
child.foo = "bar";
console.log(child.foo); // "original"

That's what's happening in your code. parent is Person, and child is john.

And just to round this out: If we wanted to create a writable property on the child at this point, we could, but just not via assignment   we'd have to use defineProperty:

Object.defineProperty(child, "foo", {
    writable: true,
    value: child.foo    // (just sets the initial value)
});
console.log(child.foo); // "original"
child.foo = "bar";
console.log(child.foo); // "bar"

Now child has its own property called foo, which shadows (hides) its prototype's foo, and is writable.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Great answer, didn't know about non writeable property existence. Still am surprised as I would expect it should have thrown some error when trying to write on it if was not writeable. – user310291 Jul 19 '14 at 13:42
  • @user310291: Thanks! Yes, I probably should have mentioned that. In loose mode, usually this sort of thing won't throw an error. In *strict* mode, usually it does. And in fact, writing to a non-writable property in strict mode normally *does* throw an error. But if I take your program and add `"use strict";` at the top (and a `var` in front of `john = ...`), I still don't get an error even though an equivalent test with my `parent`/`child` example *does* throw an error. I don't know why writing to `john.name` wouldn't throw in strict mode. Strange! – T.J. Crowder Jul 19 '14 at 14:10
  • @user310291: Interestingly, my `parent`/`child` example doesn't throw in strict mode in Chrome, so it must relate to the specific version of V8... Still weird that it throws in NodeJS for my `parent`/`child` example but not for the `Person` function example! – T.J. Crowder Jul 19 '14 at 21:05