I'm wondering on a try to convert all lower case items in an Array into upper case.
I was evaluating some variants and checked the performance where the .call()variant was the fastest one (http://jsperf.com/uppercaseperformance)
Used the following code which is working well in Firefox (32.0.1):
console.log(String.toUpperCase.call(null,['a', 'lower', 'case', 'string', 'array']))
Outputs in Firefox only:
A,LOWER,CASE,STRING,ARRAY <- Thats nice ;)
Same code isn't working in Chrome, Safari, node.js etc. Chrome/Safari outputs:
TypeError: Cannot read property 'call' of undefined
And node.js outputs:
TypeError: Cannot call method 'call' of undefined
Is .call used in a wrong context here maybe?
The Browser Compatibility on MDN website shows .call()should be supported.
The String generic methods are not part of the ECMAScript standard, they're only shipped in Firefox.
You can shim it for all other browsers (copied directly from MDN):
/*globals define*/
// Assumes all supplied String instance methods already present
// (one may use shims for these if not available)
(function () {
'use strict';
var i,
// We could also build the array of methods with the following, but the
// getOwnPropertyNames() method is non-shimable:
// Object.getOwnPropertyNames(String).filter(function (methodName)
// {return typeof String[methodName] === 'function'});
methods = [
'quote', 'substring', 'toLowerCase', 'toUpperCase', 'charAt',
'charCodeAt', 'indexOf', 'lastIndexOf', 'startsWith', 'endsWith',
'trim', 'trimLeft', 'trimRight', 'toLocaleLowerCase',
'toLocaleUpperCase', 'localeCompare', 'match', 'search',
'replace', 'split', 'substr', 'concat', 'slice'
],
methodCount = methods.length,
assignStringGeneric = function (methodName) {
var method = String.prototype[methodName];
String[methodName] = function (arg1) {
return method.apply(arg1, Array.prototype.slice.call(arguments, 1));
};
};
for (i = 0; i < methodCount; i++) {
assignStringGeneric(methods[i]);
}
}());
Note that using the shim above just adds some abstraction on top of this:
String.prototype.toUpperCase.apply(['a', 'lower', 'case', 'string', 'array']);
// "A,LOWER,CASE,STRING,ARRAY"
The interesting part here is that an array is being used as the this value for a String method.
According to the spec (§ 15.5.4.18 -> § 15.5.4.16 step 2), the this binding is coerced to a string through the internal ToString algorithm, which for objects (note that arrays are objects) invokes the internal ToPrimitive algorithm, which in turn calls the array's toString method, which in turn calls the array's join method with an empty arguments list.
That is, calling .toUpperCase() passing an array as the this binding will first implicitly coerce this to a string:
['a', 'b'].toString() === ['a', 'b'].join();
['a', 'b'].join() === 'a,b';
Then toUpperCase will map this string's characters to their equivalent uppercase characters. Therefore:
String.prototype.toUpperCase.apply(['a', 'b']) === ['a', 'b'].join().toUpperCase();
This appears to be a peculiarity in Firefox, more of a hidden feature than a bug though.
toUpperCase is actually, by the standards, only available as a prototype method of String, and as such should be invoked on a valid instance. You could of course try:
String.prototype.toUpperCase.call(null,['a', 'lower', 'case', 'string', 'array'])
Which, in Chrome, obviously returns:
TypeError: String.prototype.toUpperCase called on null or undefined
As invoking call(null, ...) on it will invoke the instance method with an empty this reference. The static invocation style you are attempting is only implemented in Firefox and not part of any current ECMAScript standard.
Try this String.prototype.toUpperCase.call(['a', 'lower', 'case', 'string', 'array'])