In my view, the strangest, most baffling thing you can say about JavaScript to a C++ or Java developer is that //any// JavaScript function can be used a constructor. Even the anonymous ones! This claim makes very little sense if you think of a constructor as some sort of privileged function, inextricably associated with a particular class. But JavaScript constructors are more general. Keep reading, and you'll see what I mean.\n\nExhibit one:\n{{{\n{}\n}}}\nis an empty object literal.^^1^^ There are two other ways of making an empty object:\n{{{\nnew Object()\n}}}\nand\n{{{\nnew (function() {})()\n}}}\nYou will never see the third expression in real JavaScript code, since the first two are easier to type, but in fact the second two methods are essentially the same, since {{{Object}}} and {{{(function() {})}}} are both functions.^^2^^ More to the point, both are functions that are being used as constructors. Since they construct empty objects, this role is rather nominal. Nevertheless, be sure you do not think of the {{{new}}} //function//{{{()}}} syntax as a function call with {{{new}}} applied to the result. The {{{new}}} keyword causes the function itself to be interpreted differently (more on this below), and any arguments are passed to the function only after {{{new}}} has created the object.\n\nNow suppose you've written this silly function for adding things:\n{{{\nfunction add(a, b) {\n return a + b;\n}\n}}}\nThe expression\n{{{\nnew add(1, 2)\n}}}\nis valid, but it returns another empty object, not 3. Yes, the code to add 1 and 2 was executed (try calling {{{alert}}} inside the function), but that code had no influence on the properties of the resulting object.\n\nSo what about non-empty objects? Again, three ways to make them:\n{{{\n{smell: 'blue'}\n}}}\n(pardon my synesthesia), or\n{{{\nvar obj = new Object();\nobj.smell = 'blue';\n}}}\nor\n{{{\nnew (function(color) {\n this.smell = color;\n})('blue')\n}}}\nIf you find this final example difficult to parse, consider an equivalent, two-statement version:\n{{{\nvar Odor = function(color) {\n this.smell = color;\n};\nvar myOdor = new Odor('blue');\n}}}\nThe difference between this last bit of code and the {{{add}}} function is that now we're manipulating {{{this}}} inside the body of the function. When a function is used as a constructor, any references to {{{this}}} inside the function become references to the brand-new (initially empty) object. That object, once subjected to the code inside the function, will be returned as the value of the {{{new}}} expression.\n\nSo there you have it. Functions that never refer to {{{this}}} can be used as constructors, but the effect is underwhelming. Functions that alter the properties of {{{this}}}, on the other hand, may be employed as meaningful constructors. Now that you've got the basic idea, consider a slightly more substantial example:\n{{{\nvar Num = function(value) {\n this.value = value;\n this.add = function(what) {\n this.value += what;\n return this;\n }\n};\nvar n = new Num(2); // n.value == 2\nn.add(1); // n.value == 3\nn.add(-4).add(5); // n.value == 4\n}}}\nEven this final example may seem rather basic, but you'll be quite lost in future posts if you aren't acquainted with these features of the language. The next post will dive into prototypes and {{{Function.prototype.apply}}}. Expect meatier examples, including at least one that I could imagine posing as an evil question in a job interview.\n\n<html><hr/></html>\n^^1^^ The object isn't really empty, since it will have a few default properties, like {{{toString}}} and {{{valueOf}}}, but it's pretty much as empty as it can be.\n^^2^^ This raises a good question: if {{{new Object()}}} and {{{new (function() {})()}}} both create empty objects, could we redefine the global function {{{Object}}} as an empty function? The answer would be yes if {{{Object}}} were only used as you see above, but {{{Object}}} can also be called as a normal function (without {{{new}}}). When used in this fashion, {{{Object}}} actually does something useful, so the empty function would not be an adequate substitute. I'll leave you to discover exactly what, say, {{{Object(42)}}} does.
In the last installment of [[4060++]], we had a look at JavaScript constructors. I attempted to make sense of the claim that any JavaScript function can be used as an object constructor. That background will be useful for this installment, but not critical. If you have trouble making it through this installment, go back and skim the previous one or hit up the widget on your left.\n\nJavaScript doesn't have any special syntax for the creation of classes, but constructor functions are a close substitute. You could define a "class" of {{{Explosive}}} objects with the following constructor function:\n{{{\nvar Explosive = function(id) {\n this.id = id;\n this.goBoom = function() {\n alert(this.id + " exploded.");\n }\n}\n}}}\nand then you could instantiate a pair of {{{Explosive}}}s and detonate them like so:\n{{{\nvar bomb1 = new Explosive(1);\nbomb1.goBoom();\n(new Explosive(2)).goBoom();\n}}}\nBut what if you want to add a new method to all {{{Explosive}}} objects while your script is running? Chances are, you won't be able to find where you hid them all, so adding a new property to each of them individually is not an option.\n\nFortunately, you can store properties common to all {{{Explosives}}} in a single place, namely the {{{prototype}}} property of the {{{Explosive}}} constructor. This jargon may be new to you, but a bit of code should make its meaning clearer. Consider this improved definition of the {{{Explosive}}} class:\n{{{\nvar Explosive = function(id) {\n this.id = id;\n}\nExplosive.prototype = {};\nExplosive.prototype.goBoom = function() {\n alert(this.id + " exploded.");\n}\n}}}\nNow, all {{{Explosive}}} objects will be given a pointer to {{{Explosive.prototype}}} when they are instantiated using the {{{new}}} keyword. (Note well: //an object receives a pointer to its prototype only if instantiated using the {{{new}}} keyword//.) When you call {{{bomb.goBoom()}}}, even though the {{{bomb}}} object itself does not support the property {{{goBoom}}}, the JavaScript interpreter will examine the {{{Explosive.prototype}}} object and call the method {{{goBoom}}} that it finds there. This saves some memory, since {{{goBoom}}} only has to be defined once, but the greater benefit is that you can modify {{{Explosive.prototype}}} even after instantiating a bomb or two:\n{{{\nvar bomb3 = new Explosive(3);\nExplosive.prototype.defuse = function() {\n this.goBoom = function() {\n alert(this.id + " failed to explode.");\n }\n}\nbomb3.defuse();\nbomb3.goBoom(); // no explosion\n(new Explosive(4)).goBoom(); // explosion!\n}}}\nThe {{{defuse}}} method redefines {{{goBoom}}} so that it no longer claims the bomb has exploded. Note that, even though {{{defuse}}} and {{{goBoom}}} are properties of the {{{prototype}}} object shared between all {{{Explosives}}}, when {{{defuse}}} redefines {{{goBoom}}}, the change only affects the object against which {{{defuse}}} was invoked. (If we wanted to defuse all bombs in a single stroke, we could redefine the prototypical {{{goBoom}}} method by altering {{{Explosive.prototype.goBoom}}} directly.)\n\nAlso note that {{{Explosive.prototype}}} has to be defined prior to instantiating any bombs; otherwise, the bombs will never get hooked up to the prototype, and if you attempt to call {{{goBoom}}} on a bomb, JavaScript will throw an exception. Depending on your point of view, this might represent a graver threat than our little "explosions." Better to define an empty prototype object, just in case, than to find out you need one when it's too late.\n\nDigression!\n\nSuppose you have an array of arguments, and you want to pass them to some function. Here's the naive, ugly way:\n{{{\nvar args = [1,2,3];\nfunc(args[0], args[1], args[2]);\n}}}\nPardon my adjectives. This approach only seems naive and ugly if you know that you could do the same using the function's {{{apply}}} method:\n{{{\nfunc.apply(this, [1,2,3]);\n}}}\nYes, you read correctly. Functions can have methods. Functions in JavaScript are objects, and {{{apply}}} is one of their properties. In fact, {{{apply}}} can be accessed directly as {{{Function.prototype.apply}}}. (Not such a digression after all, huh?) The first argument to {{{apply}}} defines the value of {{{this}}} inside the function. This makes it possible to treat any function as a method of an object, even if the function is not actually a property of the object: simply give the object as the first argument to {{{apply}}}. The second argument is the array of parameters to pass to the function.\n\nNow suppose we have an array of arguments, but instead of simply passing them to a function, we want to pass them as the arguments to a function that we're using as a constructor. The trouble is that {{{new}}} and {{{apply}}} don't mix so well, since the following syntax is invalid:\n{{{\nvar ctor = function(a, b, c) {...};\nvar obj = new ctor.apply(???, [1,2,3]);\n}}}\nFirst, {{{ctor.apply}}} is not the constructor function we wanted to use. Second, we don't know the value of {{{this}}} until after {{{new}}} creates the object, so there is no sensible first argument for {{{apply}}}. But if you read the previous installment, you know that constructor functions are just functions applied to empty objects, so the following might do the trick:\n{{{\nvar obj = {};\nctor.apply(obj, [1,2,3]);\n}}}\nWe're using {{{ctor}}} as a constructor, effectively, but the {{{new}}} keyword is nowhere to be seen! If you understand why this works, you've mastered the material from the first installment.\n\nThis solution may be clever, but it's wrong. Think about our improved {{{Explosive}}}s. The use of {{{new}}} is required if you want an object to acquire its constructor's {{{prototype}}} property. Since the object literal{{{ {} }}}was not created using {{{new}}} and the appropriate constructor, it is just a generic object, completely unaware of any prototype that the constructor {{{ctor}}} might have.\n\nIn the last installment, I promised that I would pose a tricky interview-style problem. Well, here it is: rewrite this little hack so that it works. That is, write a function called {{{newApply}}} that takes a constructor and an array of arguments and returns the object that would be created if you had passed those arguments to the constructor in a {{{new}}} expression. I'll give my solution next time.
Here's the solution the puzzle from last time:\n{{{\nvar newApply = function(ctor, array) {\n var dummy = function() {};\n dummy.prototype = ctor.prototype;\n var obj = new dummy();\n ctor.apply(obj, array);\n return obj;\n}\n}}}\nIn order to create an empty object that is aware of {{{ctor.prototype}}}, we have to create a temporary constructor function, set its prototype equal to {{{ctor.prototype}}}, and then instantiate the dummy object. We then use {{{apply}}} to simulate the application of the constructor to the new object using the arguments contained in the array.\n\nFrankly, this vague approach to a discussion about inheritance was moving far to slowly. You probably would not have found this blog if you were not some kind of JavaScript guru. If you're ready to dive head first into some substantial JavaScript, I've got just the ticket: ClassMorph1 (of 12).
''BIG NEWS'' Prototype 1.6.0_rc0 has finally gotten itself an elegant inheritance mechanism. Because its interface differs from that of my patch, and because the rest of Prototype now depends on the new interface, my claim of backwards compatibility is no longer valid. As happy as I was with my implementation, there's no sense at all in maintaining an independent patch with only slightly different semantics. For archival purposes, the most recent version of the trunk for which my patch made sense (rev 7263) still lives here: MyPrototypeBranch. Just in case I haven't made it clear enough already, //my patch is no longer intended for production use!//\n\nCheck out [[this announcement|http://prototypejs.org/2007/8/15/prototype-1-6-0-release-candidate]] for a brief example of the syntax chosen by the core team, and see [[the Rails Trac browser|http://dev.rubyonrails.org/browser/spinoffs/prototype/trunk/src/base.js]] for the code itself. I am particularly impressed with the way overridden methods are made available (you just name the first argument of the overriding method {{{$super}}}). This strategy notably removes the need for setting and resetting {{{this.$super}}} and simplifies the task of determining which methods need to be wrapped (note the test {{{value.argumentNames().first() == "$super"}}}).\n\nThis news means seraph.im will have to find a new identity. Alas, obsolescence is bittersweet.\n\n-----\n\nAdded support for inheritable static methods. No additional lines were required, but the change adds 0.05k to the size of the patch, for a total of 5.62k (uncompressed). Details in ClassMorph14.\n\nAdded a slew of ClassUnitTests to verify the functionality of the {{{instanceof}}} operator when evaluated on objects and classes created using the {{{Class.create}}} facility. A few modifications for readability have also been made to MyPrototypeBranch.
Here is the definition of {{{Class.create}}} as it currently lives in the [[Prototype]] library:\n{{{\nClass = {\n create: function() {\n return function() {\n this.initialize.apply(this, arguments);\n }\n }\n}\n}}}\nDefine a constructor by evaluating {{{Class.create()}}}. Then set the prototype of that constructor to be some object of your choosing. The only expectation is that this object should support an {{{initialize}}} method, because, when an object is created using the constructor, the constructor will try to call the object's {{{initialize}}} method.\n\nOnce you're comfortable with this idiom, proceed to ClassMorph2. There are twelve entries in total. I have kept them deliberately short. Together, they present a thorough rationale for my ongoing work towards a better inheritance mechanism for JavaScript.
In order to resolve {{{this.sup}}} properly, we need to recognize which methods are actually being overridden. Have a look at {{{Class.create}}} again. We are interested in the differences between {{{decl.prototype}}} (if it exists) and {{{ctor.prototype}}}. The pseudo-code algorithm goes something like this:\n* Consider each method in {{{ctor.prototype}}}\n## Given a method {{{func}}} from {{{ctor.prototype}}}, select the property with the same name from {{{decl.prototype}}} (note that this property may be {{{undefined}}} or something other than a function)\n## Create a function which takes the method from {{{ctor.prototype}}}, sets {{{this.sup}}} to the corresponding property from {{{decl.prototype}}}, calls the original method, and then resets the value of {{{this.sup}}}\n## Replace the {{{ctor.prototype}}} method with this new method\nFirst let's write the function from step 2 which takes two functions and generates the wrapper function to maintain {{{this.sup}}}:\n{{{\nfunction wrap(override, overridden) {\n return function(/*arguments*/) {\n var result, saved = this.sup;\n this.sup = overridden;\n result = override.apply(this, arguments);\n this.sup = saved;\n return result;\n }\n}\n}}}\nThis function works even when {{{this.sup}}} is not initially defined; the {{{saved}}} variable simply saves the {{{undefined}}} value. Also, when {{{overridden}}} is {{{undefined}}}, this code prevents {{{this.sup}}} from retaining a spurious value from a previous function call, because it forces {{{this.sup}}} to be set to {{{undefined}}}. I find this behavior quite elegant.\n\nThe function does //not// work, however, when the {{{override}}} method throws an exception, for then {{{this.sup}}} is never reset to the saved value. In order to avert this very real threat, we need to use a {{{try}}}-{{{catch}}} block:\n{{{\nfunction wrap(override, overridden) {\n return function(/*arguments*/) {\n var result, saved = this.sup;\n this.sup = overridden;\n try { result = override.apply(this, arguments); }\n catch (e) { throw e; } finally { this.sup = saved; }\n return result;\n }\n}\n}}}\nI recently submitted a [[patch|http://trac.dojotoolkit.org/ticket/1260]] relating to the same vulnerability to the [[Dojo]] bug tracker, and it was accepted the following day. The precaution is uncontroversial but surprisingly unintuitive. (Perhaps now is a good time to reiterate that I am not actually inventing these posts as I go along. The linearity of the development is purely rhetorical.)\n\nAt this point, the wrapping function isn't immediately useful. In the next post, we will write just one more function which uses {{{wrap}}} to implement the {{{this.sup}}} capability. Then you will know everything I know! ClassMorph11.
First let's pretend we already have a method that uses {{{wrap}}} to implement the rest of the algorithm given in the previous post. Call that method {{{inherit}}}, and say that it takes two arguments: the prototype of the derived class and the prototype of the base class. Once {{{inherit}}} has done its job, the functionality of the derived class will not have changed except that the function {{{this.sup}}} will now be available throughout the class. That's the goal.\n\nWe begin by modifying {{{Class.create}}}:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n ctor.prototype = (typeof decl == 'function')\n ? new decl() : (decl = decl || {});\n Class.inherit(ctor.prototype, decl.prototype);\n ctor.extend = Class._extend;\n return ctor;\n },\n ...\n}\n}}}\nI am assuming that {{{Class.inherit}}} will behave appropriately when {{{decl.protoype}}} is {{{undefined}}}. But don't take my word for it:\n{{{\nvar Class = {\n ...\n inherit: function(self, parent) {\n for (var f in self) {\n if (f == 'valueOf') continue;\n var func = self[f], pFunc = parent && parent[f];\n if (func !== pFunc && typeof func == 'function')\n self[f] = Class.wrap(func, pFunc); \n }\n },\n ...\n}\n}}}\nFor better or worse, the {{{valueOf}}} function cannot make use of {{{this.sup}}}. This is due to the fact that the call to {{{override.apply(this, arguments)}}} in {{{Class.wrap}}} attempts to invoke {{{valueOf}}} against {{{this}}}, so an infinite loop ensues during the process of {{{wrap}}}ping the method. You can either take my word for this or work it out in detail for yourself. It is not a huge loss, especially since you can still refer to {{{Base.prototype.valueOf}}} if you like.\n\nAs for the rest of the logic, {{{func}}} must be a different function from {{{pFunc}}} to count as an override, and it only makes sense to replace {{{func}}} if it is actually a function. It is entirely possible that {{{pFunc}}} will be {{{undefined}}}, either because {{{parent}}} itself is {{{undefined}}} or because {{{parent}}} has no property named {{{f}}}. The "boolean" expression {{{parent && parent[f]}}} evaluates to {{{undefined}}} (not {{{false}}}) in either of these cases; otherwise, it evaluates to some defined {{{parent[f]}}} value. This is perhaps the most subtle point in all of these explanations, so feel free to spend some time thinking it through if it doesn't come easily.\n\nI will cut this post short, with the promise that the next post will consist of nothing but the combined, final code: ClassMorph12.
At just 47 lines (1.28k), this beats the living hell out of my earlier [[patch|http://dev.rubyonrails.org/ticket/4060]] (I was happy to nudge that monstrosity under 200). Here's the whole thing:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments); }\n ctor.prototype = (typeof decl == 'function')\n ? new decl() : (decl = decl || {});\n Class.inherit(ctor.prototype, decl.prototype);\n ctor.extend = Class._extend;\n return ctor;\n },\n\n inherit: function(self, parent) {\n for (var f in self) {\n if (f == 'valueOf') continue;\n var func = self[f], pFunc = parent && parent[f];\n if (func !== pFunc && typeof func == 'function')\n self[f] = Class.wrap(func, pFunc);\n }\n },\n\n wrap: function(override, overridden) {\n return function(/*arguments*/) {\n var result, saved = this.sup;\n this.sup = overridden;\n try { result = override.apply(this, arguments); }\n catch (e) { throw e; } finally { this.sup = saved; }\n return result;\n }\n },\n\n _extend: function(sub) {\n if (typeof sub != 'function')\n sub = Class.fnFromObj(sub);\n sub.prototype = this.prototype;\n return Class.create(sub);\n },\n\n fnFromObj: function(obj) {\n return function() {\n for (var p in obj)\n this[p] = obj[p];\n this.toString = obj.toString;\n this.valueOf = obj.valueOf;\n }\n }\n}\n}}}\nThanks for sticking it out with me. If you're reading these words, even if you plan to contest everything I said (nay, especially), you are a hero to me.\n\nWait, what's that? The fun doesn't stop here? ClassMorph13!\n\nAs it turns out, supporting inheritance for static properties is quite easy: ClassMorph14 explains how.
[[Juerg Lehni]] has suggested a rather clever optimization to avoid calling {{{Class.wrap}}} on methods that never make use of {{{this.sup}}}. A text representation of a function can be retrieved before {{{Class.wrap}}} is called, so we can simply check to see whether the string {{{this.sup(}}} is present. This noticeably improves the performance of the majority of methods that will not use {{{this.sup}}}, and it also happens to fix a bug in Safari: the {{{for}}}-{{{in}}} loop may visit some properties more than once, but the second time a functional property is encountered, it will not be wrapped again, since the wrapped version of the function will not contain a call to {{{this.sup}}}.\n\nWe can do this by preventing the call to {{{Class.wrap}}} in {{{Class.inherit}}}:\n{{{\nClass = {\n ...\n inherit: function(self, parent) {\n for (var f in self) {\n if (f == 'valueOf') continue;\n var func = self[f], pFunc = parent && parent[f];\n if (func !== pFunc && typeof func == 'function' &&\n /\sWsup\ss*(\s(|[\s'\s"\s[\s.][^=]*?\s()/.test(func))\n self[f] = Class.wrap(func, pFunc);\n }\n },\n ...\n}\n}}}\nRegular expressions are horribly unreadable things. This one takes some explanation. First we match any instance of {{{sup}}} that is not the suffix of some other word, followed optionally by whitespace:\n{{{\n\sWsup\ss*\n}}}\nThen, if we find an opening parenthesis, {{{sup}}} is being called as a function, so we can assume the function needs to be wrapped:\n{{{\n\sWsup\ss*(\s(\n}}}\nOtherwise, {{{sup}}} may be followed by a single or double quotation mark (think {{{this['sup']}}} or {{{this["sup"]}}}), or an opening square bracket (think {{{this.sup['apply'](...)}}}), or a period (think {{{this.sup.apply(...)}}}):\n{{{\n\sWsup\ss*(\s(|[\s'\s"\s[\s.]\n}}}\nWe don't want to match any assignment statements with {{{sup}}} on the left-hand side, but we must eventually have an opening parenthesis:\n{{{\n\sWsup\ss*(\s(|[\s'\s"\s[\s.][^=]*?\s()\n}}}\nThe final {{{*}}} is followed by a {{{?}}} so that it will not greedily consume the opening parenthesis. I'm hardly a regex expert, so there may be something a bit cleaner, but I've tested this expression pretty thoroughly. False positives are better than false negatives, since wrapping a function is harmless but failing to do so is bad news if {{{this.sup}}} is actually used.\n\nThough I will not be changing the foregoing discussion to reflect this insight (since it arose afterward, and since rewriting is such a headache), all further changes will be reflected in [[MyPrototypeBranch]].
The first step toward supporting static properties is to make the result of {{{Class.create}}} available while the class is being defined. If the last sentence didn't make sense, consider this buggy example:\n{{{\nvar Blob = Class.create(function() {\n Blob.someStaticMethod = function(...) { ... }\n this.someNonStaticMethod = function(...) { ... }\n});\n}}}\nWhat's wrong? Well, {{{Blob}}} isn't defined until {{{Class.create}}} returns a result, but the assignment to {{{Blob.someStaticMethod}}} happens before {{{Class.create}}} returns. All hope is not lost, however, for we know what object will be returned from {{{Class.create}}} before we call the function that we passed in. Namely, {{{Class.create}}} will return the local variable {{{ctor}}}:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n decl = decl || {};\n ctor.prototype = (typeof decl == 'function')\n ? new decl() : decl;\n Class.inherit(ctor.prototype, decl.prototype || {});\n ctor.extend = Class._extend;\n return ctor;\n },\n ...\n}\n}}}\nThis suggests the following tiny change:\n{{{\nctor.prototype = (typeof decl == 'function')\n ? new decl(ctor) : decl;\n}}}\nNow, we can write\n{{{\nvar Blob = Class.create(function(Blob) {\n Blob.someStaticMethod = function(...) { ... }\n this.someNonStaticMethod = function(...) { ... }\n});\n}}}\nThe constructor function returned by {{{Class.create}}} now has a property named "someStaticMethod." Note that (subject to your personal style) the code above could be written equivalently as\n{{{\nvar Blob = Class.create(function(static_properties) {\n static_properties.someStaticMethod = function(...) { ... }\n this.someNonStaticMethod = function(...) { ... }\n});\n}}}\nOf course, this syntax assumes that the {{{decl}}} argument to {{{Class.create}}} is a function, not an object. Now that you have a basic idea about the syntax of static properties, we're ready to investigate how these properties are inherited: ClassMorph15.
The desired use-case for inheriting static properties is illustrated by the following code:\n{{{\nvar Base = Class.create(function(static_properties) {\n static_properties.staticA = function(...) { ... }\n static_properties.staticB = function(...) { ... }\n});\n\nvar Derived = Base.extend(function(static_properties) {\n static_properties.staticA = function(...) { ... }\n static_properties.staticC = function(...) { ... }\n});\n}}}\nAt this point, {{{Derived.staticA}}} (the new version), {{{Derived.staticB}}}, and {{{Derived.staticC}}} should be defined. Making this happen is easier than you might think. First we need to modify {{{Class.create}}} to take a second (optional) argument:\n{{{\nClass = {\n create: function(/*optional:*/ decl, _static) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n ...\n },\n ...\n}\n}}}\nAssume this second argument is an object with all the static properties of the base class (in fact, as we shall soon see, it will usually just //be// the base class constructor). Under this assumption, we can ensure that {{{ctor}}} has all these properties by calling {{{Object.extend}}}:\n{{{\nClass = {\n create: function(/*optional:*/ decl, _static) {\n var ctor = Object.extend(function() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }, _static);\n ...\n },\n ...\n}\n}}}\nSo where does {{{_static}}} come from? A {{{_static}}} argument is supplied only if we are subclassing (extending) an existing class, and the {{{Class._extend}}} method is where subclassing happens. Here's what {{{Class._extend}}} looks like now:\n{{{\nClass = {\n ...\n _extend: function(sub) {\n if (typeof sub != 'function')\n sub = Class.fnFromObj(sub);\n sub.prototype = this.prototype;\n return Class.create(sub);\n },\n ...\n}\n}}}\nThe change here is trivial:\n{{{\nreturn Class.create(sub, this);\n}}}\nRecall that {{{Class._extend}}} becomes the {{{extend}}} property of the local variable {{{ctor}}} returned from {{{Class.create}}} (see ClassMorph8 for an explanation). Thus, when we call {{{Base.extend(...)}}}, the value returned above will be equivalent to {{{Class.create(sub, Base)}}}. Any properties attached to {{{Base}}} will thus be copied to the generated subclass (with the exception of properties like {{{extend}}} and {{{prototype}}}, which get overwritten as necessary).\n\nThese additions do not break any of my current unit tests, but I plan to write some additional tests to exercise the new functionality as soon as possible.
The first problem with {{{Class.create}}} is that it does not actually define a class for you. Instead, you have to set the constructor's prototype independently, after calling {{{Class.create}}}. Then and only then will instances of your class support useful methods. This independence between {{{Class.create}}} and the setting of the prototype is one of the reasons that the [[Prototype]] library has been so hard to document using automatic tools. Let's fix this:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n var ctor = function() {\n this.initialize.apply(this, arguments);\n }\n ctor.prototype = decl || {};\n return ctor;\n }\n}\n}}}\nNote that nothing has broken. If you still choose to call {{{Class.create}}} with no arguments, its behavior is the same as before.^^1^^ Also note that I am refusing to assign a null or undefined value as {{{ctor.prototype}}}: if {{{decl}}} evaluates to false, I just use an empty object as the prototype.\n\nFollow me? ClassMorph3.\n\n<html><hr/></html>\n^^1^^ Opinions have been running pretty hot as to whether backwards compatibility is really a worthwhile goal. If permitted some departure from the past, I could make the code a bit simpler, but I believe there is still value in the original interface. I have also found it quite easy to layer arbitrary syntactic sugar over this system ([[ask me|mailto:newmanb@stanford.edu]] about that if you're interested), so your favorite style of creating classes is probably only a few lines away.
That was rather trivial, but important nevertheless. We're making progress. What happens if we decide not to define {{{initialize}}}? It could happen, after all, and the current code would crash because it assumes that {{{this.initialize}}} will be a function. Perhaps this is desirable behavior; at the moment, the only purpose of {{{Class.create}}} is to return a constructor which guarantees to call {{{initialize}}} on any instantiated objects, so it could be argued that the client should be forced to catch the exception if no {{{initialize}}} method is present.\n\nIn any case, you'll see this change in the rest of my posts:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n ctor.prototype = decl || {};\n return ctor;\n }\n}\n}}}\nWe could have tested the type differently: {{{this.initialize instanceof Function}}}. I performed some quick benchmarks to see which way was faster. Evaluating {{{typeof (function() {}) == 'function'}}} 10,000 times took an average of 148.4 milliseconds across 10 trials, with a standard deviation of 23.6ms. Evaluating {{{(function() {}) instanceof Function}}} in the same way took an average of 180.9ms, with standard deviation 25.2ms. So the {{{typeof}}} method is slightly faster.\n\nThis point is subtle enough that I felt it deserved its own post. A baby step, maybe, but a step in the right direction. Next: ClassMorph4.
Now for a little persona-oriented design. Imagine you're a web developer at a software company with more than one employee. You're in the middle of a project that multiple developers are working on. You're getting weary of JavaScript's tedious inheritance mechanisms, and you're itching to suggest a better system. You need to persuade your teammates that the switch will be painless.\n\nIf your project employs any sort of object-oriented JavaScript other than the style used in the [[Prototype]] library, there's a good chance you've got constructor functions lying all over the place. In other words, instead of defining class prototypes with hashes, //a la// [[Prototype]], you define classes with functions:\n{{{\nvar MyClass = function(arg1, arg2, ...) {\n this.method1 = function() {...};\n this.method2 = function() {...};\n ...\n}\n}}}\nLook familiar? Well, it's painfully familiar to me. If we can make it easier to move away from this kind of class definition to the style used in [[Prototype]], that will be a huge win for your case (and for [[Prototype]]). So here's the deal. We need to be able to pass a function to {{{Class.create}}} in the place of {{{decl}}}. In fact, I chose the generic name {{{decl}}} knowing that we'd want it to be either an object or a function (yes, there's a master plan here :).\n\nFor the sake of brevity and suspense, this post ends here. Keep reading for the implementation: ClassMorph5.
In this iteration, we'll broaden the type of {{{decl}}}, so that it can be either an object or a traditional constructor function. First we need to decide which option we prefer, because we'll want to convert the other one into the one we'd rather have.\n\nI vote to prefer objects:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n ctor.prototype = (typeof decl == 'function')\n ? new decl() : (decl = decl || {});\n return ctor;\n }\n}\n}}}\nThe line just before {{{return ctor}}} uses {{{decl}}} to construct a new object if {{{decl}}} is a function. Otherwise, we simply assign {{{decl}}} itself as the {{{prototype}}} of {{{ctor}}}, provided that {{{decl}}} is not {{{null}}}, {{{undefined}}}, or {{{false}}} (in those cases, {{{decl}}} gets redefined as the empty object).\n\nWe'll worry about whether this was the right decision a few posts on. For now, remember why we made this change: we want to make it easy to move away from non-[[Prototype]] legacy code. Now that we've made the change, how would you convert the following code to a [[Prototype]] class?\n{{{\nvar MyClass = function(arg1, arg2, ...) {\n this.method1 = function() {...};\n this.method2 = function() {...};\n ...\n}\n}}}\nEasy enough:\n{{{\nvar MyClass = Class.create(function() {\n this.initialize = function(arg1, arg2, ...) {\n // set any instance variables\n }\n this.method1 = function() {...};\n this.method2 = function() {...};\n ...\n});\n}}}\nYou'll have to be careful about any direct references to the arguments {{{arg1}}}, {{{arg2}}}, etc., since those variables will no longer be in the default scope of the class definition. You may want to set them as instance variables and refer to them as, e.g., {{{this.arg1}}}. Otherwise, your work is pretty clear-cut. Move initialization logic into the {{{initialize}}} method, and just wrap the old constructor with {{{Class.create(...)}}}.\n\nSpeaking from experience, the ability to define classes with functions is crucial. Speaking from my knowledge of the future, this will soon prove useful in another way! ClassMorph6.
ClassMorph5 was long, but the change was minor. In this post, we will begin to make the transition not just easy but worthwhile as well.\n\nWe want to support the following syntax:\n{{{\nvar Base = Class.create({...});\nvar Derived = Base.extend({...});\n}}}\nThis syntax is [[Dean Edwards|http://dean.edwards.name]]' idea, and it has Sam Stephenson's [[blessing|http://sam.conio.net/articles/better-inheritance-for-prototype]]. Subclassing should be so easy.\n\nBefore we return {{{ctor}}} we need to set its {{{extend}}} property to be a function of one argument, which should be a definition of a subclass, just like {{{decl}}}. This function should return a full-fledged subclass of {{{ctor}}}.\n\nFor the sake of our piecemeal linear progression, let us assume that {{{sub}}} is a constructor //function//, rather than a hash. This is the easy case. We will handle the object case (a bit harder) in a subsequent post:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n ctor.prototype = (typeof decl == 'function')\n ? new decl() : (decl = decl || {});\n\n ctor.extend = function(sub) {\n sub.prototype = this.prototype;\n return Class.create(sub);\n }\n\n return ctor;\n }\n}\n}}}\nWhen I first wrote this code, I used {{{new (this)()}}} instead of {{{this.prototype}}}. But that choice would be problematic, since instantiating {{{this}}} (the original constructor, {{{ctor}}}) will cause its corresponding {{{initialize}}} method to be invoked. We want to //construct// an instance of the class, but we do not want to //initialize// it. For one thing, we don't know what arguments to pass to {{{initialize}}}. For another, calling {{{initialize}}} might be costly, but constructing an instance of the superclass merely involves creating a few methods.\n\nWe could have set some sort of flag to prevent {{{initialize}}} from getting called (in fact, that's what [[Dean Edwards|http://dean.edwards.name]] does, with {{{Base.prototyping}}}), but all we really need is an object that has all of {{{ctor}}}'s class methods. That object is none other than {{{ctor.prototype}}}!\n\nNow to handle the harder case. See what happens if {{{sub}}} is an object: ClassMorph7.
Back for more? I've got a promise to keep. The {{{extend}}} method needs to handle objects as well as constructor functions. Here's a function to do the conversion:\n{{{\nfunction fnFromObj(obj) {\n return function() {\n for (var p in obj)\n this[p] = obj[p];\n this.toString = obj.toString;\n this.valueOf = obj.valueOf;\n }\n}\n}}}\nThis is probably the hardest bit of code I have proposed so far. The high-level understanding to keep in mind is that this function will become equivalent to the original object {{{obj}}} when constructed using {{{new}}}. Imagine what happens when the following code executes:\n{{{\nvar obj1 = {a: 1, b: 2, c: 3};\nvar fn = fnFromObj(obj1);\nvar obj2 = new fn();\n}}}\nThe variables {{{obj1}}} and {{{obj2}}} should now be indistinguishable. Read my entry on using arbitrary functions as constructors ([[20 August 2006]]) if you're still completely baffled. The {{{toString}}} and {{{valueOf}}} methods will unfortunately be skipped in the iteration, so they have to copied manually. (A lesson learned the hard way.)\n\nNow for the integration:\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n ctor.prototype = (typeof decl == 'function')\n ? new decl() : (decl = decl || {});\n\n ctor.extend = function(sub) {\n if (typeof sub != 'function')\n sub = Class.fnFromObj(sub);\n sub.prototype = this.prototype;\n return Class.create(sub);\n }\n\n return ctor;\n },\n\n fnFromObj: function(obj) {\n return function() {\n for (var p in obj)\n this[p] = obj[p];\n this.toString = obj.toString;\n this.valueOf = obj.valueOf;\n }\n }\n}\n}}}\nNow [[Dean Edwards|http://dean.edwards.name]]' syntax is fully supported for both objects and constructor functions! We've got some cleaning up to do (and another key feature to add), but this is beginning to look quite solid, if I may say so.\n\nOnward and upward: ClassMorph8.
Time for a break. Notice that {{{ctor.extend}}} depends on nothing except its argument, {{{sub}}}. This opens up an opportunity for a small optimization. Instead of defining {{{extend}}} over and over again every time {{{Class.create}}} is called, we can reuse the same function object every time!\n{{{\nClass = {\n create: function(/*optional:*/ decl) {\n function ctor() {\n if (typeof this.initialize == 'function')\n this.initialize.apply(this, arguments);\n }\n ctor.prototype = (typeof decl == 'function')\n ? new decl() : (decl = decl || {});\n ctor.extend = Class._extend;\n return ctor;\n },\n\n _extend: function(sub) {\n if (typeof sub != 'function')\n sub = Class.fnFromObj(sub);\n sub.prototype = this.prototype;\n return Class.create(sub);\n },\n\n fnFromObj: function(obj) {\n return function() {\n for (var p in obj)\n this[p] = obj[p];\n this.toString = obj.toString;\n this.valueOf = obj.valueOf;\n }\n }\n}\n}}}\nThat's all for this time. Note that {{{ctor.extend}}} will not conflict with any object properties called "extend," because {{{extend}}} is defined as a property of the constructor function itself, not of the resulting object.\n\nYou're having a good time. I can tell. ClassMorph9.
Time for some real fun. If you have doubts about the utility of the changes proposed so far, prepare to be impressed!\n\nWhen we create a derived class (subclass), it is very likely that we will override some of the base class's (superclass's) methods. Because we're using a chain of prototypes, the base class's (superclass's) versions of those methods still exist; they just become difficult to access. At present, if we wanted to refer to the base class's (superclass's) version of {{{initialize}}} within the derived class's (subclass's) {{{initialize}}} method, we'd have to do something like the following:\n{{{\nvar Derived = Base.extend({\n initialize: function(val) {\n Base.prototype.initialize.call(this, val);\n this.val2 = val + 2;\n }\n});\n}}}\nMany people, including [[Kevin Lindsey|http://www.kevlindev.com/tutorials/javascript/inheritance/index.htm]], do not find this particularly unpalatable. I do. The syntax can be made much cleaner, but I will admit that it requires adding more code. Though the extra code is less than 20 lines, you will have to decide what you value more.\n\nThis is what I have in mind:\n{{{\nvar Derived = Base.extend({\n initialize: function(val) {\n this.sup(val);\n this.val2 = val + 2;\n }\n});\n}}}\nThe idea is that {{{this.sup}}} simply resolves to the base class version of whatever method calls it. Since {{{this.sup}}} is being called inside {{{Derived.prototype.initialize}}}, it behaves exactly like {{{Base.prototype.initialize}}}. You don't even have to know what the base class is called in order to use {{{this.sup}}}. I just love this syntax, but I might be in the minority. Note that the more verbose syntax is still available; {{{this.sup}}} is just a shorthand.\n\nI welcome your feedback as we move forward: ClassMorph10.
Get 'em while they're [[hot|prototype/test/unit/class.html]].
[[Announcements]]\nClassMorph1\n[[Prototype]]
I am Ben Newman, and my email address is [[auratus@gmail.com|mailto:auratus@gmail.com]]. But you can talk to me //right now// using the [[Meebo|http://meebome.com]] widget below. I'll even receive messages you leave while I'm away when I get back (jabber is awesome like that).\n\n<html><div style="width:430px"><style>.mcrmeebo { display: block; background:url("http://widget.meebo.com/r.gif") no-repeat top right; } .mcrmeebo:hover { background:url("http://widget.meebo.com/ro.gif") no-repeat top right; } </style><embed src="http://widget.meebo.com/mcr.swf?id=XcCiYtdReC" type="application/x-shockwave-flash" width="430" height="300" /><a href="http://www.meebo.com/rooms" class="mcrmeebo"><img alt="http://www.meebo.com/rooms" src="http://widget.meebo.com/b.gif" width="430" height="45" style="border:0px"/></a></div></html>
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:\n* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)\n* MainMenu: The menu (usually on the left)\n* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened\nYou'll also need to enter your username for signing your edits: <<option txtUserName>>
This website is an instance of Jeremy Ruston's marvelous [[TiddlyWiki|http://tiddlywiki.com]]. It may seem as though you can edit the page, but you unfortunately do not have the ability to save your changes. That's because this isn't really a wiki. It's more like a personal wiki, or a blog. But you can get your own by heading over to the [[TiddlyWiki site|http://tiddlywiki.com]].\n\nA note to the curious: I use the (S)FTP program [[Transmit|http://www.panic.com/transmit/]] to access this html file on a remote server, and then I use Firefox to open the file for editing. When I save my changes, Transmit syncs them to the remote server. Inelegant but ever so convenient. That's the ad-hoc spirit.\n\nI'm working on a commenting system, but content comes first.
The following "tiddlers" describe my thinking about inheritance in the [[Prototype|http://prototype.conio.net]] library and JavaScript in general. When read in sequence, they should be accessible to developers who know only a little JavaScript, but the pace picks up fairly rapidly. I will try to make them mildly entertaining (or at least nostalgic) for you experts, too.\n# [[20 August 2006]]: Arbitrary functions as constructors\n# [[21 August 2006]]: Prototypes and {{{apply}}}\n# [[22 August 2006]]: Prototypes and [[Prototype|http://prototype.conio.net]]\n# ClassMorph1: Solving the problem of hidden fields
A.k.a. Mocha, LiveScript, JScript, ECMAScript. Official specification can be found [[here|http://www.ecma-international.org/publications/standards/Ecma-262.htm]].
Co-contributor, maintains http://scratchdisk.com.
[[Announcements]]\nPrototypeStuff\nClassMorph1\nHowThisWorks\nGetInTouch\n\n<html><!--Creative Commons License--><a rel="license" href="http://creativecommons.org/licenses/by/2.5/" target='_blank'><img alt="Creative Commons License" border="0" src="http://creativecommons.org/images/public/somerights20.png"/></a><br/>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/2.5/" target='_blank'>Creative Commons Attribution 2.5 License</a>.<!--/Creative Commons License--><!-- <rdf:RDF xmlns="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">\n <Work rdf:about="">\n <license rdf:resource="http://creativecommons.org/licenses/by/2.5/" />\n <dc:type rdf:resource="http://purl.org/dc/dcmitype/Text" />\n </Work>\n <License rdf:about="http://creativecommons.org/licenses/by/2.5/"><permits rdf:resource="http://web.resource.org/cc/Reproduction"/><permits rdf:resource="http://web.resource.org/cc/Distribution"/><requires rdf:resource="http://web.resource.org/cc/Notice"/><requires rdf:resource="http://web.resource.org/cc/Attribution"/><permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/></License></rdf:RDF> --></html>
The latest version of [[Prototype]], with my changes applied: [[get it here|http://seraph.im/prototype/dist/prototype.js]].
ClassUnitTests now [[running|prototype/test/unit/class.html]]. More to come, but quality over quantity. Caught a fun edge case last night, but it's fixed now (and reflected in the discussion beginning with ClassMorph5). Luckily enough, the fix actually led to a further reduction in the length of the code! (By the way, it's not a bad sign that {{{testSupLatency}}} is now failing. It's a overly strict test, and significant performance gains are had by doing something that breaks it.)
<div class='header' macro='gradient vert #999 #39f'>\n<div class='headerShadow'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n<div class='headerForeground'>\n<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;\n<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>\n</div>\n</div>\n<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>\n<div id='sidebar'>\n<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>\n<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>\n</div>\n<div id='displayArea'>\n<div id='messageArea'></div>\n<div id='tiddlerDisplay'></div>\n</div>
Sam Stephenson's [[masterpiece|http://prototypejs.org]] of JavaScript style. Get the latest version using subversion:\n{{{\nsvn co http://dev.rubyonrails.org/svn/rails/spinoffs/prototype\n}}}\nI owe a lot to this library. It taught me JavaScript.
For better or worse, I am the author of patch [[#4060|http://dev.rubyonrails.org/ticket/4060]], an update to the [[Prototype library|http://prototype.conio.net]] that aimed to support a more flexible inheritance mechanism. The patch had three emphases:\n* Be backwards-compatible with the {{{Class.create}}} mechanism\n* Use Javascript's prototype chains as the language [[designer|http://en.wikipedia.org/wiki/Brendan_Eich]] intended\n* Make it very easy to call overridden methods\nThe patch met these goals. There were even a few early adopters who put my changes to good use. But, alas, there were complaints, and I am entirely sympathetic to them. Just a few:\n* The patch added more than 200 lines to the library, which increased the download size by about 7k.\n* I got bogged down implementing nifty features like method privacy, forgetting how dearly JavaScripters love the freedom of their language. I love it too! What was I thinking?!\n* The implementation was not nearly idiomatic enough to fit in with the rest of the library. (Aside: I learned JavaScript by reading straight through the uncommented Prototype library. It was painful, but it left me with an enduring taste for JavaScript's elegance. If you've ever memorized a poem to figure out what it really means, you know the feeling. I should have striven for the same elegance, the same "rightness," when I was writing my patch.)\n* There were requests for benchmarks. Frankly, I should have ignored them. Optimization can always come later if the code is worth using. A request for a benchmark is a sign of a deeper problem: it means the code is too complicated to be evaluated analytically, so empirical results are needed.\nThe way I see it, the 4060 version of the patch is dead. [[Dean Edwards|http://dean.edwards.name]] has a fascinating, albeit somewhat different approach to the same problem, and he certainly has more traction in the community than I do. I have learned a lot from him and his code.\n\nBut what exactly killed the patch? Was it my blindness, or the superiority of other solutions? I do not deny those factors, but I think my biggest mistake was submitting the update as a patch, rather than just blogging about it somewhere. I should have billed it as a research project, rather than giving the arrogant (and unintended!) impression that it should be included in the library right away. That's the trouble with bug trackers. Big changes founder and rot. You find yourself avoiding commenting the code, lest there be complaints about its size. You feel reluctant to toss in experimental features, since they may be mistaken as part of the core. Even your [[unit tests|http://groupspace.org/devben/proto-minimal/test/unit/class.html]] become difficult to grasp in a glance.\n\nThis blog represents a revamped effort at evangelizing my ideas. The [[code|prototype/dist/prototype.js]], which has been rewritten from scratch, lives here now. Thanks for stopping by.
ad-hoc avant-wiki for insta-ideation
Seraph.im
/***\nPlace your custom CSS here\n***/\n/*{{{*/\nbody {\nfont-family: georgia, verdana, sans-serif;\n}\n.headerShadow {\nvisibility: hidden;\n}\n#mainMenu {\nwidth: 250px;\n}\n#displayArea {\nmargin: 1em 17em 0em 260px;\n}\n/*}}}*/\n