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.

3
  • 2
    FYI If you replace john = Object.create(Person); with john = new Person; you will get the expected result. Commented Jul 19, 2014 at 12:26
  • 2
    possible duplicate of Understanding the difference between Object.create() and new SomeFunction() in JavaScript Commented Jul 19, 2014 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. Commented Jul 19, 2014 at 13:34

1 Answer 1

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.

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

3 Comments

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: 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!
@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!

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.