views:

591

answers:

2

I'm unable to use native code functions as JavaScript objects in WebKit-based browsers. Is it impossible to alias these functions directly?

This is easiest to explain by example, so here is what I am running in the Developer Tools console:

console.warn;
// Outputs:
// function warn() {
//     [native code]
// }

console.warn("console.warn");
// Outputs: "console.warn"

var _c = console;
_c.warn("_c.warn");
// Outputs: "_c.warn"

var _w = console.warn;
_w("_w");
// Outputs: "TypeError: Type error" on Safari/WebKit (Mac)
// Outputs: "TypeError: Illegal invocation" on Chrome (Mac)

var _w2 = function () { console.warn.apply(console, arguments); }
_w2("_w2");
// Outputs: "w2"

This issue came up as I tried to use jQuery Lint in Safari; it uses the following approach to prevent breakage if window.console does not exist:

_console = {
    warn: window.console && console.warn || function(){},
    ...
}

_console.warn("some error");

Here's my temporary workaround:

if((jQuery.browser.safari || jQuery.browser.webkit) && window.console) {
    jQuery.LINT.level = 3;
    jQuery.LINT.console = {
        warn: function() { console.warn.apply(console, arguments); },
        group: function() { console.group.apply(console, arguments); },
        groupEnd: function() { console.groupEnd(); },
        groupCollapsed: function() { console.group.apply(console, arguments); },
        log: function() { console.log.apply(console, arguments); }
    }
}
+2  A: 

That should be:

console.warn.apply(console,arguments);

The first parameter to apply is the value of this to pass into the warn method.

slebetman
Thanks; I've updated my question to correct this oversight. It solves the majority of the issues I was having, but I'll hold off a bit longer to see whether anyone has some good info about native functions in WebKit.
PCheese
+5  A: 

You can't alias any methods in JavaScript, native or not, WebKit or not.

When you say var _w = console.warn;, you are stripping warn away from its owner object console and treating it as a standalone function. When you call it, it gets no this reference to the console, so it fails to work.

You may find this unusual from how bound methods work in other languages, but that's just how JavaScript method calls are designed: the this reference is passed to a function based solely on what owner is in owner.method() (or owner['method']()). If the function is called alone without an owner, this is set to the window object and the method will most likely fall over.

To get around this and pass in a proper this, you must either use method.call (or method.apply) explicitly as described by @slebetman, or make your own bound function using a closure like var _w= function() { console.warn.apply(console, arguments) }; or, in ECMAScript Fifth Edition, method.bind(owner).

bobince
Oddly, newer versions of Firebug don't mind if you alias `console`'s methods.
J-P
Well okay, I lied a bit. :-) Of course it depends on the implementation; it is naturally possible to make a function that can be called back without caring what its `this` is, in which case aliasing the method into a variable will work... although clearly it's not something you can rely on. (Further, for non-native ‘host objects’ provided by the browser all bets are off.)
bobince