Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we add functions to javascripts prototype?

Tags:

javascript

I am learning javascript.

Why are functions defined outside of the object?

function Man(firstName, lastName) { 

    this.firstName = firstName; 
    this.lastName = lastName;   
}

Man.prototype.getName = function () {    
    return this.firstName + this.lastName;
};

Looks a bit weird to me. It works if I add the function inside the class (removing .protoype etc) but I am being told to define the function outisde the object.

Why?

like image 289
user2627149 Avatar asked Jan 13 '23 19:01

user2627149


1 Answers

Looks a bit weird to me.

That's just how setting up the prototypes assigned by constructor functions works. And you're not the only one who finds it awkward, see below. :-) (Update: And see further below for how ES6 makes this so much easier.)

It works if I add the function inside the class (removing .protoype etc) but I am being told to define the function outisde the object.

If you do this:

function Man(firstName, lastName) { 

    this.firstName = firstName; 
    this.lastName = lastName;
    this.getName = function () {    
        return this.firstName + this.lastName;
    };
}

...then each Man object gets its own copy of the function. They don't share, you end up with a new function object for each Man object. (A smart engine can reuse the underlying function code, but there will be two function objects.) The purpose of prototypes is to create features that can be shared by the objects constructed by the constructor.

The other great thing about prototypes is that the object's connection to the prototype is ongoing, which means you can add features to objects that already exist, by adding to their prototype. Example:

function Man(firstName, lastName) { 

    this.firstName = firstName; 
    this.lastName = lastName;   
}

Man.prototype.getName = function () {    
    return this.firstName + " " + this.lastName;
};

var joe = new Man("Joe", "Bloggs");
console.log(joe.getName()); // "Joe Bloggs"

Man.prototype.introduction = function() {
    return "Hi there, I'm " + this.getName();
};

console.log(joe.introduction()); // "Hi there, I'm Joe Bloggs"

Note how we added introduction to the prototype after joe was created. It doesn't matter, the lookup is dynamic. When you do joe.introduction, the engine looks for an introduction property on joe and, if it doesn't find it, goes looking on the prototype. So adding to the prototype later (indirectly) enhances existing objects.


You're not the only one who finds the syntax awkward. You frequently see people creating "extend" methods like the one in jQuery, Underscore, Prototype, and so on. The extend method just assigns properties from a source object to a destination object. The simple form of it is:

function extend(target, source) {
    var name;
    for (name in source) {
        target[name] = source[name];
    }
}

...although usually you see something with more features (multiple source objects, returning the destination, etc.).

And then you can use it like this:

function Man(firstName, lastName) { 

    this.firstName = firstName; 
    this.lastName = lastName;   
}
extend(Man.prototype, {
    getName: function () {    
        return this.firstName + this.lastName;
    },
    introduction: function() {
        return "Hi there, I'm " + this.getName();
    }
});

That uses object initializer syntax (sometimes called an "object literal"), passes it into extend, and extend puts the properties on Man.prototype.

You also see things like my Lineage script which simplify that syntax further and handle making a few other things (like calling a parent prototype's version of a function) simpler.


As of ES6, which is very nearly finalized and is making its way into browsers (you can also transpile), this gets a lot simpler:

class Man { 
    constructor(firstName, lastName) {
        this.firstName = firstName; 
        this.lastName = lastName;   
    }

    getName() {    
        return this.firstName + " " + this.lastName;
    }

    introduction() {
        return "Hi there, I'm " + this.getName();
    }
}

var joe = new Man("Joe", "Bloggs");
console.log(joe.getName()); // "Joe Bloggs"

console.log(joe.introduction()); // "Hi there, I'm Joe Bloggs"
like image 93
T.J. Crowder Avatar answered Jan 22 '23 08:01

T.J. Crowder