25

I am wondering can we still change the function body once it is constructed ?

     var O = function(someValue){
           this.hello = function(){
                return "hello, " + someValue;
           }
     }

     O.prototype.hello = function(){
           return "hhhhhhh";
     }

     var i = new O("chris");
     i.hello();   // -> this still returns the old definition "hello, chris"

The javascript statement O.prototype.hello = function(){....} doesn't override and redefine the hello function behavior. Why is that ? I know it will have a type error if you tried to reuse the parameter someValue.

      // this will fail since it can't find the parameter 'someValue'
      O.prototype.hello = function(){
             return "aloha, " + someValue;
      } 

I am wondering why It allows to add function during runtime like

      O.prototype.newFunction = function(){
           return "this is a new function";
      }

      i.newFunction();   //  print 'this is a new function' with no problem.

but doesn't allow you to change the definition once it's defined. Did i do something wrong ? how do we override and redefine a function within a class ? and is there a way to reuse the parameter that we passed in earlier to create the object ? in this cases how do we re-use someValue if we want to extend more functions to it.

peter
  • 7,752
  • 16
  • 64
  • 89

8 Answers8

26

When you use new, the value of this inside the constructor points to the newly created object (for more information on how new works, take a look at this answer and this answer). So your new instance i, has a hello function. When you try to access the property of an object, it walks up the prototype chain until it finds it. Since hello exists on the instance of the object, there is no need to walk up the prototype chain to access the version of hello that returns hhhhhhhh. In a sense, you have overridden the default implementation in your instance.

You can see this behavior if you don't assign hello to this inside your constructor:

var O = function(someValue) {

 }

 O.prototype.hello = function(){
       return "hhhhhhh";
 }

 var i = new O("chris");
 console.log(i.hello()); //this prints out hhhhhhh

What you're doing is kind of backwards. The prototype basically provides the "default" form of something, which you can override on a per-instance basis. The default form is used only if the property you're looking for cannot be found on the object. That is, JavaScript will start walking up the prototype chain to see if it can find a property that matches what you're looking for. It it finds it, it will use that. Otherwise, it will return undefined.

What you basically have in the first case is as follows:

Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
     |
     +----i.hello (returns "hello, chris")

So when you do i.hello, JavaScript sees that there is a hello property on i and uses that. Now if you didn't explicitly define a hello property, you basically have the following:

Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
     |
     +----i.hello (is "undefined", so JavaScript will walk up the chain until 
                   it sees O.prototype.hello, which does have a defined value 
                   it can use.)

