3

I tried to use the following code to add a start method to an object:

var Bounce = Bounce || {
    Info : {},
    Game : {}
};

Bounce.Game.prototype.start = function() {
    Bounce.log("Starting " + new Bounce.Info());
}

But this results in the following error (on the Bounce.Game.prototype.start line):

Uncaught TypeError: Cannot set property 'start' of undefined

Looking at the object in Chrome's console, I can see that it doesn't contain the prototype object (but has toString, valueOf and constructor etc.).

This is easily fixed by adding the following line before the prototype access:

Bounce.Game = function() {};

I don't know why this is necessary when the object has already been initialized?

W3Schools tells me "Every JavaScript object has a prototype", but that doesn't appear to be the case.

Michael
  • 7,348
  • 10
  • 49
  • 86
  • The "prototype", which you thought you were accessing, is actually `__proto__`. – Leo Jan 02 '15 at 11:13
  • Is it your intent that `Bounce.Game` be a class? If so, it should be a function rather than `{}`. If not, then I'm not sure what you hope to accomplish by setting a method on its prototype instead of directly on the object itself. – Mark Reed Jan 02 '15 at 11:29
  • 1
    Conceptually, all objects have a prototype, but only function objects have `prototype` **property**. They are not the same. If you read the ECMAScript spec, prototype is usually represented like **[[Prototype]]** which is an implementation detail lies in the JS engine rather than a language feature. However, in some engines **[[Prototype]]** can be accessed with `__proto__` property. – Leo Jan 02 '15 at 11:30
  • @MarkReed Yes. So is the W3Schools statement false then? – Michael Jan 02 '15 at 11:31
  • 1
    No. Every object has a prototype. It does not, however, have a property *named* `prototype`. Two different things. Yes, this is confusing. But they are related by the fact that the function's `prototype` property *becomes* the actual prototype for new objects created using that function as a constructor. – Mark Reed Jan 02 '15 at 11:32
  • 2
    Should really note that `__proto__` is non-standard - there are better [patterns](http://stackoverflow.com/search?q=%5Bjavascript%5D+constructor+patterns). – Emissary Jan 02 '15 at 11:35
  • @Emissary The standardized approach is `Object.getPrototypeOf()`, it returns the **[[Prototype]]**. – Leo Jan 02 '15 at 11:39
  • 1
    @Leo is right, however you won't be able to call Bounce.Game.start(). check this out: http://jsbin.com/bapuvo/1/edit?html,js,console,output – balanza Jan 02 '15 at 11:55
  • @balanza Interesting. The error ("TypeError: object is not a function") reinforces what others have said too. – Michael Jan 02 '15 at 12:05
  • 1
    Interesting question indeed, glad it's reopened. – Leo Jan 02 '15 at 12:36
  • 1
    The following answer explains about prototype and constructor functions, it may be helpful to you: http://stackoverflow.com/a/16063711/1641941 – HMR Jan 02 '15 at 15:44

2 Answers2

4

Conceptually, all objects do have a prototype, but only function objects (including constructors like Object, Array, although they don't produce functions) have a property named prototype. They are not the same.

If you read the ECMAScript spec, prototype is usually represented like [[Prototype]], which is an implementation detail lies in the JS engine, rather than a language feature. However, in some engines [[Prototype]] is exposed and can be accessed with __proto__ property (non-standard).


By the way:

  1. If you want to access [[Prototype]], Object.getPrototypeOf() is your friend.

  2. When using a instanceof b, it's actually comparing a's [[Prototype]] chain with b's prototype property.

  3. And why we say null is the prototype of all? It's also not referring to prototype but [[Prototype]]:

    Object.getPrototypeOf(Object.getPrototypeOf({})) // null
    Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf([]))) // null
    Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(new String("")))) // null
    
    // or shorter like this
    ({}).__proto__.__proto__ // null
    ([]).__proto__.__proto__.__proto__ // null
    ("").__proto__.__proto__.__proto__ // null
    
Leo
  • 13,428
  • 5
  • 43
  • 61
2

So, inspired by @Leo comments, I think about this solution to use a plain {} object with its prototype.

We have this object:

var Bounce = Bounce || {
    Info : {},
    Game : {}
};

We define the prototype property for the given object

Object.defineProperty(Bounce.Game, 'prototype', {
    get: function() {
      return Object.getPrototypeOf(Bounce.Game); 
    }
});

Now, we can use prototype as usual:

Bounce.Game.prototype.start = function(){
  console.log('start');
};

Bounce.Game.start();

Check this out: http://jsbin.com/bapuvo/3/edit

Community
  • 1
  • 1
balanza
  • 1,059
  • 1
  • 12
  • 34
  • Interesting approach. But why not simply `Bounce.Game.start = function...` ? – Leo Jan 04 '15 at 16:07
  • A prototype is meant to be "shared" across different instances, for inheritance and memory-saving reason. This solution is for the problem as @Mikaveli proposed, maybe I'll never use this approach. For instance, we don't know where this code is used and how: if it's in a full CommonJS-compliant environment, I'd rather use a proper module design solution (think about nodejs or titanium). – balanza Jan 04 '15 at 16:36
  • @Leo answering your question: yes, it's the same. As you are not gonna use `new` operator with a plain object, using prototype became pointless. – balanza Jan 04 '15 at 16:56