2

I'm trying to make it possible to inherit from this class:

function Vehicle(p) {
  this.brand = p.brand || "";
    this.model = p.model || "";
    this.wheels = p.wheels || 0;
}

Vehicle.prototype.getBrand = function () {
    return this.brand;
};
Vehicle.prototype.getModel = function () {
    return this.model;
};
Vehicle.prototype.getWheels = function () {
    return this.wheels;
};

var myVehicle = new Vehicle({
    brand: "Mazda",
    model: "RX7",
    wheels: 4
});

console.log(myVehicle);

I tried doing it this way:

function Vehicle(p) {
    this.brand = p.brand || "";
    this.model = p.model || "";
    this.wheels = p.wheels || 0;
}

Vehicle.prototype.getBrand = function () {
    return this.brand;
};
Vehicle.prototype.getModel = function () {
    return this.model;
};
Vehicle.prototype.getWheels = function () {
    return this.wheels;
};

function Car (){}
Car.prototype = new Vehicle();
Car.prototype.getWheels = function() {
    return 4;
};

var myCar = new Car({
    brand: "Mazda",
    model: "RX7"
});

console.log(myCar);

but it seems like it doesn't work:

> Uncaught TypeError: Cannot read property 'brand' of undefined 

Could someone explain to me what's wrong? I guess it's not the write way to implement it but why?

Adonis K. Kakoulidis
  • 4,951
  • 6
  • 34
  • 43

2 Answers2

4

In addition to what @elclanrs said:

function Car () {
    Vehicle.apply(this, arguments);
}
var c = function() {};
c.prototype = Vehicle.prototype;
Car.prototype = new c();

Live demo: http://jsfiddle.net/x3K9b/1/

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • I tried your example and if you run `console.log(myCar.brand);`, it will return `undefined`. – Adonis K. Kakoulidis May 17 '13 at 02:46
  • @Adonis K.: that's because my answer is an addition to the elclanrs' one. Now fixed my code to be complete, check it now? – zerkms May 17 '13 at 02:47
  • Seems to be working, I guess I need to do some digging into super and prototype chaining. Thanks! – Adonis K. Kakoulidis May 17 '13 at 02:50
  • You should really use `Object.create`. There's a [polyfill](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create) for old browsers... – plalx May 17 '13 at 02:59
  • @plalx: I didn't know for it. Now I see it's a better solution – zerkms May 17 '13 at 03:07
  • @plalx The code in this answer basically *is* the polyfill for Object.create (except for the additional argument). – bfavaretto May 17 '13 at 03:40
  • 1
    @bfavaretto, I know that already, but here the OP might start re-writing this pattern over and over... and seriously, why not simply do it the proper way? There is simply no valid reason not to use `Object.create`. – plalx May 17 '13 at 03:45
  • @zerkms, `Object.create` takes a second argument that cannot be polyfilled. Have a look at the docs here https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create – plalx May 17 '13 at 03:49
3

You need to call "super" in Car:

function Car() {
  Vehicle.apply(this, arguments);
}

Aside from that you could make p optional by just assigning an empty object for example; that would get rid of the error. And finally point to the right constructor so:

function Vehicle(p) {
  p = p || {}; //<=
  this.brand = p.brand || "";
  this.model = p.model || "";
  this.wheels = p.wheels || 0;
}

//...

Car.prototype = new Vehicle();
Car.prototype.constructor = Car; //<=

Edit: Otherwise just use Object.create:

Car.prototype = Object.create(Vehicle.prototype);

That takes care of assigning the constructor and everything.

elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • 1
    The error is obviously caused by another line - `Car.prototype = new Vehicle();` – zerkms May 17 '13 at 02:40
  • @zerkms: That seems right, he should point to the right constructor though: `Car.prototype.constructor = Car`; – elclanrs May 17 '13 at 02:42
  • Check here: https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript. It's valid, hacky, but valid. You could use `Object.create(Vehicle.prototype)` – elclanrs May 17 '13 at 02:43
  • 1
    I meant - that's what OP asked. It should be an answer, not a comment – zerkms May 17 '13 at 02:44
  • Oh I see the problem now. OP should make `p` optional. – elclanrs May 17 '13 at 02:45
  • it's not a problem. If base a class requires a parameter to be required - it has to be required. Changing interface is not a solution – zerkms May 17 '13 at 02:45
  • Well, changing interface **IS NOT** a solution. Prototype based inheritance **DOESN'T REQUIRE** you to have all parameters optional. *Not an answer* – zerkms May 17 '13 at 02:48
  • p is required... The point is to create a car object with those parameters – Adonis K. Kakoulidis May 17 '13 at 02:48
  • Then `Object.create` should do. – elclanrs May 17 '13 at 02:51
  • @elclanrs [`Object.create`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create#Cross-browser_compatibility) can do the job but it's supported in IE9+. Thanks for taking the time to help me. I mostly want this so that I can understand how prototyping works. – Adonis K. Kakoulidis May 17 '13 at 03:01