Asked  6 Months ago    Answers:  5   Viewed   30 times

I am wondering if there are any advantages of using any of these over the other, and which way should I go?

Constructor approach:

var Class = function () {

    this.calc = function (a, b) {
        return a + b;
    };

};

Prototype approach:

var Class = function () {};

Class.prototype.calc = function (a, b) {
    return a + b;
};

I don't like that, using the prototype, method definitions are separated from the class, and I'm not aware if there is any specific reason I should use this over just the first approach.

Also, is there any benefit of using a function literal to define a "class", over just function definition:

var Class = function () {};

vs

function Class () {};

Thanks!

 Answers

77

Methods that inherit via the prototype chain can be changed universally for all instances, for example:

function Class () {}
Class.prototype.calc = function (a, b) {
    return a + b;
}

// Create 2 instances:
var ins1 = new Class(),
    ins2 = new Class();

// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2

// Change the prototype method
Class.prototype.calc = function () {
    var args = Array.prototype.slice.apply(arguments),
        res = 0, c;

    while (c = args.shift())
        res += c;

    return res; 
}

// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3

Notice how changing the method applied to both instances? This is because ins1 and ins2 share the same calc() function. In order to do this with public methods created during construction, you'd have to assign the new method to each instance that has been created, which is an awkward task. This is because ins1 and ins2 would have their own, individually created calc() functions.

Another side effect of creating methods inside the constructor is poorer performance. Each method has to be created every time the constructor function runs. Methods on the prototype chain are created once and then "inherited" by each instance. On the flip side of the coin, public methods have access to "private" variables, which isn't possible with inherited methods.

As for your function Class() {} vs var Class = function () {} question, the former is "hoisted" to the top of the current scope before execution. For the latter, the variable declaration is hoisted, but not the assignment. For example:

// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); } 

// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }
Tuesday, June 1, 2021
 
diegoiglesias
answered 6 Months ago
28

See http://jsperf.com/prototype-vs-this

Declaring your methods via the prototype is faster, but whether or not this is relevant is debatable.

If you have a performance bottleneck in your app it is unlikely to be this, unless you happen to be instantiating 10000+ objects on every step of some arbitrary animation, for example.

If performance is a serious concern, and you'd like to micro-optimise, then I would suggest declaring via prototype. Otherwise, just use the pattern that makes most sense to you.

I'll add that, in JavaScript, there is a convention of prefixing properties that are intended to be seen as private with an underscore (e.g. _process()). Most developers will understand and avoid these properties, unless they're willing to forgo the social contract, but in that case you might as well not cater to them. What I mean to say is that: you probably don't really need true private variables...

Tuesday, June 1, 2021
 
Noob_Programmer
answered 6 Months ago
62

Yes, the first function has no relationship with an object instance of that constructor function, you can consider it like a 'static method'.

In JavaScript functions are first-class objects, that means you can treat them just like any object, in this case, you are only adding a property to the function object.

The second function, as you are extending the constructor function prototype, it will be available to all the object instances created with the new keyword, and the context within that function (the this keyword) will refer to the actual object instance where you call it.

Consider this example:

// constructor function
function MyClass () {
  var privateVariable; // private member only available within the constructor fn

  this.privilegedMethod = function () { // it can access private members
    //..
  };
}

// A 'static method', it's just like a normal function 
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};

MyClass.prototype.publicMethod = function () {
  // the 'this' keyword refers to the object instance
  // you can access only 'privileged' and 'public' members
};

var myObj = new MyClass(); // new object instance

myObj.publicMethod();
MyClass.staticMethod();
Friday, June 4, 2021
 
DaveRandom
answered 6 Months ago
45

It's just to properly reset the constructor property to accurately reflect the function used to construct the object.

Sub.prototype = new Super();

console.log(new Sub().constructor == Sub);
// -> 'false' 

Sub.prototype.constructor = Sub;
console.log(new Sub().constructor == Sub);
// -> 'true' 
Wednesday, June 30, 2021
 
seaders
answered 5 Months ago
100

Lets examine your code a little bit.

function Food(){}
function Bread(){}
function Sushi(){}
var basicFood = new Food();
Bread.prototype = basicFood;
Sushi.prototype = basicFood;

Note: When you set the same object as the prototype of two objects, augmentation in one prototype, will reflect in the other prototype as well. For example,

Bread.prototype = basicFood;
Sushi.prototype = basicFood;
Bread.prototype.testFunction = function() {
    return true;
}
console.log(Sushi.prototype.testFunction()); // true

Lets get back to your questions.

var bread = reconstructify(new Bread(), Bread);
var sushi = reconstructify(new Sushi(), Sushi);
console.log(sushi instanceof Bread);    // Why true?
console.log(bread instanceof Sushi);    // Why true?

As per the instanceof docs from MDN,

The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor.

So when we do something like

object1 instanceof object2

JavaScript will try to find if the prototype of the object2 is in the prototype chain of object1.

In this case, it will return true only when the Bread.prototype is in the prototype chain of sushi. We know that sushi is constructed from Sushi. So, it will take Sushi's prototype and check if it is equal to Bread's prototype. Since, they both point to the same basicFood object, that returns true. Same case for, bread instanceof Sushi as well.

So, the right way to inherit would be, like this

function Food()  {}
function Bread() {}
function Sushi() {}

Bread.prototype = Object.create(Food.prototype);
Bread.prototype.constructor = Bread;
Sushi.prototype = Object.create(Food.prototype);
Sushi.prototype.constructor = Sushi;

var bread = new Bread();
var sushi = new Sushi();

console.log(sushi instanceof Bread);  // false
console.log(bread instanceof Sushi);  // false
console.log(sushi.constructor);       // [Function: Sushi]
console.log(bread.constructor);       // [Function: Bread]
console.log(sushi instanceof Food);   // true
console.log(bread instanceof Food);   // true
console.log(sushi instanceof Sushi);  // true
console.log(bread instanceof Bread);  // true
Monday, August 9, 2021
 
Thiago Custodio
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share