DarkFm
DarkFm

Reputation: 440

JavaScript functions and the new operator

What is the difference between adding a property to a function object vs, adding a property to the objects prototype. See code below.

var foo = function() { this.name = alert("test") };

foo.newProp = function() { ... };
var value = new foo();

// vs

foo.prototype.newProp = function() { ... }
var value = new foo();

My confusion is what happens to value when this code is run for each, AND how 'this' is affected.

Upvotes: 0

Views: 334

Answers (3)

user1636522
user1636522

Reputation:

Intro

Since functions are objects, case 1 is nothing more than adding a property to an object. Case 2 is more interesting. Consider the following code :

function f (name) {
  this.name = name;
}

f.prototype.sayHello = function () {
  console.log("Hello I'm " + this.name + " !");
};

var x = new f("X");
var y = new f("Y");
<button type="button" onclick="x.sayHello()">x.sayHello()</button>
<button type="button" onclick="y.sayHello()">y.sayHello()</button>

Note that there is nothing like x.sayHello = function () {...} in this code. Think of it twice and ask yourself :

Since x.sayHello is never explicitly defined, what is the underlying mechanism that prevents x.sayHello() from crashing the program ?

That is the question.

Unveil the magic

Fortunately, there is nothing magical, only JavaScript internal processing. Indeed, when you create an object with new, or when you call a function, there is something happening behind the scene in which the prototype property is involved.

When you write new f(), the JavaScript engine :

  1. Creates a new object.
  2. Sets this to the new object.
  3. Executes f.
  4. Adds a property called __proto__ to the new object.
  5. Binds __proto__ to f.prototype.
  6. Returns the new object.

When you write x.sayHello(), the JavaScript engine :

  1. Looks into x.
  2. Goes to step 6 if sayHello is found.
  3. Looks into x.__proto__ otherwise.
  4. Goes to step 6 if sayHello is found.
  5. Raises an exception and exits otherwise.
  6. Sets this to x.
  7. Executes sayHello.

Key points to highlight :

  • When you use the new keyword, JavaScript creates a prototype chain, i.e. the engine performs the following task : x.__proto__ = f.prototype.
  • When you try to call a function, JavaScript performs a lookup through this prototype chain. For example, when you write x.sayHello(), if JavaScript fails to find sayHello into x, it looks into x.__proto__ which is f.prototype.
  • The value of this depends on the context. For example, when you write x.sayHello(), this is x, when you write y.sayHello(), this is y, when you write new f(), this is a new object. More about this.

For the ninjas

Custom implementation :

function instanciate (f, args) {
  var object = {};
  f.apply(object, args);
  object.__proto__ = f.prototype;
  return object;
}

function execute (object, fName, args) {
  var f = lookup(object, fName);
  if (typeof f !== "function") {
    throw "not a function";
  } else {
    f.apply(object, args);
  }
}

function lookup (object, key) {
  do {
    if (object.hasOwnProperty(key)) {
      return object[key];
    } else {
      object = object.__proto__;
    }
  }
  while (object !== null);
}

function f (name) {
  this.name = name;
}

f.prototype.sayHello = function () {
  console.log("Hello I'm " + this.name + " !");
};

var x = instanciate(f, ["X"]);
var y = instanciate(f, ["Y"]);
<button 
  type="button"
  onclick="execute(x, &quot;sayHello&quot;)"
>execute(x, "sayHello")</button>
<button
  type="button"
  onclick="execute(y, &quot;sayHello&quot;)"
>execute(y, "sayHello")</button>

Side note

The global object is not special :

f.prototype.__proto__ = window.__proto__;
window.__proto__ = f.prototype;
name = "John Doe";
sayHello(); // "Hello I'm John Doe !"

Related links

Upvotes: 3

Rahul Arora
Rahul Arora

Reputation: 4523

Firstly, we know that functions are nothing but objects as we can attach properties to it. That is what is happening in the case1. We are just simply attaching a property to a function Foo

Case 1

var Foo = function(){
    this.name = "myname";
}

Foo.newProp = function(){}; 

The property attached to the function can be simply accessed as how we access properties of an object.

Case 2

In this case the same property is sitting on the prototype of that function. When any object is created using this function Foo, it will get access to this function by default.

When calling the function using that object, the context this will know the object calling that function and this points to the object calling it.

Foo.prototype.newProp = function(){ console.log("function on prototype");}

var value = new Foo();

console.log(value);

Also in case 2, since we are adding properties to the prototype of the function, they are not sitting on the object made using that function but on the proto of that object. Check this screenshot shown below:

enter image description here

That means there is a common memory location to which every object accessing that property will be pointing and hence it is memory efficient as well.

You can always go ahead and modify that property by overriding it by defining it directly on the object as:

value.newProp = function(){ console.log("modified property") };

Now, if you call this function, it will find it directly on the object and wont go down the prototype chain to access it from the common memory location i.e prototype of the function Foo

Upvotes: 2

RIYAJ KHAN
RIYAJ KHAN

Reputation: 15292

foo.newProp = function() { ... };?

This will define function at function(class) level.

You can access it directly without using object.

foo.newProp() // call it.

foo.prototype.newProp = function() { ... };?

Its define the newProp at instance level(object or class instance).

You need define the object using new then only you will able to access it.

var value = new foo();
value.newProp() // call it

By defining the function using prototype,you make it available to all already + newly defined object the given function

Upvotes: 0

Related Questions