1

I've been reading Is JavaScript's "new" keyword considered harmful? and this Adobe Article on using prototypal inheritance rather than 'new'. The Adobe article has a 'new' example:

function Foo() {
    this.name = "foo";
}
Foo.prototype.sayHello = function() {
    alert("hello from " + this.name);
};

...that it replaces with:

var foo = {
    name: "foo",
    sayHello: function() {
        alert("hello from " + this.name);
    }
};

Here the 'sayHello' method isn't attached to the object's prototype. Doesn't this mean 'sayHello' gets unnecessarily duplicated in memory (I know V8 avoids this, but in older JS engines, for example) as it is copied to all objects that inherit from foo?

Shouldn't it be:

var foo = {
    name: "foo",
    prototype: {
        sayHello: function() {
            alert("hello from " + this.name);
        }
    }
};

Or similar?

Community
  • 1
  • 1
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • Using `new` doesn't mean you are not using prototypal inheritance – Ruan Mendes Mar 28 '14 at 20:22
  • @JuanMendes The argument (as Crock writes it) is that object cloning rather than constructor functions is more consistent with other prototypal languages - see http://javascript.crockford.com/prototypal.html . That said, as others have mentioned, `Object.create()` just calls `new` anyway. – mikemaccana Mar 30 '14 at 11:39

3 Answers3

2

The method is not attached to the prototype because this very object becomes the prototype that is attached to newly created objects with Object.create. And you don't need a prototype of the prototype.

Remember that Object.create doesn't deep clone objects. Rather, it is equivalent to something like

Object.create = function(proto) {

   function F() {}
   F.prototype = proto;
   return new F();

}

(actual implementation is more complicated but this short version illustrates the idea)

All newly created objects "inherit" the method from the foo which acts as the prototype and there is no duplication here.

Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • Perhaps it's my understanding of `prototype` that's incorrect. I always read `prototype` (the thing literally called `prototype`) was 'things to be inherited by child objects' rather than `__proto__` which is the real parent object. But I can that's not correct in testing... – mikemaccana Mar 30 '14 at 11:34
  • OK read http://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript which clarified. – mikemaccana Mar 30 '14 at 11:35
  • The `prototype` from the constructor function is attached as the `__proto__` to objects created by the constructor function (and objects created by `Object.create` as well, as `Object.create` is a smart wrapper over an anonymous constructor function). Thus, when you call `var f new Foo()`, the `f.__proto__` points to `Foo.prototype`. The latter, `Foo.prototype` can be accessed and changed freely, the former, `f.__proto__` is not intented to be changed directly. – Wiktor Zychla Mar 30 '14 at 12:49
  • Btw. I don't see anything "harmfull" in `new`. Actually, it is simpler to define inheritance with `new` and prototypes. On the other hand, prototypal inheritance with `Object.create` is slightly tricker if you need a hierarchy of inheriting objects (like `Worker` iheriting from `Person` and newly created objects inheriting from one of these two). – Wiktor Zychla Mar 30 '14 at 12:52
2

No, it won't get duplicated if you create another object using that as a protoype. The almost equivalent code using Object.create is actually slightly different, you are not using the prototype, you are just creating an object. To use prototypal inheritance, do the following. Note that using new still sets up the prototype chain so the title of the article you linked to is not very accurate and you are still sharing the properties on a single object.

var foo = {
    name: "foo",
    sayHello: function() {
        alert("hello from " + this.name);
    }
};

var extended = Object.create(foo);
var extended2 = Object.create(foo);

extended.name = "first";
extended2.name = "second";
extended.sayHello();  // hello from first
extended2.sayHello(); // hello from second

// Methods are shared, outputs true
console.log(extended.sayHello === extended2.sayHello)

// Now if you delete the property again, it will go up the chain
delete extended.name;
extended.sayHello(); // hello from foo

You could also just do

var extended = Object.create(Foo.prototype);

It would get duplicated if you create a constructor to get new instances instead of Object.create or new Foo

function createFoo() {
    return {
        name: "foo",
        sayHello: function() {
             alert("hello from " + this.name);
        }
    }
}

var a = createFoo();
var b = createFoo();
 // The function objects are not shared    
alert('Are functions the same? ' + a.sayHello === b.createFoo);

They would also not be shared if you use the closure approach to creating objects. Crockford suggests that to create truly private members. I don't use it because it doesn't use the prototype chain and inheritance is tricky to implement without just copying properties.

Function Foo() {
    var name = 'foo';
    this.sayHello = function () {
        alert(name);
    };
    this.setName = function (newName) {
        name = newName;
    };
}

var a = new Foo();
var b = new Foo();
console.log(a.sayHello === b.sayHello); // outputs false
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • Thanks @Juan. I can see that's the case, but still wasn't sure /why/ after reading your answer. Reading http://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript helped. – mikemaccana Mar 30 '14 at 12:06
0

Going to answer my own question here, for my own notes and also to help explain to others:

Q. Using Object.create(), should exemplars have methods attached to their prototype property?

A. No, because Object.create(parentObject) itself sets up the parentObject as the prototype of a dynamically-created constructor.

Also: prototype is always a property on constructor Functions - not on regular Objects. Eg:

var someArray = [];
someArray.__proto__ === Array.prototype

Object.create() dynamically creates constructors, setting the prototype on them to the object in its first argument.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494