Skip to content

Instantly share code, notes, and snippets.

@sycobuny
Created November 20, 2012 20:17
Show Gist options
  • Save sycobuny/4120759 to your computer and use it in GitHub Desktop.
Save sycobuny/4120759 to your computer and use it in GitHub Desktop.
Question about prototype declarations in JS and "standard practice"

There's a couple ways to define an "object" in JavaScript.

The following uses a standard function to declare a class and some attributes and an accessor:

function Car(make, model, year) {
    this.make  = make;
    this.model = model;
    this.year  = year;

    this.display = function() {
        return '' + this.year + ' ' + this.make + ' ' + this.model;
    }
}

car = new Car('2006', 'Hyundai', 'Elantra');
console.log('I drive a ' + car.display());

According to things I've read, and logic, this is actually slightly inefficient if you want to instantiate a whole fleet of cars. That's because the code declaring the accessor runs each and every time, which is both slowing you down (processing time) and bloating you up (taking up memory for each object with an anonymous function). The Better Way™ is to use prototype:

function Car(make, model, year) {
    this.make  = make;
    this.model = model;
    this.year  = year;
}

Car.prototype.display = function() {
    return '' + this.year + ' ' + this.make + ' ' + this.model;
}

car = new Car('2006', 'Hyundai', 'Elantra');
console.log('I drive a ' + car.display());

This is all well and good, except that I tend to prefer my methods to be all scoped inside of the "class" to which they belong. The question I have is, is this a common way to solve the prototyping problem, or is it frowned upon? It's not as inefficient as redeclaring the functions each time, but it's slightly less efficient than definitively declaring the functions only once:

function Car(make, model, year) {
    this.make  = make;
    this.model = model;
    this.year  = year;

    if (this.prototype.display) {
        this.prototype.display = function() {
            return '' + this.year + ' ' + this.make + ' ' + this.model;
        }
    }
}

car = new Car('2006', 'Hyundai', 'Elantra');
console.log('I drive a ' + car.display());

I'm actually not entirely sure if that'll work, as I'm unsure if this.prototype inside the declaration refers to the same thing as Car.prototype (I'd generally prefer to keep the class name written in as few places as possible) but I'm about to Try It And See.

function Car(make, model, year) {
var proto = Object.getPrototypeOf(this);
this.make = make;
this.model = model;
this.year = year;
proto.display = proto.display ||
function() {
return '' + this.year + ' ' + this.make + ' ' + this.model;
};
}
car = new Car('Hyundai', 'Elantra', '2006');
console.log('I drive a ' + car.display());
function Vehicle() {}
function Car(make, model, year) {
var proto = Object.getPrototypeOf(this);
this.make = make;
this.model = model;
this.year = year;
proto.display = proto.display ||
function() {
return '' + this.year + ' ' + this.make + ' ' + this.model;
};
}
Car.prototype = new Vehicle();
function FlyingCar(make, model, year) {}
FlyingCar.prototype = new Car();
truck = new Vehicle();
car = new Car('Hyundai', 'Elantra', '2006');
spacecar = new FlyingCar('Toyama', 'Astro', '2050');
if (truck.display) {
console.log('The truck is a ' + truck.display());
}
else {
console.log('Truck? There is no truck.');
try {
console.log('But if it were, it would be a ' + truck.display());
}
catch (e) {
console.log('Honest.');
console.log(e);
}
}
console.log('I drive a ' + car.display());
console.log('But I fly a ' + spacecar.display());
/* OUTPUT:
*
* Truck? There is no truck.
* Honest.
* > TypeError
* I drive a 2006 Hyundai Elantra
* But I fly a undefined undefined undefined
*/
function Vehicle() {}
function Car(make, model, year) {
var proto = Object.getPrototypeOf(this);
this.make = make;
this.model = model;
this.year = year;
proto.display = proto.display ||
function() {
return '' + this.year + ' ' + this.make + ' ' + this.model;
};
}
Car.prototype = Vehicle;
function FlyingCar(make, model, year) {
var proto = Object.getPrototypeOf(this);
proto.apply(this, arguments);
}
FlyingCar.prototype = Car;
truck = new Vehicle();
car = new Car('Hyundai', 'Elantra', '2006');
spacecar = new FlyingCar('Toyama', 'Astro', '2050');
if (truck.display) {
console.log('The truck is a ' + truck.display());
}
else {
console.log('Truck? There is no truck.');
try {
console.log('But if it were, it would be a ' + truck.display());
}
catch (e) {
console.log('Honest.');
console.log(e.message);
}
}
console.log('I drive a ' + car.display());
console.log('But I fly a ' + spacecar.display());
/* OUTPUT:
*
* Truck? There is no truck.
* Honest.
* Object #<Vehicle> has no method 'display'
* I drive a 2006 Hyundai Elantra
* But I fly a 2050 Toyama Astro
*/
@sycobuny
Copy link
Author

Best I can seem to come up with:

function Car(make, model, year) {
    var klass = Car;

    this.make  = make;
    this.model = model;
    this.year  = year;

    console.log(this);
    if (!klass.prototype.display) {
        klass.prototype.display = function() {
            return '' + this.year + ' ' + this.make +  ' ' +
                   this.model;
        };
    }
}

car = new Car('2006', 'Hyundai', 'Elantra');
console.log('I drive a ' + car.display());
``​`

@sycobuny
Copy link
Author

Best I can seem to come up with:

function Car(make, model, year) {
    var klass = Car;

    this.make  = make;
    this.model = model;
    this.year  = year;

    console.log(this);
    if (!klass.prototype.display) {
        klass.prototype.display = function() {
            return '' + this.year + ' ' + this.make +  ' ' +
                   this.model;
        };
    }
}

car = new Car('2006', 'Hyundai', 'Elantra');
console.log('I drive a ' + car.display());

@rintaun
Copy link

rintaun commented Nov 20, 2012

function Car(make, model, year) {
    this.make  = make;
    this.model = model;
    this.year  = year;

    if (!Object.getPrototypeOf(this).display) {
        Object.getPrototypeOf(this).display = function() {
            return '' + this.year + ' ' + this.make + ' ' + this.model;
        };
        console.log('CREATED DISPLAY');
    } else {
        console.log("DIDN'T CREATE DISPLAY");
    }
}

@sycobuny
Copy link
Author

function Car(make, model, year) {
    var proto = Object.getPrototypeOf(this);

    this.make  = make;
    this.model = model;
    this.year  = year;

    console.log(this);
    if (!proto.display) {
        proto.display = function() {
            return '' + this.year + ' ' + this.make +  ' ' + this.model;
        };
    }
}

My next question is whether there's anything similar to ||= in JS, cause the following did not work:

    proto.display ||= function() {
        return '' + this.year + ' ' + this.make +  ' ' + this.model;
    };

@rintaun
Copy link

rintaun commented Nov 20, 2012

This works for me:

function Vehicle() {}

function Car(make, model, year) {
    var proto = Object.getPrototypeOf(this);

    this.make  = make;
    this.model = model;
    this.year  = year;

    proto.display = proto.display ||
    function() {
        return '' + this.year + ' ' + this.make +  ' ' + this.model;
    };
}
Car.prototype = new Vehicle();

function FlyingCar(make, model, year) {}
FlyingCar.prototype = new Car();

var a = new FlyingCar('a','b','c');
a.display()

It outputs "undefined undefined undefined"

@sycobuny
Copy link
Author

Hey, lookit that - all my comments posted, days later. Weird.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment