Skip to content

Instantly share code, notes, and snippets.

@thomd
Last active April 1, 2017 23:20
Show Gist options
  • Save thomd/119e893001bdd85367b556bd14932d0c to your computer and use it in GitHub Desktop.
Save thomd/119e893001bdd85367b556bd14932d0c to your computer and use it in GitHub Desktop.
Pitfalls on Prototypal Inheritance with JavaScript
//
// Circle.prototype = Object.create(Shape.prototype)
// Circle.prototype.constructor = Circle
//
// shape constructor (parent)
var Shape = function(x = 0, y = 0) {
this.x = x
this.y = y
}
Shape.prototype = {
move: function(x, y) {
this.x += x
this.y += y
},
position: function() {
return [this.x, this.y]
}
}
// circle constructor (child)
var Circle = function(r) {
Shape.call(this)
this.r = r
}
// set for Circle.prototype an object which has a __proto__ to Shape.prototype in order to inherit only it's methods
Circle.prototype = Object.create(Shape.prototype)
Circle.prototype.area = function() {
return Math.PI * this.r * this.r
}
Circle.prototype.constructor = Circle
// rectangle constructor (child)
var Rectangle = function(w, h) {
Shape.call(this)
this.w = w
this.h = h
}
// set for Rectangle.prototype an object which has a __proto__ to Shape.prototype in order to inherit only it's methods
Rectangle.prototype = Object.create(Shape.prototype)
Rectangle.prototype.area = function() {
return this.w * this.h
}
Rectangle.prototype.constructor = Rectangle
// now some instances:
var circle = new Circle(2)
circle.move(2, 2)
console.log(circle.constructor.name) // Circle
console.log(circle.position()) // [2, 2]
console.log(circle.area()) // 12,566...
var rectangle = new Rectangle(2, 3)
rectangle.move(2, 2)
console.log(rectangle.constructor.name) // Circle
console.log(rectangle.position()) // [2, 2]
console.log(rectangle.area()) // 6
//
// Circle.prototype = Shape.prototype
// Circle.prototype.constructor = Circle
//
// shape constructor (parent)
var Shape = function(x = 0, y = 0) {
this.x = x
this.y = y
}
Shape.prototype = {
move: function(x, y) {
this.x += x
this.y += y
},
position: function() {
return [this.x, this.y]
}
}
// circle constructor (child)
var Circle = function(r) {
Shape.call(this)
this.r = r
}
// reference Circle.prototype to Shape.prototype, so Circle will have Shape's methods, but when creating new methods for
// Circle, they will be created in fact at Shape - not Circle. This can yield to issues when sub-classing further
// objects from Shape
Circle.prototype = Shape.prototype
Circle.prototype.area = function() {
return Math.PI * this.r * this.r
}
Circle.prototype.constructor = Circle
// rectangle constructor (child)
var Rectangle = function(w, h) {
Shape.call(this)
this.w = w
this.h = h
}
// reference Rectangle.prototype to Shape.prototype, so Circle will have Shape's methods, but when creating new methods for
// Circle, they will be created in fact at Shape - not Circle. This can yield to issues when sub-classing further
// objects from Shape
Rectangle.prototype = Shape.prototype
// This will now overwrite the 'area' method from Circle, as this method is in fact set at Shape
Rectangle.prototype.area = function() {
return this.w * this.h
}
Rectangle.prototype.constructor = Rectangle
// now some instances:
var circle = new Circle(2)
circle.move(2, 2)
console.log(circle.constructor.name) // Circle
console.log(circle.position()) // [2, 2]
console.log(circle.area()) // NaN <- area method was overwritten by second sub-classing
var rectangle = new Rectangle(2, 3)
rectangle.move(2, 2)
console.log(rectangle.constructor.name) // Circle
console.log(rectangle.position()) // [2, 2]
console.log(rectangle.area()) // 6
//
// Circle.prototype = new Shape()
// Circle.prototype.constructor = Circle
//
// shape constructor (parent)
var Shape = function() {
this.abstract = true
}
Shape.prototype = {
isAbstract: function() {
return this.abstract ? true : false
}
}
// circle constructor (child)
var Circle = function(r) {
this.r = r
}
// set for Circle.prototype an instance of Shape. Here you will also inherit the property 'abstract' of 'Shape' which
// should not and will yield a wrong result when calling 'circle.isAbstract'
Circle.prototype = new Shape()
Circle.prototype.area = function() {
return Math.PI * this.r * this.r
}
Circle.prototype.constructor = Circle
// rectangle constructor (child)
var Rectangle = function(w, h) {
Shape.call(this)
this.w = w
this.h = h
}
Rectangle.prototype = new Shape()
Rectangle.prototype.area = function() {
return this.w * this.h
}
Rectangle.prototype.constructor = Rectangle
// now some instances:
var circle = new Circle(2)
circle.move(2, 2)
console.log(circle.constructor.name) // Circle
console.log(circle.area()) // 12,566...
console.log(circle.isAbstract()) // true <- should be 'false'
var rectangle = new Rectangle(2, 3)
rectangle.move(2, 2)
console.log(rectangle.constructor.name) // Circle
console.log(rectangle.area()) // 6
console.log(rectangle.isAbstract()) // true <- should be 'false'
//
// Circle.prototype = Object.create(Shape.prototype)
//
// shape constructor (parent)
var Shape = function() {}
Shape.prototype = {}
// circle constructor (child)
var Circle = function(r) {
this.r = r
}
Circle.prototype = Object.create(Shape.prototype)
Circle.prototype.area = function() {
return Math.PI * this.r * this.r
}
// now an instance:
var circle = new Circle(2)
console.log(circle.constructor.name) // Object <- should be 'Circle'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment