I have a class
function Man(){...}
Man.drinkBeer = function(){...}
I need to inherit SuperMan from Man. And I still want my Superman be able to drink some beer.
How can I do that?
Object.setPrototypeOf(SuperMan, Man);
This will set the internal __proto__ property of your derived function to be the base function.
Therefore, the derived function will inherit all properties from the base function.
Note that this affects the functions themselves, not their prototypes.
Yes, it's confusing.
No existing browser supports setPrototypeOf(); instead, you can use the non-standard (but working) alternative:
SuperMan.__proto__ = Man;
This is what CoffeeScript does for class inheritance:
var __hasProp = {}.hasOwnProperty,
__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;
};
And they use it like so:
var Man = (function(){
function Man() { ... }
...
return Man;
})();
....
var SuperMan = (function(_super){
__extends(SuperMan, _super);
function SuperMan() { ... }
...
return SuperMan;
})(Man);
....
Related
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:
1. The Classical proxy Constructor
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;
2. The Inheritance by Copying Properties
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];
}
3. Classical Pattern - Rent-a-Constructor (or Borrow a Constructor)
function Snake() {
return Snake.__super__.constructor.apply(this, arguments);
}
QUESTION:
Are my assumptions correct? Is coffescript compiler using 1+2+3?
The inheritance by copying seems to use a shallow copy, meaning that it is not inspecting to check if the property is an object/array and starting a recursion. Even tough the result seems a perfect deep copy (objects/arrays are copies, not references). Why/how?
Isn't the rent-a-constructor creating a repetition of the inheritance? Properties copied and then parent constructor called again?
Can the _extends function also be used between objects instead of Constructors?
Thanks
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.
I have found and adapted a JavaScript "class" extend function from coffeescript:
var extend = (function() {
var hasProp = Object.prototype.hasOwnProperty;
function ctor(child) {
this.constructor = child;
}
return function(child, parent) {
for (var key in parent) {
if (hasProp.call(parent, key)) {
child[key] = parent[key];
}
}
ctor.prototype = parent.prototype;
child.prototype = new ctor(child);
child.__super__ = parent.prototype;
// child.prototype.__super__ = parent.prototype; // better?
return child;
};
})();
I am wondering, if there is a reason why they used child.__super__ instead of child.prototype.__super__ (see out-commented code line).
I like the out-commented version more because:
You can access super properties via this.__super__.propertyName instead of ClassName.__super__.propertyName. So you have no redundancy in the class naming.
This makes even more sense for nested inheritance since you can use this.__super__.__super__.propertyName instead of ClassName.__super__.constructor.__super__.propertyName
I do not see any reason for it, but you could even still call "static" functions in a "static" way like that:
ClassName.prototype.__super__.constructor.staticMethod()
Are there any drawbacks with my version that I might have overlooked?
EDIT: I corrected the line to var hasProp = Object.prototype.hasOwnProperty;
Because you're not supposed to use __super__ in your code at all.
It's a compiler artifact, every use of the super macro/keyword/whatever it is will compile to
ClassName.__super__.methodName.call(this, …) // or
ClassName.__super__.methodName.apply(this, …)
// or, in static class functions even
ClassName.__super___.constructor.functionName.call(this, …)
They don't trust the dynamic this binding that you have proposed to use (this.__super__), they rather went for a static reference of the parent. Actually it might have been a good idea not to use a property at all, but just a local super variable in their module scope.
Also, this.__super__ will not work in an inherited method:
function A() { }
A.prototype.method = function() { console.log("works") };
function B() { A.call(this); }
B.prototype = Object.create(A.prototype);
B.prototype.__super__ = A.prototype;
B.prototype.method = function() { this.__super__.method.call(this); }
function C() { B.call(this); }
C.prototype = Object.create(B.prototype);
C.prototype.__super__ = B.prototype;
var b = new B(), c = new C();
b.method() // "works"
c.method() // Maximum recursion depth exceeded
Stack Overflow because you did not get the .__super__ that you expected!
I am working on a Node.js server and I am using coffee script to develop.
How does this work on coffee script?
EventEmitter = require('events').EventEmitter
util.inherits(Connector, EventEmitter)
Is it?
EventEmitter = require('events').EventEmitter
class #Connector extends EventEmitter
I am basically trying to add emit to Connector.
Something like:
this.emit('online')
Yes, extends does a similar thing as util.inherits.
Implementation of util.inherits:
inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
Compilation of extends:
var __hasProp = {}.hasOwnProperty,
__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;
};
function Connector() {
return Connector.__super__.constructor.apply(this, arguments);
}
__extends(Connector, EventEmitter);
The differences are:
The exact name of the super property on the child constructor
util.inherits uses Object.create while extends does use an ES3-compatible version
util.inhirits makes the constructor property of the child constructor non-enumerable
extend copies "static" properties of the parent constructor onto the child constructor
The extends keyword automatically calls the super constructor if no constructor is given for the class
I am implementing a JavaScript library that can also run on node, and I'd like to use node's API as much as possible. My objects emit events, so I found this nice library called eventemitter2, and which reimplements EventEmitter for JavaScript. Now I'd like to find the same for util.inherits. Has anybody heard about such a project ?
Have you tried using the Node.js implementation? (It uses Object.create, so it may or may not work on the browsers you care about). Here's the implementation, from https://github.com/joyent/node/blob/master/lib/util.js:
inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
Another method is used by CoffeeScript, which compiles
class Super
class Sub extends Super
to
var Sub, Super,
__hasProp = {}.hasOwnProperty,
__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; };
Super = (function() {
function Super() {}
return Super;
})();
Sub = (function(_super) {
__extends(Sub, _super);
function Sub() {
return Sub.__super__.constructor.apply(this, arguments);
}
return Sub;
})(Super);
You don't need to use any external library. Just use javascrit as is.
B inherits from A
B.prototype = Object.create (A.prototype);
B.prototype.constructor = B;
And inside the constructor of B:
A.call (this, params...);
If you know that javascript has a property named constructor, then avoid it, no need to hide or not enumerate it, avoid avoid avoid. No need to have a super property, simply use A.call. This is javascript, don't try to use it like any other language because you will miserably fail.
I'm currently in the process of converting a quite large actionscript library to work in a nodejs project of mine. While doing so I stumbled upon something that could be an issue: Building classes from classes.
Is there a way to use an object as the base for another object(IE: inherits all members from the base object, then overwrites same name members from the extending object)?
Right now this is what I'm doing, though it's getting a bit difficult to manage now that there are 3+ classes built one on top of another:
// The base object which others may extend
function A() {
this.a = "pie";
}
A.prototype.yum = function() {
return this.a + " is AWESOME!";
}
// The "extends A" object.
// Instead of creating an instance of "B", I current just create an instance of "A",
// then adding the members from "B" to it at which point I return the "A" instance.
function B() {
var a = new A();
a.b = "pie";
// Notice how I have to declare the overwriting function here instead of being able
// to drop it into B's prototype. The reason this bothers me is instead of just
// having one copy of the function(s) stored, each time a "new B" is created the
// function is duplicated... for 100s of "B" objects created, that seems like poor
// memory management
a.yum = function () {
return "I like " + this.a + " and " + this.b;
};
return a;
}
console.log((B()).yum());
Is it possible to do something along the following?
I know this isn't valid, but it gives the idea.
function A(){
this.a = "pie"
}
A.prototype.yum = function () {
return this.a + " is AWESOME!";
}
function B(){
// Throws an "illegal left hand assignment" Exception due to overwriting `this`;
this = new A();
this.b = "cake"
}
B.prototype.yum = function () {
return "I like "+this.a+" and "+this.b;
}
console.log((new B()).yum());
Notes:
1: I know javascript doesn't have classes; it uses objects and prototypes. Otherwise I wouldn't be asking.
2: This isn't the actual code im (trying) to convert; it's a generalized example
3: Please do not suggest a library. I know at times they are valuable, but I'd rather not have to maintain, depend on and include an entire library for the project.
ANSWER:
I know it's bad form to alter native member prototypes, but I think this merits it, due to the lack of possible functionality, and the size of it.
Object.prototype.extendsUpon = function (p) {
var h = Object.prototype.hasOwnProperty;
for(var k in p)if(h.call(p,k))this[k]=p[k];
function c(c){this.constructor=c;}
c.prototype = p.prototype;
this.prototype = new c(this);
this.__base__ = p.prototype;
}
function object_Constructor_built_ontop_of_another_constructor() {
this.extendsUpon(base_Object_to_built_atop_off);
this.__base__.constructor.apply(this, arguments);
// From here proceed as usual
/* To access members from the base object that have been over written,
* use "this.__base__.MEMBER.apply(this, arguments)" */
}
Very much possible. You can do it in multiple ways, the more complete is used in coffeescript:
var ClassBase, ClassTop,
__hasProp = {}.hasOwnProperty,
__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; };
ClassBase = (function() {
function ClassBase() {}
return ClassBase;
})();
ClassTop = (function(_super) {
__extends(ClassTop, _super);
function ClassTop() {
return ClassTop.__super__.constructor.apply(this, arguments);
}
return ClassTop;
})(ClassBase);
There is going to be some boilerplate code. ClassTop is inheriting everything from ClassBase. The classes don't have much inside them other then an __extend, a (function(_super... and some constructor boilerplate but it's fairly simple.
The inheritance is mostly managed by the __extends boilerplate that does some magic. The full __extends method is beautified here:
__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;
};
Again, much less scary then before. You're basically checking properties that the parent has and applying them to the child. More information can be found here: http://www.jimmycuadra.com/posts/coffeescript-classes-under-the-hood