In my precedent post I have discussed about few common JavaScript errors, included the classic new Class() call to retrieve a runtime created function, populating its prototype.
While my suggestion is both semantic and reasonable, I am pretty sure nobody on earth will ever implement that native Function wrapper, and this is the reason I am posting again, this time hopefully with a better suggestion.
JavaScript Class Abstract Concept
It's extremely hard to understand for those with classic OOP background but JavaScript has no Classes, just objects, where functions are first class objects but still objects!Assumed this, we can try in any case to think about a JavaScript Class as a "function which aim is to create instances of function itself".
What Is Wrong Right Now
What I argued about in my precedent post is that this statement is too often unconsidered:
// try with Mootools or some other library
new Class({}) instanceof Class;
// FALSE, since Class returns a function
// and not the created instanceof Class
Above misconception about what a Class is and how meaningless and redundant is the new keyword in latter case is truly annoying me!
The reason developers want "Classes" in JavaScript is because they think their OOP knowledge is better than what JS already offers, so why on earth they should create and use such hybrid nonsense?
A Class IS A Function
Nothing more and nothing less. The fact we would like to use that function to create and initialize instances is just how every Functions work, or if we prefer, a developer convention and a quick way to chain a prototype into an object.Since a Class is a Function, and whatever "new Class()" will always be an "instanceof Function", Class.prototype should be exactly the Function.prototype one, so that nobody can ever say again: "Look, I have created a Class" ... no bullshit Sherlock, you created a Function!
How To Define A "Class"
If we think about classical OOP languages, the only way to use a Class is to define it indeed. In few words, if the plan is to use a new Class() approach, how about a mandatory Class definition Object ?
// the most basic Class implementation
function Class(
// Class definition object: mandatory
__proto__
) {
// define the "Class"
// if the definition object has a constructor property
var Class = __proto__.hasOwnProperty("constructor") ?
// use it ...
__proto__.constructor :
// otherwise create one and assign it
(__proto__.constructor = function () {})
;
Class.prototype = __proto__;
return Class;
}
// we already said that a Class is simply a function ...
Class.prototype = Function.prototype;
Above code is basically all we need to start using new Class(definition) approach:
// class creation via definition object
var Person = new Class({
// this is our Person constructor
constructor: function (name) {
if (name) {
this.setName(name);
}
},
// this is a public property
name: "anonymous",
// this is a public method
setName: function (name) {
this.name = name;
}
});
Person instanceof Class; // true
Person instanceof Function; // ... as well
var dunno = new Person;
dunno instanceof Person; // true
dunno.name; // anonymous
dunno.constructor === Person; // true
I think this approach is as elegant as simple, but there is still a lot to consider about it.
A constructor IS An Implicit Init Method
I don't really know "when" it started and I am not sure about "why", but lots of libraries use this extra call to init/initialize/initializator/WTinitF method. If we think about this approach, it's like this:
// the JavaScript way
var a = [];
// the init way
var a = [];
a.init();
Now, think that for every created instance, rather than simply use the constructor for what it is, a method able to initialize an instance as is for basically every OOP language, we need to access to the instance chained prototype to execute a second init ... not sure what you think about it, but this sounds a bit redundant to me ...
A Class constructor is already able to initialize an instance, that is simply why I have decided to be semantic and use just the constructor method as is rather than an extra init call.
A constructor Must Be Unique
The definition approach is something good in any case, but above implementation does not consider one of the best features in JavaScript: the context injection.If we would like to reuse common methods, as example for a mixin or a hybrid prototype, initial suggestion is not enough.
The moment we use the same constructor to initialize a variable, we are already stuck. Even worst, we cannot reuse the same definition object to partially define one or more classes with common methods or initialization.
How to solve this? We need to chain the definition object in order to be able to extend the created Class.prototype for everything we need.
// chained Class definition example
var Class = (function () {
// the public Class function
function Class(__proto__) {
// the created "Class"
function Class() {
// initialize the instance via original constructor
__proto__.constructor.apply(this, arguments);
}
// assign the prototype ...
Chain.prototype = __proto__ || (__proto__ = {});
// ... and create the chain, assigning the right constructor
Class.prototype = new Chain(Class);
return Class;
}
// runtime __proto__ chain with right constructor assignment
function Chain(Class) {
this.constructor = Class;
}
Class.prototype = Function.prototype;
return Class;
})();
We are still far away from a proper solution, at least we can now reuse a generic function to initialize one or more instances and we can reuse definition objects
var commonDef = {
constructor: function (name) {
if (name) {
this.name = name;
}
},
name: "unknown"
};
var Named = new Class(commonDef);
// this operation won't change commonDef object
Named.setName = function (name) {
this.name = name;
};
var Person = new Class(Named.prototype);
// this operation wont change Named.prototype
Person.prototype.getName = function () {
return this.name;
};
Thanks to prototype chain we are now able to extend directly another prototype, so that new Person, as example, will be instanceof Named too.
Don't Mix Class Definitions And Prototypes
While it could look good, the latter example has truly a bad design.A Class definition should be a concept a part. If we use another Class.prototype to define a new Class we are mixing definitions with "defined" concept.
To extend or inherit from another object there must be a specific method.
To better explain myself, let's consider this simple PHP example:
class A {
// here we define the class
public function doStuff() {
}
}
class B extends A {
// we are extending another class
// but we are still defining here class B structure
public function __construct() {
}
}
If we pass directly a Class.prototype as definition, we are basically doing this, rather than what I have already showed:
class B extends A {}
Can we see the difference? B extends A and nothing else, there is no definition for B, everything is just like A.
OK, in JavaScript we can add and remove properties and methods from a prototype or a single instance in whatever moment, but aren't we trying to emulate somehow classic OOP paradigm? So let's agree that if we like this approach, prototypes must be untouchable after Class definition, and defined prototypes mustn't be used as Class definition ... do we agree?
Function.prototype.extend FAIL
I am the first one in error, I have tried to defined dunno how many times a proper Function.prototype.extend in order to make every function extensible.The problem, specially in this case/approach, is that functions are functions, while Classes are Classes. If we make a generic "extend" method available for any kind of function, even those never used to create instances, we are simply polluting without a valid reason a shared prototype: shared between classes and functions.
Moreover, the fact a Class is extending another one should be instantly recognizable, and not something that could happen in the middle of a generic session, do you agree?
In few words, some common approach is not truly readable/reasonable:
function B() {
// something happens here
};
// lazy extend, could happen everywhere
// even with functions such Math.max, kinda nonsense
B.extend(A);
B.prototype = {...};
// new Class pattern
var C = new Class({
// long definition object
})
// we need to scroll till the end to understand
// if C.prototype is chaining another one
.extend(B)
;
If we compare again with precedent PHP snippet, or every other OOP language where extends is basically the first thing we can understand about a class, I think nobody should ever use above pattern in JavaScript: it's cool, it's dynamic ... but it's absolutely wrong as well if we are trying to bring classic OOP approach into this language!
Only Explicit Extend Via Definition Object
At least for this topic, most common libraries are already implementing what I am talking about, which is something like this:
var Man = new Class({
// first thing ever into definition object
extend:Human,
// optional constructor ir simply
// the rest of the definition
gender:"male"
});
The fact extend must be explicit is simple: nobody should be able to pollute Object.prototype adding an extend method/property, if we recycle objects we don't want to extend automatically any inherited extend from internal chains.
Public Statics
Almost the last piece of the puzzle, the ability to add via definition public static properties or methods.This could be another special property name, used like this:
var File = new Class({
statics:{
exists:function (fileName) {
// some cool code here
return found;
}
},
constructor: function (fileName) {
this.name = fileName;
},
exists: function () {
return File.exists(this.name);
}
});
var fileName = "index.html";
var index = new File(fileName);
// it should be always true
File.exists(fileName) === index.exists();
Class.definition
I am trying to keep writing semantic code so I guess the last piece ever to add into the run-time created class is simply one: the definition object.For both introspection and tricky code, I think it's a good idea to be able to retrieve the definition object. It could be reused, it could be analyzed, there are several things that could let us think: how to get back that object?
Well, since the extremely useful __proto__ property is not standard, the only way to retrieve that object is to associate directly to the Class, being its definition, and via definition property name. Is there anything more semantic and logic than this?
var def = {what:"ever"};
var MyClass = new Class(def);
MyClass.definition === def; // true
Now we can say we have everything!
this._super bullshit
Don't get me wrong, I do appreciate the effort every framework is putting in order to have a nice way to retrieve a super method, if any, but what we need to understand is that:- _super, parent, whatever it is, requires to be portable for each parent "level", not just one
- to make first point possible, _super requires a lot of initial parsing, wrapping, and runtime substitutions, an inevitable performances bottleneck, as showed in my old tests
- wrappers are anonymous and all equals, almost impossible to debug
- wrappers could fail, causing disasters if the "super" does not fallback into the original one for that level
- try catch for super are performances killers, at the same time the best way to avoid disasters
- wrappers could cause infinite loops, if super is not handled properly
- super/parent is not an instance related property!!!
In classical OOP the super/parent keyword is related to the current method, it is never an instance property.
When we need "super" we are talking about the super definition of the overridden method, if any, or another one up to the initial ring of the prototypal chain.
In few words, super has nothing to do with the instance, but for JavaScript nature, the simplest way to change runtime its reference, is to attach it to the instance.
Last, but not least, I have personally encountered problems, dunno how many times, with a whole stack or super wrappers without being able to understand which method was failing and where!
I am pretty sure that developers perfectly know what I am talking about, so while this._super can be "cool", have we never thought how much better explicit injections could be?
var A = new Class({
doStuff: function () {
this.where = "A";
}
});
var B = new Class({
extend:A,
doStuff: function () {
// zero ambiguity, zero wrappers
// best performances + fast debug
A.prototype.doStuff.call(this);
// if some error occurs here
// we know where it did, rather than a wrapper
this.where += "B";
}
});
Furthermore, while in classic OOP super/parent is usable for properties as well, I have never seen a proper implemntation for this in any framework.
As summary, while "super" or "parent" is something truly natural for classic OOP developers, there is no easy way to implement it properly in JavaScript, specially because if the same function can be used as method for 3 different classes, as example, the keyword super there will completely lose its meaning, basically the reason the only way to implement it, is to attach, substitute, and execute, runtime, via instances, rather than proper methods definition lookups. This is why my Class implementation does not provide such problematic "solution" for lazy developers.
WebReflection Class
var Class = (function () {
/*!
* JavaScript meaningfull Classic OOP Class Factory
* @author Andrea Giammarchi
* @license Mit Style
*/
/**
* Public exposed Class function
* @param Object the Class definition
*/
function Class(definition) {
// create the function via named declaration
function Class() {}
// find out if this is an extend
var $extend = hasOwnProperty.call(definition, "extend");
// temporary shortcut for inherited statics
var $;
// reassign the Class if there is a constructor ...
if (hasOwnProperty.call(definition, "constructor")) {
// wrapping it for faster execution
Class = constructor(definition.constructor);
}
// assign inherited public static properties/methods, if defined in the extend definition
if (
$extend &&
hasOwnProperty.call($ = definition.extend, "definition") &&
hasOwnProperty.call($ = $.definition, "statics")
) {
extend.call(Class, $.statics);
}
// assign public static properties/methods, if defined
// eventually overwrite inherited statics
if (hasOwnProperty.call(definition, "statics")) {
extend.call(Class, definition.statics);
}
// assign the prototype accordingly with extend
($extend ?
// chain the prototype extending it with the definition object
extend.call(Class.prototype = create(definition.extend.prototype), definition) :
Class.prototype = create(definition)
)
// be sure the constructor is the right one
.constructor = Class
;
// static public definition
Class.definition = definition;
// return the created class
return Class;
}
// wrap the constructor via closure
function constructor(constructor) {
// creating a named declared Class function
function Class() {
// return in any case for dual behaviors (factories)
return constructor.apply(this, arguments);
}
return Class;
}
// extend a gneric context via __proto__ object
function extend(__proto__) {
for (var key in __proto__) {
if (hasOwnProperty.call(__proto__, key)) {
this[key] = __proto__[key];
}
}
return this;
}
// trap the original Object.prototype.hasOwnProperty function
// as shortcut and hoping nobody changed it before this file inclusion ...
var hasOwnProperty = Object.prototype.hasOwnProperty;
// quick/fast Object.create emulator, if not in ES5
var create = Object.create || (function () {
function Object() {}
return function (__proto__) {
Object.prototype = __proto__;
return new Object;
};
})();
//* optional standard "for in" for Internet Explorer
// it could be removed if we don't define "magic mathods" in definition objects
// Internet Explorer does not enumerate properties/methods
// with name present in the Object.prototype
if (!({toString:null}).propertyIsEnumerable("toString")) {
// if this happens, to make the extend consistent
// we need to force Object.prototype names
extend = (function ($extend) {
function extend(__proto__) {
for (var i = length, key; i--;) {
if (hasOwnProperty.call(__proto__, key = split[i])) {
this[key] = __proto__[key];
}
}
// execute the original extend in any case for other properties/methods
return $extend.call(this, __proto__);
}
// constructor is not in the list since there is a re-assignment in any case
var split = "hasOwnProperty.isPrototypeOf.propertyIsEnumerable.toLocaleString.toString.valueOf".split(".");
var length = split.length;
return extend;
})(extend);
}
//*/
// a Class is a Function and nothing else
Class.prototype = Function.prototype;
return Class;
})();
A simple test case:
var A = // base class
new Class({
constructor: function () {
alert("A");
},
toString: 123
})
;
var B = // extended class
new Class({
extend:A,
constructor: function () {
A.call(this);
alert("B");
},
toString: 4
})
;
var b = new B;
// 4, true, true, true in every browser
alert([b.toString, b instanceof A, b instanceof B, b.constructor === B]);
Personal Thoughts
What I don't like is that extend, as statics, become part of the prototype as well. Unfortunately I decided that these words should be kinda reserved in order to speed up Classes creation.At the same time, Classes creation is something performed once and never again, so I may decide to change in the future the way I define the prototype, simply looping via "for in" and attaching properties. This could cause some sort of problem with truly inherited stuff, so right now I guess this is it.
29 comments:
Very good post! I really like your ideas. Especially interesting is your use of `constructor` and `extend`.
---
I understand your point of view about super/parent, but I must disagree.
IMHO, hard-coding references to specific parent class methods inside your subclass methods is an anti-pattern. It's much more flexible to dynamically reference the parent method.
Debugging wrapped methods is a solved issue (for me at least). c.f. dynaTrace ajax, WebKit Inspector, FireBug.
Performance isn't as big an issue as you imply.
If you need insane performance (e.g. a CSS Selector engine) then you should be writing all your code as high-perf as possible. That means avoiding any form of instantiation and drastically limiting function calls and other normal stuff. This is rarely necessary or helpful.
If you need a normal to extremely-high level of performance (e.g. a DOM FX engine), the bottle neck isn't in function calls or method wrapping. The bottle neck is the DOM. Reducing function calls and method wrapping isn't going to increase your FX frames-per-second. Optimizing your DOM calls and timing is the solution.
---
I'm very eagerly awaiting your next post! Thanks!
Thanks, I have updated the post showing the image of my old tests.
Each wrapped method could cost 2 up to 3 (or more) times the hard-coded solution.
Firebug and others cannot help that much inside scoped private vars and it's not that easy to follow the lookup, everything is anonymous with the same structure.
But the most important thing here is ambiguity.
JavaScript functions can be executed with every kind of context via call.
A method/function that contains a this.parent is not portable, reusable, recyclable, etc etc, because the object could be simply a basic one, or it could come from another framework/library and we are blocked because we have no idea how that library created that instance.
Super/parent is both not portable, slower, and somehow dangerous, because you have no control over run-time injections.
Via "hard coded" calls, where hard coded to me means more readable methods, we have even more power and flexibility.
Super, with long extended hierarchies, is something to avoid, because while it's directly exposed in classic OOP, it's just a magic trick implemented in the wrong place, the current injected instance, and side effects are in my opinion able to create less robust structures, non sharable/portable methods (mixins cannot contain "super", so limited mixin) and less readability.
I agree we disagree, but I'll hardly change my opinion about super ;)
fwiw, MooTools has a new Class implementation since your original graphs were made. Somewhere around 1.2.3 iirc.
But even so, 52K operations per second is ~0.019 milliseconds and 10.8M ops/sec is roughly ~0.00009 ms. In the real world that is never the bottleneck.
One real-world example is MooTools' FX engine. It's very smooth in all browsers. It is without question high performance. Yet it uses MooTools classes, subclasses, method wrapping, dynamic super, etc…
The performance would not change one bit if we hard-coded all of it. That simply is not a bottleneck.
However, MooTools' new selector engine Slick is one of the very few projects where these miniscule performance gains are actually noticed. We're mostly focused on spec and accuracy (see SlickSpeed), but our tertiary focus has been on performance. Not only do we not wrap methods in Slick, we avoid as many function calls as possible.
We benchmark almost every major choice we make to ensure we're using the least-slow possible option. Because it actually matters in this one very very rare instance.
But how many people are writing their own selector engines?
How many people even need 60 ops/sec in their real-world code?
So my point is this: Dynamic super implemented with method wrapping is more than fast enough.
But if you NEED to do something insanely fast, you shouldn't be using ANY class implementation at all.
Note: I'm not trying to convert you to the holy church of the divine method wrapping or anything. I'm just trying to shed a little light on why we chose to do what we do ;)
Thanks for allowing comments!
The performance would not change one bit if we hard-coded all of it. That simply is not a bottleneck.
I would truly like to see a proper benchmark over a MooTools super into 15 subclasses (a single method is enough) against proper pointers, and adding few subclasses without that method in the middle to jump over the chain.
After this, performances are just a marginal aspect, the fact _super is wrongly attached runtime to the instance, rather than current overridden method, is conceptually wrong.
The fact _super strongly depend on the current wrap means that we cannot reuse methods across different non related classes so mixins needs to be redefined and the total amount of functions, their double execution for each call (wrap/function) possible errors in the super and possible inconsistencies if something wrong happens plus the missed possibility to avoid wrappers, are something I do not like that much.
Finally, you can't spot performances problems over powerful PC, try differences with Atom based devices, as I do, and/or smart phones. In these cases performances matters, and if we gain 0.01 but the logic, reusability, and RAM, is under control, I'll never prefer the other way round.
Hope you at least got my points, and thank you for these useful comments.
Regards
This seems great, thanks for the post.
I'm trying to use it, but it seems that statics methods are not inherited.
Can you confirm it ?
Thanks.
Coulnd't `super` be a property on the class that points directly to the extended Object prototype?
For example:
var B = new Class({
extend: A,
doStuff: function() {
this.super.doStuff();
//do more stuff...
}
});
Your extend method could like so:
var extend = function(proto) {
for(var key in proto) {
if(proto.hasOwnProperty(key) {
this[key] = proto[key];
}
}
this.super = proto.prototype; // or just proto.
};
This way, you can still use an OOP term like super, you don't have any performance loss, no wrapping. The benefit is that you don't have to reference A several times in your class. Then, if you ever need to change the extended class from A to C, all of your `this.super` calls still reference the extended object.
Very interesting post!
An idea to allow a parent method that doesn't require wrapping to set it and unset it would be to define a function that dynamically grabs the parent.
Bravo Andrea,
For quite a while I've been bothered by Class implementations that break instanceof. Being able to confirm an object's type is IMO more than useful and fits naturally with the whole concept of encapsulating reusable data/logic within a Class.
Thanks for presenting such a detailed case for a more accurate Class implementation. I hope this post can be summarized and posted on Ajaxian. There'll probably be strong opinions but I believe anyone who's lived in both the JavaScript and OOP worlds will have to respect your case.
Keep up the thought provoking posts ;-)
Thanks Andrea.
Why use
hasOwnProperty.call(definition, "constructor")
instead of
definition.hasOwnProperty("constructor")
?
alcou, statics are not instances related, but class related.
If you define a static variable, this will be attached to the Class, not into the prototype ;)
seanmonstar ... that is the perfect case where you cannot go UP more than a prototype, since that instance will point to the same proto when executed in the super method. This will cause an infinite loop or something not expected, that is why I have already said things are not that simple and trust me, I have tried for years!!!
Thanks {Michael : iSkitz} not sure Ajaxian is the right place for this stuff developers think is already over ... I'll think about it!
Aaron, the reason is described there, I always prefer to trap Object.prototype since some library could always decide to override some method for whatever reason.
Moreover, trapping the method in a local scope speeds up its execution ;)
CrypticSwarm that is the same case where you cannot go up more than a level.
this.constructor.definition.extend
this, and constructor, will be always the same in the parent execution so that there you cannot execute the parent.
this, and parent, are problematic topic not easy to solve, simply slower, not portable, not scalable, and problematic ;)
Nice post!
Unless I've read your code wrong, the constructors/classes will still make a double function call every time a new instance is created? How is that different from "the init way"?
I also have to agree with SubtleGradient about hard-coding references. What if you decided to rename "A", it could lead to a lot of refactoring?
As discussed before, would something like the following not be simpler... direct call to the constructor, easy access to parent methods, and no iteration required (avoid IE bug):
http://mootools.net/shell/hPGkj/
It would be good to get your thoughts? :)
reiss, since the constructor must be unique, the one sent via definition is already a method used to initialize instances.
It is trapped via closure and the point is that there won't be any exposed "init" method since the only place where instances are initialized IS the constructor. I should have explained this part a bit better.
About the name, a basic RegExp as \bA\b is for the whole project, or better, just the "classes/definition folder" is not a big deal, while to find out who was referencing that method via this._super in long chains could be a disaster prone change.
What if a method changes and it is mixed in with other subclasses? Who is pointing to that method via this._super? Which subclass will be affected?
As I can imagine, that is a problem, while hard coded methods are never ambiguous ;)
Andrea, i agrea that statics are related to the class, maybe i miss something, but i give you a simple example to explain my problem :
var Vehicle = new Class({
constructor : function() {},
statics : {
getNbInstance: function() {}
}
});
var Car = new Class({
extend: Vehicle,
constructor : function() {}
});
if i look in the Car class object, i don't have my static method getNbInstance. In classic OOP programming, the static method getNbInstance would be inherited by class Car.
alcou, you've got a valid point, I'll update the code asap to inherit statics as well.
Thanks, Regards
Andrea, thanks for your reply.
I continue to test your solution. Everything is fine, except that i don't find a solution to have "private" (meaning like Crockford private) instance variable/method (private shared are ok with a closure).
alcou I have fixed the statics inheritance problem, please confirm it.
Non shared private are utopia in JavaScript so you will rarely find a way to define them, unless these are not attached runtime for each method execution, but in this case, we'll have still performances problems.
"protected" properties/methods best suggestion is till at least one underscore in front the property/method name (_doStuff rather than doStuff)
Regards
@alcou:
You could still do this like you would normally in the constructor. If you wanted the sugar of being able to define them like you would the rest of the Class then it would require method wrapping.
I made a quick working example of one way that this sugar could be incorporated into Andrea's class implementation.
You will notice at the bottom that you define a 'privates' key in your definition that is an object with all the private instance variables, and also a 'privledged' in the definition that is an object with all the public methods that should have access to the instance private variables.
Since the definition of the privledged ones aren't defined in the same scope as local variables they need to be wrapped to have access to the private variables. I choose to pass the privates as the first argument of any privledged function rather than setting privates on 'this' and unsetting after (would probably have more of a performance penalty when using with large objects. Also when passing as an argument the privates could potentially be used and modified in a timeout where the setting and unsetting method would have unset by the time the timeout was run.)
Andrea, I'm not sure about the double constructor calls, they could be avoided and still not expose "init/constructor". They also seem to go against your own rule by being ambiguous e.g.
alert(A);
would return:
function Class() {
// return in any case for dual behaviors (factories)
return constructor.apply(this, arguments);
}
I'm still not convinced by hard-coding references, even with find and replace I think there could be problems.
Btw.. I've updated the class performance tests with your latest version:
http://dl.dropbox.com/u/4262108/inheritance/index.html
I think you've introduced a lot of code that could have been avoided, which would help with performance, but it's still a nice Class abstraction :)
@Andrea :
It's ok, statics are inherited now. Thanks for you reply.
@CrypticSwarm :
Clever idea, but it's not working.
First i've to replace self[p] = function() with a closure.
But the biggest problem is that if Contact inherits from Person. var c = new Contact() is not instance of Person.
I don't know if it's possible to make it works.
But thanks a lot for the idea !
@alcou
Whoops! you are correct on both accounts.
The instanceof problem was because the prototype property was on the old constructor, and not the new one. Moving my code above setting of the prototype fixes that.
This probably works a bit better
Why would anyone try to simulate classes in JavaScript?
--DBJ
* ... "super" misconception.
In classical OOP the super/parent keyword is related to the current method, it is never an instance property.
... super has nothing to do with the instance, but for JavaScript nature, the simplest way to change runtime its reference, is to attach it to the instance.*
OK then, smart guy, explain this:
From http://java.sun.com/docs/books/tutorial/java/IandI/super.html:
Consider this class, Superclass:
public class Superclass {
public void printMethod() {
System.out.println("Printed in Superclass.");
}
}
Here is a subclass, called Subclass, that overrides printMethod():
public class Subclass extends Superclass {
public void printMethod() { //overrides printMethod in Superclass
super.printMethod();
System.out.println("Printed in Subclass");
}
public static void main(String[] args) {
Subclass s = new Subclass();
s.printMethod();
}
@dkaye ... and with that code you told us what, exactly?
Sorry. I spaced.
I had in mind two things.
First, your statement, quote - In classical OOP the super/parent keyword is related to the current method, it is never an instance property. When we need "super" we are talking about the super definition of the overridden method, if any, or another one up to the initial ring of the prototypal chain. -end quote.
Second, your statement in the previous blog post, JavaScript _super bullshit, that Prototype's implementation of $super() is right or nearly right.
In my confusion, I thought you were really stating that $super.method() is bullshit. So, I sent the Beginner's Java example with super.method() in it.
But now I see you were really arguing that according to class-based OOP, Resig's this._super(), for example, is incorrect, whereas Prototype's $super() is correct.
Have I clarified my misunderstanding?
Learn more about JavaScript before you post how you CAN and CANT do things. Your Class solution displays some bad practices. You CAN, in fact, recursively create a 'super' reference AND call it in correct context without doing so at run time. You CAN also solve many of you issues you are facing with a deepCopy function for pointers since with native JS, all objects are pointers by default.
While i think you hit some good areas, i think this implementation is less than desirable.
1. The Class.definition is straight up stupid and i see absolutely no use case for it. A classes prototype is its blueprints and should never be used in any other way. The definition also has a HUGE flaw which is native to javascript; the pointer. Objects are always pointers by default and if class B extends A but has an Array member on the prototype, you are now riddling classes with pointers.
2. No super? What kind of class is this? It's not too hard to create a recursive super for a class (recursive meaning deeper than one level). And making the developer use call() and apply() stinks IMO. And you can always write performance efficient code for this.
hey Michael, this is quite outdated Class but it looks like you learned JS yesterday or you would not underline 3 times something obvious such: objects are all pointers ... or the array in the prototype that is obviously shared since JS is born ...
Moreover, call and apply are the ABC of this language so if these stinks, you better use another one ( or opt for GWT or whatever "wannabe some other language" framework you like )
Finally, here the latest performances+class implementation, have fun with JavaScript!
Post a Comment