In Douglas Crockford's "JavaScript: The Good Parts", he walks through three types of inheritance: classical, prototypal, and functional. In the part on functional inheritance he writes:
"The functional pattern also gives us a way to deal with super methods."
He then goes on to implement a method named "superior" on all Objects. However, in the way he uses the superior method, it just looks like he is copying the method on the super object for later use:
// crockford's code:
var coolcat = function(spec) {
var that = cat(spec),
super_get_name = that.superior('get_name');
that.get_name = function (n) {
return 'like ' + super_get_name() + ' baby';
};
return that;
};
The original get_name
method is copied to super_get_name
. I don't get what's so special about functional inheritance that makes this possible. Can't you do this with classical or prototypal inheritance? What's the difference between the code above and the code below:
var CoolCat = function(name) {
this.name = name;
}
CoolCat.prototype = new Cat();
CoolCat.prototype.super_get_name = CoolCat.prototype.get_name;
CoolCat.prototype.get_name = function (n) {
return 'like ' + this.super_get_name() + ' baby';
};
Doesn't this second example provide access to "super methods" too?
3 Answers 3
var coolcat = function(spec) {
var that = cat(spec),
super_get_name = that.superior('get_name');
that.get_name = function (n) {
return 'like ' + super_get_name() + ' baby';
};
return that;
};
var cat = coolcat({ ... });
Can just as well be done with prototypes
var CoolCat = {
constructor: function () {
Cat.constructor.apply(this, arguments);
return this;
},
getName: function () {
return 'like ' + Cat.getName() + ' baby';
}
};
var cat = Object.create(CoolCat).constructor({ ... });
Of course the main difference here is that your not creating two new functions every time you invoke coolcat
. coolcat
creates a new function for get_name
and a new function for super_get_name
and basically wastes memory left and right.
One may argue coolcat
has privacy but it does not because anyone can call cat({}).superior('get_name');
and get your "private" method.
Of course if one wants privacy and prototypes that's perfectly possible, using the klass
macro as shown in the article
var Cat = klass(function (privates) {
return {
constructor: function (name) {
privates(this).name = name;
},
getName: function () {
return privates(this).name;
}
};
});
var CoolCat = klass(Cat, function (privates, $super) {
return {
constructor: function () {
$super.constructor.apply(this, arguments);
},
getName: function () {
return 'like ' + $super.getName.call(this) + ' baby';
}
};
});
var cat = new CoolCat("someName");
console.log(cat.getName());
Here we achieve both privacy and super class inheritance in an "elegant" manner.
In this elegant means not having the closure overhead of multiple functions per object (we still add a single closure per object for the privates
function) and also allowing normal prototypical OO to just work.
-
In your klass gist, what is the "pd" object?kindohm– kindohm2012年01月20日 15:16:50 +00:00Commented Jan 20, 2012 at 15:16
-
Ah, found it in the longer article you linked to.kindohm– kindohm2012年01月20日 15:20:34 +00:00Commented Jan 20, 2012 at 15:20
-
@kindohm I've updated the gist to actually link to pd.Raynos– Raynos2012年01月20日 15:22:06 +00:00Commented Jan 20, 2012 at 15:22
-
in your snippet just below "Can just as well be done with prototypes", this assumes that Cat is a function, and not strictly an object, correct? Otherwise Cat.apply is undefined within CoolCat.constructor. Objects (not functions) do not have an apply() method, correct?kindohm– kindohm2012年01月20日 21:14:53 +00:00Commented Jan 20, 2012 at 21:14
-
@kindohm I made the mistake of of doing
Cat.apply
instead ofCat.constructor.apply
. Non-callable functions do not have a genericapply
methodRaynos– Raynos2012年01月20日 21:39:27 +00:00Commented Jan 20, 2012 at 21:39
It's a great question about a great book. The book takes a couple of readings to be fully digested. I came across this question, while researching some subtleties of javascript based on the book. I've basically had the same question, but after some pondering, I think that both kindohm's own and Raynos answers are incomplete.
I think kindohm is right that the reason is related to privacy and access, but it is not because the super method is private with the functional approach, as Raynos rightly points out.
On the other hand Raynos's answer sounds almost like there is a mistake in the book, as Raynos offers a different solution than the one that the book gives.
But I think that the solution given in the book still has its place. I think that the main reason/justification for it is in the paragraph preceding the code for the 'superior' method:
The function will invoke the original method even if the property is changed
This is the difference between the functional approach and the pseudo-classical approach that is given as an example in the original question.
It is not a perfect privacy but it gives an additional layer of isolation.
The answer is public versus private. With the functional approach, super methods can be kept private to the object. The second example (using prototype) exposes the super method publicly.
-
1It's perfectly fine to post your own answer on your question, as a tip give it a few more hours next time. You answered almost immediately, and that may be perceived as trying to rep up a sockpuppet account. So next time, just wait a few hours, even a couple of days, and possibly give a longer answer. Nothing wrong with this answer, as your network profile shows that you have been contributing a lot on StackOverflow, obviously not a sockpuppet, but it was the first thing that came to mind when I saw your question, so I thought I'd share.yannis– yannis2012年01月20日 06:38:50 +00:00Commented Jan 20, 2012 at 6:38
-
You don't need the functional approach to do privacy.Raynos– Raynos2012年01月20日 07:32:22 +00:00Commented Jan 20, 2012 at 7:32