4

I try do improve my JavaScript skills. I don't understand why (5) works and (2) returns error. Isn't the same?

  1. B.fn() //OK
  2. B.fn2() //TypeError: Object # has no method 'fn2'
  3. var a = new A()
  4. a.fn() //OK
  5. a.fn2() //OK

    var A = function () {
        this.fn = function () { alert(3); }
    }
    A.prototype = {
        fn2: function () { alert(4); }
    };
    
    var B =
        {
            fn: function () { alert(1); }
        }
    B.prototype = {
        fn2: function () { alert(2); }
    };
    
theateist
  • 13,879
  • 17
  • 69
  • 109

4 Answers4

7

a is an instance of the A class, where as B is the class itself. Since fn2 is not defined as static function, it will only be available to an instance of class B as opposed to the class B itself.

If you wanted to use B directly, you could use:

new B().fn2()

if you define B as a function()

Alternatively, you could define fn2 the same way you have defined fn

Fareesh Vijayarangam
  • 5,037
  • 4
  • 23
  • 18
  • 2
    Actually `new B().fn2()` wouldn't work because there is no `B()` function defined anywhere. – rsp Mar 22 '11 at 08:43
1

(easy explanation) The prototype property only applies when using a function as constructor (by using the new operator). The function creates a clone of it's prototype and the this keyword inside the function is set to the clone. The properties on the clone are direct references/pointers to the prototypes properties.

The object literal {} is a more powerfull expression alternative to new Object() and as such "inherits" properties from Object.prototype.

So:

function ClassLike() {}
ClassLike.prototype = {
    foo : "bar"
}

var instance = new ClassLike();
alert( instance.foo ); // bar

Works because the new operator kicks some operations in motion to create a new object whereas:

var instance = {
    foo : "bar"
}
instance.prototype = {
    baz : "foobar"
}

Merely adds another property (prototype) to an already created object and no process is put in motion to actually assign/change the objects original prototype.

Now Mozilla has added a non standard (IE does not support it) way to change an already instantiated objects prototype through __proto__ and there are some petitions going to add it to ES5 (EcmaScript 5). I would not use it atm. but it works like this:

var instance = {};
var parent = {
    foo : "bar"
}
instance.__proto__ = parent;

alert( instance.foo ); // bar

Another way to change an already instantiated object's prototype is to add to the Object constructors prototype (which is not advised for many reasons). As such:

var instance = {}; // a more powerful alternative to `new Object()`

Object.prototype.foo = "bar";

alert( instance.foo ); // bar

It's all possible, though wether it's wise to do it... I'd say no, but opinions vary and I rather avoid the debate ;)

Anyways, just remember that the prototype property on works when you new a function, otherwise it just becomes a property on the instance.

BGerrissen
  • 21,250
  • 3
  • 39
  • 40
  • Thank you for your answer. I have relative question. I need privileged functions for accessing private members, right? If yes, so why when I call "new C().fn()", "this" doesn't see "m" member"? var C = function(){var m; this.fn=function(){return this.m;}} – theateist Mar 22 '11 at 10:06
  • `var m` creates a local variable, not a property. `this.m` tries to refer to a public property. The `this` keyword does not work the same as in other languages, it's just an 'injected' variable pointing to the owning object of the function. This would work: `var C = function(){var m=1; this.fn=function(){return m;}}` – BGerrissen Mar 22 '11 at 10:19
  • `__proto__` won't be added to ES5: ES5 is already a final non-changing standard. There is, however, `Object.getPrototypeOf(o)` which gives the prototype of `o`. Equally, it's not true to say "now Mozilla have added"; it has existed for a very long time, and also exists in Carakan (Opera), JSC (Safari), and V8 (Chrome). – gsnedders Mar 22 '11 at 20:40
0

Well, your answer is pretty simple.In JavaScript, only constructors have "prototypes" i.e. the prototype property. Object literals e.g "{}" don't. Therefore, your number 2 will never work unless you change it as follows:

var B = function(){ // the B constructor
          return this;
        }
B.prototype = {
fn2: function(){ alert(2); }
}
0

What you have demonstrated is caused by something that I would consider the biggest problem with OO in JavaScript: prototype must be a property of the constructor function and not the object itself. It means that if you have a single object that can be easily defined as an object literal, you still need to have a useless constructor just to define the prototype of your object.

Read this article: Prototypal Inheritance in JavaScript by Douglas Crockford. Here is the relevant part:

[...] JavaScript itself is conflicted about its prototypal nature. In a prototypal system, objects inherit from objects. JavaScript, however, lacks an operator that performs that operation. Instead it has a new operator, such that new f() produces a new object that inherits from f.prototype This indirection was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript. JavaScript's constructor pattern did not appeal to the classical crowd. It also obscured JavaScript's true prototypal nature. As a result, there are very few programmers who know how to use the language effectively. Fortunately, it is easy to create an operator that implements true prototypal inheritance. It is a standard feature in my toolkit, and I highly recommend it for yours.

Read the article for some ideas no how to effectively solve what you are trying to do.

rsp
  • 107,747
  • 29
  • 201
  • 177