1

I'm trying to get a better grip on the what and whys of javascript classes. Specifically, I'm trying to understand the differences between assigning a method to a prototype vs using a this.methodName = function ... statement in a constructor. So, I did an experiment:

function CThis(){
  this.method= function() {
    console.log("method of ",this);
  };
}

function CProto(){
}

CProto.prototype.method = function() {
  console.log("method of ",this);
};

window.onload = function(){
  ct = new CThis(); 
  ct.method();
  cp = new CProto();
  cp.method();
};

My hypotheses was that the two would behave the same way, but I learned something. This was the output:

"method of " Object { method: CThis/this.method() } oop.js:3:4
"method of " Object {  } oop.js:11:2

Using this.method in the constructor actually gave me the behavior I would want from an instance of a class in a typical oop program: ie, "this" was referring to the class instance. Using the prototype method, it seems that this referred to an empty object.

I guess my question here is threefold:

  1. What is the "this" referring to inside CProto.prototype.method?
  2. What's the rest of the story with respect to assigning a function to this inside a constructor vs using the object's prototype?
  3. Seemingly, the version using this. inside a constructor is the one doing what I'd want to do (namely, be able to access the variables inside an instance of a class). Given that, why do the javascript oop tutorials talk about prototypes so much?

Thanks in advance!

---EDIT---

I thought a little more about this and realized that perhaps it would be worthwhile to extend the example beyond the single method and try to see what variables where accessible.

function CThis(){
  this.localthis = "I'm this.localthis";
  var localvar = "I'm localvar";
  this.method= function() {
    console.log("method of ",this);
    console.log("value of this.localthis:", this.localthis);
    console.log("value of localvar with this.:", this.localvar); 
    console.log("value of localvar without this.:", localvar); 
  };
}

function CProto(){
  this.localthis = "I'm this.localthis";
  var localvar = "I'm localvar";
}

CProto.prototype.method = function() {
  console.log("method of ",this);
  console.log("value of this.localthis:", this.localthis); 
  console.log("value of localvar with this.:", this.localvar);  
  console.log("value of localvar without this.:", localvar);  
};

window.onload = function(){
  ct = new CThis(); 
  ct.method();
  cp = new CProto();
  cp.method();
};

And the new output:

method of " Object { localthis: "I'm this.localthis", method: CThis/this.method() } oop.js:5:4
"value of this.localthis:" "I'm this.localthis" oop.js:6:4
"value of localvar with this.:" undefined oop.js:7:4
"value of localvar without this.:" "I'm localvar" oop.js:8:4
"method of " Object { localthis: "I'm this.localthis" } oop.js:18:2
"value of this.localthis:" "I'm this.localthis" oop.js:19:2
"value of localvar with this.:" undefined oop.js:20:2
ReferenceError: localvar is not defined

So there are definitely differences with respect to variable scope (in the this.method, I can access var variables from inside the constructor).

JawguyChooser
  • 1,816
  • 1
  • 18
  • 32
  • 2
    Possible duplicate of [Use of 'prototype' vs. 'this' in JavaScript?](http://stackoverflow.com/questions/310870/use-of-prototype-vs-this-in-javascript) – James Thorpe Jan 27 '16 at 17:29
  • 2
    `this` was still the instance of CProto, the console just didnt include the prototype methods in the console output. – Patrick Evans Jan 27 '16 at 17:29
  • So far, I think the best answers are in these comments! @James Thorpe: thanks for the link, that thread has a lot of good information. Patrick Evans: thanks for addressing my confusion about why the outputs weren't the same. – JawguyChooser Jan 27 '16 at 17:42

2 Answers2

0

In the first case each instance you create has its own copy of the method, and therefore when you print the object out it's visible. The JS engine also likely has to perform the compilation step on this function each time the constructor is called.

In the second case, none of the instances you create have any properties at all. Instead, when you call the method, the js engine walks up the prototype chain looking for a property of the right name. It looks at cp.__proto__ and finds a pointer to the object which you're referring to as CProto.prototype, which does have a property called "method" which it can call.

As an additional test, add a real instance variable to your classes. Add this.foo = 42; to both constructors, then see what you get.

db48x
  • 3,108
  • 24
  • 16
0

Firstly, javascript OO is prototypical, so it's not exactly like Class-based OO. You seem to have grasped the essential parts.

The way Prototypical inheritance works is a bit like a backwards data-tree: if you don't find the property on the object itself, look at it's prototype, if it's not on it's prototype, look at it's prototype's prototype and so forth until there is no prototype left.

1. What is the "this" referring to inside CProto.prototype.method?

when calling a METHOD, this always refers to the contextual object (i.e. the object used to call the method).

obj1.hello() // this === obj1
obj2.hello() // this === obj2

Object.getPrototypeOf(obj1).hello() // this is now global since you didn't use a contextual object.

obj1.hello.call(obj2) // this === obj2, because we forced a context change

2. What's the rest of the story with respect to assigning a function to this inside a constructor vs using the object's prototype?

Assigning to the prototype makes all instances of the prototype inherit that method. Assigning within the constructor means only the instance has the method. Otherwise, it's essentially the same. The only advantage a instance method will have is access to the instance's private context (closure).

When assigning within the constructor, the object itself is the "owner" of the method. This changes how Object.key() and for in will work. When it's on the Prototype, the prototype object is the "owner" so your instance objects don't have the method as an own property. It may seem trivial, but this is an important distinction, especially when looping/cycling through object properties.

When you're assigning via the prototype there is only one instance of your method (it's on the prototype). When you're assigning via the constructor, you're creating a new context and a new function for each instance. While insignificant, there is a performance difference.

3.

Pretty much explained with the two above answers.

Sebastien Daniel
  • 4,649
  • 3
  • 19
  • 34
  • Thanks! W.r.t. your answer to (2), I think there's a bit of a distinction without a difference. You say "assigning to a prototype makes all instances of the prototype inherit that method" and "assigning within the constructor means only the instance has the method". But a constructor is used to create instances, so effectively, what's the difference here? Aren't all instances going to be created by calls to the constructor? The only difference I can find so far is your grammatical use of singular vs plural. Cheers! – JawguyChooser Jan 27 '16 at 17:42
  • improved point 2 answer. And yes, all instances are created via the constructor. Though you could bypass the constructor by doing `Object.create(myPrototype, {});`, creating an object with the same prototype as your constructor instances. – Sebastien Daniel Jan 27 '16 at 17:46
  • @JawguyChooser Anything else need to be clarified? – Sebastien Daniel Jan 27 '16 at 18:01