July 16th, 2008 by kangax
One of the easiest ways to inspect an object is to type convert it to a string:
function foo() {
return 'foo';
}
foo + ''; // "function foo(){ return 'foo'; }"
// or
foo.toString();
// or
String(foo);
When using Class.create from prototype.js, I often get annoyed by a meaningless result of constructor’s toString:
var Person = Class.create({
initialize: function(name) {
this.name = name;
},
speak: function(msg) {
return this.name + ' says: ' + msg;
}
})
Person + ''; // "function klass() { this.initialize.apply(this, arguments); }"
var Employee = Class.create(Person, {
initialize: function($super, dept) {
$super();
this.dept = dept;
}
})
Employee + ''; // "function klass() { this.initialize.apply(this, arguments); }"
As you can see, constructor’s default toString tells little about what’s going on under the hood. In fact, Class.create returns a generic constructor-function which only calls initialize method of a prototype. In other words it acts as a proxy. One of the ways to see actual constructor’s code is to inspect “initialize” method directly:
// nothing new here
Person + ''; // "function klass() { this.initialize.apply(this, arguments); }"
// and the actual code
Person.prototype.initialize + ''; // "function (name) { this.name = name; }"
This is quite verbose, don’t you think? Let’s monkey patch Class.create a little:
Class.create = (function(original) {
var fn = function() {
var result = original.apply(null, arguments);
result.toString = function() { return result.prototype.initialize.toString() };
return result;
};
fn.toString = function(){ return original.toString() };
return fn;
})(Class.create);
We simply redefine original Class.create with an “enhanced” version. New version “binds” toString of prototype.initialize as toString of constructor-function. As a bonus, it also takes care of a “wrapped” Class.create.toString itself - trying to be as unobtrusive as possible:
// monkey-patch Class.create first...
// Person.toString now yields much more informative result
Person + ''; // "function (name) { this.name = name; }"
// Class.create code seems to be unchanged
Class.create + ''; // outputs actual Class.create code
April 29th, 2008 by kangax
Max and I have just updated Scripteka to version 1.1
In case you haven’t been following things around here, Scripteka is the prototype extensions library - a convenient place to find, rate and submit prototype.js based goodness.
The library still looks and feels the same, since our main goal was to improve overall usability and functionality.
Some of the new features include:
- User Dashboard, where one can see all their scripts, and edit them.
- Image upload for scripts. Finally, you can replace that default “empty page” image with your own.
- Improved RSS feed with more useful information, and better formatting.
- Improved communication and response to various actions. This includes:
- Automated, token-based “claim script” feature. No more claiming script and not knowing what to do next.
- More robust and informative feedback on script approval/rejection.
- Small usability improvements.
- Many bug fixes, polishing.
January 30th, 2008 by kangax
Enumerable#inject is one scary method. It took me a while to understand the beauty of this functional programming paradigm. Just in case you were ever wondering about its real life usage - here’s one of them.
We all know that using namespaces is generally a wise thing to do. You don’t have to create a multi level object structure, but nesting your code under a pseudo-namespace is what makes maintenance, upgrades and migrations somewhat easier and error-prone.
Few popular javascript libraries implement some sort of a utility function that automatically creates nested objects. What it means is that instead of writing it manually, you could let the helper handle it for you:
// creating nested structure manually is painful
var com = {
thinkweb2: {
projects: {
_prototype: 'a deeply nested property...'
}
}
}
com.thinkweb2.projects._prototype // => 'a deeply nested property...'
Well, it appears that inject can handle such task in only 3 lines of code:
'com.thinkweb2.projects.prototype'.split('.').inject(window, function(parent, child) {
var o = parent[child] = { }; return o;
})
Object.inspect(com.thinkweb2.projects.prototype) // => "[object Object]"
Not bad - a completely generic namespacing snippet.
What’s happening here is not a black magic. Inject accepts initial object as a first argument (in this case - window object) and iterator function as the second argument. It then calls iterator function on each item from the collection (in our case it’s simply a string split into an array). Iterator function receives accumulated result as a first argument and current value as a second. Iterator performs an action (creates a nested object) and returns accumulated result (the newly created parent object) to be used in following iterations.
Alternatively, we could wrap this all nicely and define as a String.prototype method:
String.prototype.namespace = function(separator){
this.split(separator || '.').inject(window, function(parent, child) {
var o = parent[child] = { }; return o;
})
}
foo.bar.baz.quux = 'Nothing special...'; // => ERROR: foo is not defined
// Default separator is '.'
'foo.bar.baz.quux'.namespace();
foo.bar.baz.quux = 'Nothing special...'; // => "Nothing special..."
// Or using a custom one
'MY_AWESOME_APPLICATION::util::DOM::dimensions'.namespace('::');
'dimensions' in MY_AWESOME_APPLICATION.util.DOM; // => true
Note how we use “separator” argument (or defaulting to ‘.’) making the function somewhat more flexible.
P.S.
Heavy namespacing could lead to unnecessary complexity.
Lack of it - to buggy behavior.
In the end, the only thing that matters is what works best for you.
Enjoy!
January 22nd, 2008 by kangax

A long awaited Prototype cheat sheet - a full reference to a bleeding edge 1.6.0.2 is finally here. I had no experience creating something like this before, so any bugs or suggestions are very much appreciated. Couple of notes about notations:
- Modules are sorted in a somewhat logical order - those commonly used are mostly in the left/center area, while deprecated/utility methods are all the way to the right
- Method can be recognized by parentheses following it (anything that doesn’t have ones is a property)
- Deprecated items are marked red and have NO parentheses/arguments specified
- Prototype extends quite few native objects’ prototypes with a set of convenient methods. In such cases there’s an explicit note about it next to a module name - i.g.
stripScripts() method from “String (String.prototype)” can be called as 'foo'.stripScripts()
- When a module is also a class, there’s a “(constructor)” note next to it - i.g. “Hash (constructor)” means that it should be called as
new Hash()
- There are few bonus items (such as those from Prototype.Browser) which are not yet included in documentation
Download (12564 downloads) and Enjoy!
Update:
I have managed to choose the most retarded format for the cheat sheet - almost squared - which was impossible to print or navigate. Sincere apologies. There is an updated version at the same address which also fixes few other annoyances:
- Ajax.Responders is now a separate section
- Added missing Event.fire
- Added Prototype.BrowserFeatures.XPath
- Added simple “Dimensions/Offsets” diagram
- Minor rearrangements
Update 2:
Uploaded a Higher Contrast version (3083 downloads)
Seems to look much better when printed.