What this means is that you can provide a default implementation in the prototype, and then override it (in a sense it's like sub-classing). What you can also do is modify the behavior on a per-instance basis by directly modifying the instance. The version of hello that you have on the prototype is kind of a fail-safe and a fall-back.

EDIT: Answers to your questions:

Overriding on a per-instance basis means you attach a property or a function to a particular instance. For example, you could do:

i.goodbye = function() {
    return "Goodbye, cruel world!";
};

Which means that this behavior is specific to that particular instance (i.e., only to i and not to any other instances that you may have created).

If you take out this, then you have basically have:

hello = function() {
    return "hello, " + someValue;
}

Which is equivalent to doing:

window.hello = function() {
    return "hello, " + someValue;
}

So in this case, hello is a global reference to that function. What this means is that hello isn't attached to any of your objects.

hello can be undefined if you don't have this.hello = function() { .... }; inside your constructor. I was also talking about the general process that JavaScript uses to try to resolve properties on objects. As I mentioned before, it involves walking up the prototype chain.

Community
  • 1
  • 1
Vivin Paliath
  • 87,975
  • 37
  • 202
  • 284
  • Can you explain more what happen behind the scene ? I am not quite understand about the flow. – peter Aug 29 '12 at 17:17
  • 3 questions : (1) by saying 'override on a per-instance basis' what did you mean by that ? (2) what happen if we have it without 'this' keyword ? (3) what did you mean by explicitly define a hello property ? in what case will you have i.hello that is undefined ? – peter Aug 29 '12 at 18:12
3

When you create an instance of the O object using new O("somename"); you are assigning an instance method to the newly created object. When you then assign another method of the same name to O's prototype the method is already shadowed by the instance method. So:

Object.prototype.hello // undefined
       |
       O.prototype.hello // alternate function
         |
         i.hello // original function provided in constructor

JavaScript starts at the bottom of the chain and stops when it finds a match for the name. So it stops at i.hello and never sees O.prototype.hello.

JavaScript (as of ECMAScript 5) really doesn't (as far as I am aware) give you a good way to do private variables like that that can be accessed by instance methods added after definition (either added on the instance or on the prototype). Closures get you most of the way there but if you want to be able to add methods outside of the closure that have access to closure variables you need to expose get and / or set methods that give these new methods access to the closure variables:

// Possibility #1 - marked as private member
var O = function(someValue) {
    this._someValue = someValue;
};
O.prototype.hello = function() { return "hhhh"; };

var i = new O("somename");
i.hello = function() { return "aloha," + this._someValue; };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #2 - factory function + closure with get and set methods
var OMaker = function(someValue) {
    var realO = function() {};
    realO.prototype.getSomeValue = function() { return someValue; };
    realO.prototype.setSomeValue = function(newVal) { someValue = newVal; };
    realO.prototype.hello = function() { return "hhhh"; };
    return realO;
};
var O = OMaker("somename"),
            i = new O();
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

// Possibility #3 - eschew prototype inheritance and create new objects
var O = function(someValue) {
    return {
        getValue: function() { return someValue; },
        setValue: function(newValue) { someValue = newValue; },
        hello: function() { return "hhhh"; }
    };
};
var i = O(); // Note the lack of the "new" keyword
i.hello = function() { return "aloha," + this.getSomeValue(); };
console.log(O.hello());  // hhhh
console.log(i.hello());  // aloha, somename

You'll really want to read bobince's great answer on OOP in JavaScript for more information on the subject.

Community
  • 1
  • 1
Sean Vieira
  • 140,251
  • 31
  • 286
  • 277
  • @user1389813 - no, you can, you just need to define the function on the prototype first and then you can override it on the instance. For "private" variables it gets a bit wonkier - I've updated my answer with some examples. – Sean Vieira Aug 29 '12 at 17:41
  • Thanks for the updates. For possibility #2, i am wondering what if you make all the functions like realO.getSomeValue without realO.prototype instead ? and closure just basically works like an object, correct ? like typical OOP language object right? – peter Aug 29 '12 at 18:05
  • @user1389813 - then you wind up with individual unconnected objects. You can do it, you just avoid JavaScript's OO tools completely. See my updated answer :-) – Sean Vieira Aug 29 '12 at 18:50
  • I notice possibility#2 and #3 both end up with the same result. what are the significant difference there ? and which one is the best approach for doing OO javascript in practice ? is it the closure way ? – peter Aug 29 '12 at 19:35
  • @user1389813 - it would be more correct to say that #1 and #2 wind up with the same results (albeit #2 has getter and setter methods) - `i instanceof O` is true in both cases and you can augment instances of `O` by adding to `O.prototype`. In #3 `i instanceof O` is *false* and updating the prototype of `O` does not update "instances" of `O` such as `i`. – Sean Vieira Aug 29 '12 at 21:14
  • why possibility#3 without the new keyword ? – peter Sep 01 '12 at 14:42
  • @user1389813 - because when you call a function with `new` you are asking JavaScript to create a new object for you that you can assign properties to via `this` - however in #3 we are returning our own "custom" object so we don't need to call our constructor with `new` - we can but we don't need to. – Sean Vieira Sep 01 '12 at 17:03
  • So all the properties including variables and functions will get copied over to the instance whenever you call 'new' ? I am wondering what happen to those methods that don't have this. Is it still accessible ? – peter Sep 01 '12 at 18:54
1

No. You cannot, but here is a good example of ways around that limitation by patterning your inheritance in a different way.

JavaScript override methods

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"
Community
  • 1
  • 1
Victor 'Chris' Cabral
  • 2,027
  • 1
  • 16
  • 30
  • Doesn't this mean it is the same vehicle for all cars? (problem 1) http://blog.jackiergleason.com/ – Jackie Aug 20 '15 at 14:18
1

Firstly, you need to understand prototypal inheritance.

When you create an object using O as a constructor, this happens:

  • 1st, a new object is created.
  • 2nd, the hello property of that object is set to a function (via the constructor you defined).
  • 3rd, a secret link from the object, pointing to the O.prototype object, is created.

When referencing properties of O objects, those properties are first looked for in the object itself. Only if the object doesn't have the property itself does it look up to it's prototype.

Secondly, you need to understand closures.

someValue is a variable (not a property) defined in the O function. It can only be accessed from other things that are also defined in the same function (or any functions defined inside the O function). So, we say "someValue was closed over". It can't be accessed by a function that you define outside of O.


To achieve what you want, you either need to set someValue to a property (which makes it less like a private thing and more like a public thing). Or, you need to define all the functions that need access to someValue inside of O's original definition.

To change what i.hello points to after i has been created, you need set the property of the object directly.

i.hello = function () { /* stuff */ };

MicronXD
  • 2,108
  • 15
  • 23
0

If I recall correctly, functions that are direct members of objects take precedence over like-named members of that object's prototype. Therefore, O.prototype.hello is usurped by O.hello, even though the former is defined later in the code.

The reason someValue isn't available to your O.prototype.hello is because the scope of someValue is constrained to the constructor function and any functions defined or executed within it. Since O.prototype.hello is defined outside the scope of the O constructor, it doesn't know about someValue

jackwanders
  • 14,192
  • 3
  • 38
  • 39
0

This is because when you access a property of an Object, JavaScript check the properties of the objects first, before going into its prototype.

This is analogous to Java's derived class overriding the base class functionality.

For a better understanding check the code example in Inheriting properties

Also note that someValue in your case is local to the constructor function. If you need it in other functions, you should assign it to this.someValue inside the constructor.

You'll be able to override the hello function for a particular Object here like the following. But not for the entire Class.

i.hello = function(){ console.log('even here someValue is not accessible');};

  var O = function(someValue){
       this.someValue = someValue;
       this.hello = function(){
            return "hello, " + someValue;
       }
 }

 var i = new O("chris");
 console.log(i.hello()); // prints hello, chris
 i.hello = function() { 
   return 'Hi there '+ this.someValue;
 }
 console.log(i.hello()); // prints Hi there chris

 var test = new O('Sujay')
 console.log(test.hello()) // this still prints hello, Sujay

Note that here we have not changed the constructor, and hence this will not work with other instances like test in the above example.

The best way to do it would be to define functions only in the prototype & not in the constructor, like the following snippet.

 var O = function(someValue){
      this.someValue = someValue;
 };
 O.prototype.hello = function(){
            return "hello, " + this.someValue;
 };

 var i = new O("chris");
 console.log(i.hello()); // prints hello, chris
 O.prototype.hello = function() { 
   return 'Hi there '+ this.someValue;
 }
 console.log(i.hello()); // prints Hi there chris

 var test = new O('Sujay')
 console.log(test.hello()) // prints Hi there Sujay
Sujay
  • 2,072
  • 21
  • 31
0

When you access a property, system first looks for it in the instance. If it is not found, it looks for it in the prototype. This is why this.hello is being used, rather than O.prototype.hello.

If you wish to override the implementation of hello, you will need to use JavaScript inheritance. Here is a basic example:

var A = function(){
    console.log("A is getting constructed");
};

A.prototype.constructor = A;
A.prototype.someValue = 1;
A.prototype.hello = function() {
    console.log("A.hello(): " + this.someValue);
};

var B = function(){
    //Constructor of A is automatically called before B's

    console.log("B is getting constructed");
};
B.prototype = new A; //Inherit from A
B.prototype.constructor = B;
B.prototype.hello = function() {
    console.log("B.hello() called");
    console.log("Calling base class method");
    A.prototype.hello.call(this);
};

var a = new A();
a.hello();

var b = new B();
b.hello();
RajV
  • 5,925
  • 6
  • 39
  • 54
0

Override method refreshEditor for created instance:

var cp = hot1.getPlugin('comments');
cp.refreshEditor = (function (original) {
  return function (force) {
    //do something additional
    if(console) {
      console.log('test!!!!!!!!!');
      console.log(force)
    }
    original.call(cp, force);
  }
})(cp.refreshEditor);
SmaLL
  • 1
  • 2