Reputation: 4228
I'm learning the different inheritance implementations in javascript, mostly following the Javascript Patterns book by Stoyan Stefanov.
Now I was inspecting how Coffescript implements it. So given a parent and a child classes
or constructors:
class Animal
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."
class Snake extends Animal
move: ->
alert "Slithering..."
super 5
sam = new Snake "Sammy the Python"
sam.move()
They are compiled to:
var Animal, Horse, Snake, sam,
_extends = function(child, parent) {
for (var key in parent) {
if (_hasProp.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
},
_hasProp = {}.hasOwnProperty;
Animal = (function() {
function Animal(_name) {
this.name = _name;
}
Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};
return Animal;
})();
Snake = (function(_super) {
_extends(Snake, _super);
function Snake() {
return Snake.__super__.constructor.apply(this, arguments);
}
Snake.prototype.move = function() {
alert("Slithering...");
return Snake.__super__.move.call(this, 5);
};
return Snake;
})(Animal);
sam = new Snake("Sammy the Python");
sam.move();
As I've understood the implementation of the inheritance in coffescript result from the combination of different patterns:
In this case we we also reset the constructor pointer
and store the Superclass reference. What Stefanov defines 'Holy Grail'.
With this pattern the child only inherits properties of the prototype.
// the proxy function
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
With this pattern we simply copy the properties of one object into another
_hasProp = {}.hasOwnProperty;
for (var key in parent) {
if (_hasProp.call(parent, key)) child[key] = parent[key];
}
function Snake() {
return Snake.__super__.constructor.apply(this, arguments);
}
QUESTION:
_extends
function also be used between objects instead of Constructors? Thanks
Upvotes: 0
Views: 274
Reputation: 239250
- Isn't the rent-a-constructor creating a repetition of the inheritance? Properties copied and then parent constructor called again?
The properties being copied here...
for (var key in parent) {
if (_hasProp.call(parent, key)) child[key] = parent[key];
}
... are not prototypal properties, they are "class level" properties, methods defined on the function itself. It's copying properties from the function Animal
to the function Horse
.
The difference is:
class Animal
# Not part of prototype, part of Animal, must be copied
@build: (name) ->
new @(name)
constructor: (name) ->
@name = "An animal named #{name}"
# Part of prototype
sayName: ->
alert(@name)
class Bird extends Animal
constructor: (name) ->
@name = "A bird named #{name}"
# Both Animal and Bird have build because of the copying of properties:
a = Animal.build('sam') # an animal named sam
b = Bird.build('bob') # a bird named bob
Some annotation on the compiled JavaScript:
var Animal, Bird, a, b,
__extends = function(child, parent) {
for (var key in parent) {
# Copies Animal.build to Bird.build
if (__hasProp.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
# Makes sayName available to Bird via prototypal inheritance
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
},
__hasProp = {}.hasOwnProperty;
Animal = (function() {
Animal.build = function(name) {
return new this(name);
};
function Animal(name) {
# This still (theoretically) needs to be invoked, regardless of whether
# the properties are copied over, though it isn't invoked in this example
this.name = "An animal named " + name;
}
Animal.prototype.sayName = function() {
return alert(this.name);
};
return Animal;
})();
Bird = (function(_super) {
__extends(Bird, _super);
# There is no "Bird.build" defined here, it is copied from Animal
function Bird(name) {
this.name = "A bird named " + name;
}
# There is no "move" defined here, it is provided by our prototyep
return Bird;
})(Animal);
a = Animal.build('sam');
b = Bird.build('bob');
Regardless, the properties being copied and then "the parent constructor being called again" isn't really what would be happening.
The properties are not defined in the parent constructor, the parent constructor is just an executable blob of code that needs to run. It may not define any properties, or it might define a bunch of properties, but those properties are not going to be set by the prototype or by the _hasOwnProperty
loop.
Upvotes: 2