ECMAScript Quiz
I have learn a little bit with ECMAScript scope these days and I want to share it with you. I am programming with Flash and there is a lot of concurrency problems that need to be resolved. Closure are good tools for solving some of these issues in an elegant way.
The game here is to guess the result of each script that stands in a simple HTML page. This will also works with Flash, the output will be certainly the same:
Question 1
funcs = Array();
strings = Array("hello","world");
for(var i=0;i<strings.length;i++)
{
var str=strings[i];
funcs[i]=function(){alert(str);}
}
for(var i=0;i<strings.length;i++)
{
funcs[i]();
}
This code outputs 2 times the same string : "world". It's because the anonymous function copy the reference on the variable "str" but not copy is value. A elegant solution to this problem is to use a closure maker. The function "alertMaker" receive the "str" string by copy, and the anonymous function capture this argument in this scope.
function alertMaker(str){
return function(){alert(str)}
}
for(var i=strings.length-1;i>=0;i--){
funcs[i]=alertMaker(strings[i]);
}
for(var i=funcs.length-1;i>=0;i--){
funcs[i]();
}
Question 2
var obj = {h:"hello",w:"world",f1:function(){
alert(this.h);
var f2 = function(){
alert(this.w);
}
f2();
}
};
obj.f1();
This one outputs the "hello" string but fails to display the "world" string. It's because in the f2 scope
the this keyword is not a reference on obj anymore. The this keyword
available in the f2 scope is now a reference on f1. To get this script working as wanted
you have to write it this way:
var obj = {h:"hello",w:"world",f1:function(){
alert(this.h);
f1Parent = this;
var f2 = function(){
alert(f1Parent.w);
}
f2();
}
};
obj.f1();
Question 3
window.world = function world(){ alert("BUZZ!")};
Function.prototype.world=function(){alert('world')};
Function.prototype.hello1=function(){alert('hello'); this.world();};
Function.prototype.hello2=function (){alert('hello');}
function helloWorld1(){arguments.callee.hello1();}
function helloWorld2(){arguments.callee.hello2(); this.world();}
helloWorld1();
helloWorld2();
"helloWorld1" outputs two strings : "hello" and "world". helloWorld2
outputs two strings again but "hello" and "BUZZ!".
- The call of
worldinhello1uses the "Function.prototype.world" function because the caller is the "helloWorld1" itself. - The call of
worldinhelloWorld2uses "window.world" because the caller is the rootwindow Object.