Skip navigation.

exploreopera

| Help

Sign up | Help

Posts tagged with "ecmascript"

func5000 - how far does your UA get?

,

Can your browser handle a call depth of 5000 functions? Look here and see how far you get with various JS engines:

func5000.htm

I'd really like to know if there is a hard-coded limitation or if it depends on system speed and memory, for example. Discussion is open in the comments :smile:

new adventures in date parsing

, , ,

Besides the famous getYear ECMAScript date parsing is so badly specified that it is full of quirks. Opera has a good share of them..

Some random testing in IE7, Firefox 2 and Opera 9 shows that a simple new Date(somestring) dishes up several surprises:

  • Opera seems to be the only one that tries to parse European date formats, though it defaults to U.S. format for ambiguous dates. Hence, "1/2/2008" is January 2nd while "30/10/2008" is October 30th in Opera.
  • Greater than expected values for a specific part increases the whole date value in IE and Firefox. In other words, passing in January 32nd returns February first.
  • ..but this is more thoroughly implemented in Firefox than IE, "1/1/2008 23:59:60" gives you January 2nd in the former and NaN in the latter.
  • Opera defaults to values from current timestamp on computer for any input that doesn't make sense. In other words, if you pass in January 32nd on April 11th, January 11th is not-quite-expected outcome..
  • The IE and Firefox parsers support both m-d-y and y-m-d formats. So if the input is ambiguous, how do they know which is which? Elementary, dear Watson: if the first integer is greater than 70 it is assumed to be a year. If it is less than 70 it is assumed to be a month. "69-1-1" is obviously a 1906 date while "70-1-1" is new year's day 1970. Ah, the magic of arbitrary heuristics.
  • Speaking of the magic number "70", why can Firefox parse "1 january 70" but says "invalid date" for "1 january 69"??
  • ..and why does Opera refuse to recognise single digits as valid years, just substituting the current year instead? Opera, pretending "1 january 9" is today's date is NOT the correct fix for those Y2K bugs.. :cry:
  • Finally, a quirk I'll leave my readers to ponder the inner workings of: if I tell Opera to parse "january 100 2007" on this very day, April 11th 2007 I get "Thu, 11 Jan 2007", which is consistent with the observation that it replaces out-of-range values with current values. Now, who can explain what Opera does if you parse "january 101 2007"?


Executive summary: good thing we're writing browsers and not time machines!

interesting jQuery stuff

, , ,

The other day (well, night actually) I was looking at a problem on a site that used the jQuery library. Though I've heard of it, I haven't seen jQuery on live sites before and I thought it shows off some quite interesting features of JavaScript.

The latest version of the library is available so you can see what I'm talking about.

A function can call itself recursively as constructor

Just inside the jQuery function we find:

if ( window == this )
return new jQuery(a,c);


This clever little trick means you don't need to use the "new" keyword all over your code to create a new jQuery object. You can just do
var obj=jQuery()
and the this check will detect that it wasn't called as a constructor and call itself recursively, this time using the "new" keyword to define the expected jQuery object.

I have a question about the if clause though - it presumably means trouble if you change the "this" object and use something else than window. Say,
var obj = jQuery.call(document)
would probably break something. I don't know jQuery well enough to tell if that would be a problem, but the check could perhaps be
if(this.constructor!=arguments.callee)
to catch all cases?

Using || operator to default to an argument inside a function call

You may be used to seeing the || operator being used to provide a default value for a variable. For example
var username=prompt('Your name please')||'anonymous'
will set the variable to "anonymous" if the user cancels the prompt or doesn't type any value.

It still took me a while to understand what is going on here:

return this.setArray(
// HANDLE: $(array)
a.constructor == Array && a ||

// HANDLE: $(arraylike)
// Watch for when an array-like object is passed as the selector
(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||

// HANDLE: $(*)
[ a ] );


The basic idea is: if the object a is an array, we pass it to the function directly. If it is an object that is somewhat similar to an array (for example a NodeList), we pass the result of calling the jQuery.makeArray function which will take all the elements/nodes and add them to an actual array. Otherwise, we just pass an array containing nothing but the object a.

The key to understanding this is to understand that the statement
a.constructor == Array && a
actually "returns" a if the first comparison is true. It is probably better phrased as "evaluates to a" but it may be easier explained as "returns".

Similarly,
(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a )
will go through all the conditions, and if they all hold true return the result of calling the makeArray function.

The || operator ties it all together and ensures the first clause that evaluates to something is chosen as input to the function.

You can use Array.push to add array elements to any object

The next interesting snippet is inside the setArray function itself:
setArray: function( a ) {
this.length = 0;
[].push.apply( this, a );
return this;
}


Look at [].push.apply( this, a );. What happens here is that the array literal's "push" method is used to push element a onto the this object which isn't an array but a jQuery object. That's right, push is generic and can be used with any object. It means the object gets a ".length" property like an array has and can be iterated with for loops. Example:

var obj = new Object(); /* Yes: an object, not an array. */
var str='';
Array.prototype.push.call(obj, 'Hello');
Array.prototype.push.call(obj, ' World');

for(var i=0;i<obj.length;i++){
str+=obj[i];
}
alert(str);


Array methods being generic is part of the amazing flexibility of JavaScript, but this feature of the language might not be well known among authors.

About the jQuery code, I wonder if their approach of creating an empty array literal to use its push method is slower or faster than my Array.prototype lookup. Perhaps the question will inspire a reader to do some performance testing?

ECMAScript's FunctionDeclaration versus FunctionExpression

, ,

In ECMAScript, functions can be defined by a "FunctionDeclaration" or a "FunctionExpression". Basically, the difference is that a FunctionDeclaration includes a function name so that it can be called from other functions - it is defined as:

function Identifier ( FormalParameterListopt ){ FunctionBody }

while a FunctionExpression is the anonymous function - note in the definition that the "Identifier" part is marked as optional:

function Identifieropt ( FormalParameterListopt ){ FunctionBody }

However, including an identifier is still allowed. The identifier can be used inside the function to refer to itself, but not outside it.

This may confuse authors if we forget that whether something is a FunctionDeclaration or a FunctionExpression depends on the context. The right hand side of an assignment or the arguments of a function call will be a FunctionExpression, in other words behave like an anonymous function. For example

var a=function b(){return 'Hi'}; alert(b());


fails: there is no function named b there. alert(a()) would work. Likewise,

setTimeout( function a(){}, 50 ); a();


fails because the argument is evaluated as a FunctionExpression and after the setTimeout() call no function named "a" exists. (IE seems to violate/extend the standard here!)

It can be confusing that a FunctionExpression is allowed to look exactly like a FunctionDeclaration but behaves differently, and I don't really see a strong use case since arguments.callee exists.

quirky arguments

, , ,

The humble "arguments" variable that always exists inside a function call in JavaScript has demonstrated some unexpected behaviour today. This makes me really puzzled because I can not find anything in the ECMA-262 specification that mandates the behaviour - yet all browsers do the same thing in a very obscure corner of their implementations.. Why do we all implement it that way if we weren't explicitly instructed to by the specification?

Here is the issue in a minimal snippet of JavaScript:

function test(){}

test.arguments='something';

test();

alert(test.arguments); // test.arguments is now null ??


so..? Inside the function body we have a local variable called "arguments", but this variable also becomes a property of the function itself, and is subsequently reset to null. Consider

function test(){ alert(arguments==test.arguments); }

which says "true" and shows us that the local variable and the function property refer to the same object.

Some side-effects of this implementation include:

  • "arguments" is a reserved word as function property name, it can not be used
  • We can work out whether a function has been called by a script or not! Consider
    Function.prototype.arguments=true;
    function test(){};
    .
    . /* function definitions and calls here */
    .
    var testWasCalled = ! test.arguments;

    "testWasCalled" is now true if and only if the script has used the "test" function during its execution. Might be a handy trick for trying to optimise large scripts :wink:


..but where in the spec is this specified? In other words, is it a bug or a feature?

spot a subtle JS error here..

, , ,

The other day I ran into weird errors while editing the Y!Mail patch in browser.js. The whole thing stopped working, and it took me a while to see the problem. Can you see it?

// need to fake someMethod
Element.prototype.someMethod=function(){
    // faking someMethod here
}
// we also have an insertBefore problem so let's fix that too
(function(oF){
    // tweaking insertBefore here
})(Element.prototype.insertBefore);


What was wrong? Hats off if you can see it without running the code.. :smile:

Y!RegExp quiz

, , , ...

This snippet is brought to you by Yahoo!Japan blogs. Quiz of the day: what is the RegExp meant to do and why does it fail?

if(img.match(/(.[jJ][pP][eE]?[gG]|.[gG][iI][fF]|.[pP][nN][gG])/)) {
return true;
}
return false;


(Yes, the variable name is a hint.)

eval ... everywhere?

, , , ...

Harvested from an Opera bug report, here is another interesting surprise from Gecko internals: Object.prototype.eval . It's like a normal eval() but in the scope of that object. In other words, doing

document.body.eval('tagName');


is practically the same as doing

with(document.body){eval('tagName');}


(Before you ask, the best practice would be to say either
document.body.tagName
or
document.body['tagName']
Using eval should be avoided and generally can be avoided without problems. So don't let me catch you using any of the two first snippets on a production site!)

So hey Gecko, where did this come from? Is it something we should copy and implement or something we should scorn and point fingers at?