High Performance JavaScript
This is a very nice book with tons of interesting tips and tricks.
JavaScript: сделаем Scheme или что-то похожее
From now on, everything about that is in russian and about JavaScript will go there. Boom.
Another way to restore built-in method in Chrome/Safari
A short follow-up to this post. Apparently, both Chrome and Safari restore built-in method if you try to delete it from the prototype. In other words:
Array.prototype.map = null;
[].map
> null
delete Array.prototype.map
[].map
> function map() { [native code] }
Quick JavaScript profiling
The text below was posted on my old blog in August, 2008. Since I still use this technique, I think it is worth moving it here.
Sometimes, you need to know how many times a function was executed and how much time did it take. It is not a problem if you have good profiling tools, but sometimes they are not available. A few months ago I was optimizing my code in some very early Fx3 build and the tools I use were not ready yet (Firebug was too buggy to use and Venkman didn’t work at all). So I was on my own. Fortunately, in javascript, you can monkey patch already created functions and insert your profiling code there:
function p_register(func_name) {
var func = window[func_name];
var h_name = "__" + func_name + "__";
window[h_name] = func;
window[func_name] = function() {
// start profiling
var result = window[h_name].apply(window, arguments);
// store profiling results
return result;
};
}
In the code above, I’ve copied the function and replaced the original with a new one which contains my profiling code and a call to the hidden copy. So, every call to a function I wanted to profile was counted. As for the bottleneck, it was about prototype.js, its `Element.hasClassName` method and elements extension: it was too expensive to use them with a lot of elements, so I wrote a tiny func which acted just as `.hasClassName` but without element extension.
On the modified prototypes in JavaScript
I wrote a small function that can be used to retrieve links to the unmodified built-in methods (useful when you are running code in the environment where built-in objects’ prototypes were modified).
var getbuiltin = function(obj, fn) {
var iframe, func;
iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
func = iframe.contentWindow[obj].prototype[fn];
document.body.removeChild(iframe);
return func;
};
/* Example: */
Array.prototype.push = function() {
this[this.length] = 'B';
};
var A = new Array();
A.push('A'); /* ['B'] */
var B = new Array();
B.push = getbuiltin('Array', 'push');
B.push('A'); /* ['A'] */
It works in Chrome, Safari and Firefox but not in Internet Explorer. There, you can’t access any JavaScript objects via frame’s window object. In other words, iframe.contentWindow.Array is undefined.
Puzzled.
Update: Thanks to Sanjar, aforementioned getbuiltin.js works in IE now.
High-order map implementation in JavaScript
I was writing simple JavaScript the other day and, to make sure that my code works in all browsers, instead of using Array.map, I wrote a trivial map implementation myself. Then, just out of curiosity, I wrote two more implementations and posted them on StackOverflow to see what other fellow programmers think.
Here’s my original implementation, pretty simple:
var map = function(arr, func) {
var newarr = [];
for (var i = 0; i < arr.length; i++) {
newarr[i] = func(arr[i]);
}
return newarr;
};
My second implementation is my favorite, in terms of style, but I am not going to use it anytime soon because, although such approach is adequate and fast in such languages as Scheme, it is terribly slow without tail call optimization. Plus, as mentioned in the StackOverflow thread, concat function is too expensive to use it here.
var map1 = function(arr, func) {
if (arr.length === 0) return [];
return [func(arr[0])].concat(map1(arr.slice(1), func));
};
In my third implementation I used iterative recursion; it has no real advantages over the first method.
var map2 = function(arr, func) {
var iter = function(result, i) {
if (i === arr.length) return result;
result.push(func(arr[i]));
return iter(result, i+1);
};
return iter([], 0);
};
I received a few code samples after I posted my code chunks on the StackOverflow and below are interesting things I noticed.
What if obj.length property is not a positive number?
The code in SpiderMonkey makes sure that the obj.length is a positive number with bit manipulation. And although this works with strings and objects, it fails if value is a negative number.
"string" >>> 0; // 0
var obj = {}; obj >>> 0; // 0
-1 >>> 0; // 4294967295
So with SpiderMonkey approach, in case of negative obj.length value, we will initialize a huge array and try to iterate over it.
What about this inside a callback function?
My implementations don’t care about this object and this is probably a bad thing. The code from mikesamuel uses an empty object while the code in SpiderMonkey provides an optional parameter for this.
jQuery, as we see, does not care about that either.
Why jQuery flattens resulting array?
Here’s the line from jQuery source code that flattens result before returning it.
return ret.concat.apply( [], ret );
I don’t know why they do that but it may cause unexpected results if you want to generate an array of arrays.
$.map([1,2,3], function(i) { return [i, i + 1]; });
// Expected: [ [1,2], [2,3], [3,4] ]
// Returned: [1, 2, 2, 3, 3, 4]
My latest version
With all the knowledge I got from the StackOverflow thread mentioned above I wrote another version of map implementation.
var map = function(arr, func /*, context */) {
if (typeof(arr.length) != 'number' || arr.length < 0) {
throw new TypeError();
}
if (typeof(func) != 'function') {
throw new TypeError();
}
var mapped = new Array(arr.length);
var context = (arguments.length > 2 ? arguments[2] : {});
for (var i = 0; i < arr.length; i++) {
mapped[i] = func.call(context, arr[i]);
}
return mapped;
};
The code (with some trivial tests) is also on my github page.
Update: Jesse Farmer reminded that map is a special case of inject and thus can be implemented as a call to the latter. His code: http://gist.github.com/208582.
Improving the Footnotes
The text below was posted on my old blog in August, 2008. Since I still use this technique, I think it is worth moving it here.
The New Oxford American Dictionary describes a footnote as “an ancillary piece of information printed at the bottom of a page”. In print, such implementation is okay because the only thing you have to do is to glance down to the bottom of the current page and read the note. But on the web, such implementation doesn’t work well. It forces you to click on the note reference, read the note and then click on the back link (or hit your browser’s back button) to get back to where you were. To adapt the footnotes to the web, we should make it possible to read a note without any keyboard or mouse actions if its reference is visible in the viewport.
Fortunately, it is easy to implement with JavaScript and CSS. To show how it works I have created a small proof-of-concept page: footnotes.html. It was tested only in Firefox 3 so it may be broken in other browsers (actually, I think it will be broken because I used some fx-only javascript features). But it should be no problem to fix it for other major browsers. And don’t forget that we are on the web: instead of plain text, you can use images, maps and various web gadgets.