Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not understanding use of new keyword in Javascript

The code snippet below is from 'Javascript Web Applications' by O'Reilly. In it, the author explains that using the new keyword ordinarily returns a this context, unless you specifically return something else-- below, he's returning ' a function that would set up a new class', in his words (pg 7):

var Class = function(){
    var klass = function(){
        this.init.apply(this, arguments);
    };
    klass.prototype.init = function(){};
    return klass;
};

var Person = new Class;

Person.prototype.init = function(){
    // Called on Person instantiation
};

// Usage:
var person = new Person;

I'm not following this. What does returning klass do here? in the case of var Person = new Class, am I not getting a new Class object, but rather, some function that can create a Class? That's what I'm reading from the text but it's confusing to me.

like image 897
larryq Avatar asked Jan 14 '13 07:01

larryq


2 Answers

Before getting where he's going, the first step is getting this.

There are three things this can typically point to. If you have a function which is a property of an object:

var Bob = {
    name : "Bob",
    say  : function () { console.log(this.name); }
};

Bob.say(); // "Bob"

In this case, this points to whatever object owns the property (whatever is one-dot ahead, at the exact moment the function is called)

var say = Bob.say; // passing the function to a variable
var Sam = { name : "Sam" };
Sam.say = Bob.say; // passing the function to an object's property

say(); // undefined -- not what you were expecting
Sam.say(); // "Sam"

So this is decided at the last possible second.

Functions also have properties of their own, like objects.
Two of these are functions called .call and .apply and they allow you to run the function, but tell the function exactly what this is.

Remember how say(); didn't work on its own?

var say = Bob.say;
say.call(Bob); // "Bob" -- hooray!

The next part of this is the one we're most used to in object-oriented languages; using this along with new to make a new instance of a class:

var Person = function (name) {
    this.name = name;
    this.say = function () { console.log(this.name); };
};

var bob = new Person("Bob");
bob.say(); // "Bob"

Basically, inside of this constructor function (or any function, as constructors aren't special in JS), the function starts by checking if new was called.
If it was, set this to a brand new object, regardless of whether it's part of an object:

var Creatures = {};
Creatures.Person = Person;

Creatures.Person("Bob"); // `this` === Creatures
Creatures.name; // "Bob" -- whoops
var bob = new Creatures.Person("Bob");
bob.say(); // "Bob", yay!

So it's like the function is saying if (new) { this = {}; } at the top of the function.

I said there were three possibilities.
The third is that this === window
If you use a function where this isn't an object (either through call/apply, or as a property of an object) and new was not used to make a new object, then this points at window.
That is why say(); did not work on its own, before; window.say(); is the equivalent.

Back to new for a second -- new does a couple of other things.
Namely, it sets the value of the object's constructor:

var bob = new Person();
bob instanceof Person;  // true

It also gives objects made from that constructor access to the prototype object, which all instances share.

So now, look at what's going on inside of Class/Klass:
We're creating a new Class(); object.
Inside of the Class function, this = {}; (because of new).
Then, we're making a new "constructor" function, klass.

Then, we're setting the prototyped init function, which we .apply to any new this (in the klass, at the time a new instance is called).

Then we're returning the klass.

klass is actually what you're calling when you make a new Class

And when you call for a new object of a class, klass is being run.

Hope that helps with new and this and .call and Class/klass.

like image 62
Norguard Avatar answered Nov 11 '22 13:11

Norguard


In javascript the new keyword implicitly returns this.

When you have function Foo () {} and apply new Foo, under the hood javascript does this function Foo () { return this; }. He is overwriting the way javascript's new works by explicitly returning a different object (the klass object).

Look at this example.

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

function Bar () {
    return new Foo; 
};

var bar = new Bar;  // bar is now a Foo, because of the explicit return.

bar.name //=> 'foo'

If you can follow that you can follow what he is doing.

When you say var Person = new Class, it is actually returning the klass function and storing it in the Person variable (pointer).

like image 38
mwoods79 Avatar answered Nov 11 '22 13:11

mwoods79