Looping & Closure

Here is simple code to dynamically create set of buttons that greet in different languages.

document.body.onload =  createButtons;
function createButtons() {
    var greetings = [
        {lang:'English', msg:'Good Morning !!'},
        {lang:'German', msg:'Guten Morgen !!'},
        {lang:'French', msg:'Bonjour !!'},
        {lang:'Finnish', msg:'Huomenta !!'}
    ];
    document.createElement('h1');
    console.log(greetings);
    for(var i=0;i<greetings.length; i++){
        var greet = greetings[i]
        var input = document.createElement("button");
        input.appendChild(document.createTextNode(greet.lang));
        input.setAttribute('class','btn btn-default btn-lg');
        input.onclick = function(event){
            alert(greet.msg);
        };
        document.body.appendChild(input);
    }
}

Pretty simple, looping through array of objects and adding buttons with language label and greet message in respective language as alert dialog on click action.
Here is the final html looks like

Multi-language greeting buttons

Many of you guys might already have understood the issue with above code. One can easily notice that all button greet you in ‘Finnish’.

Miss-matched greet message

Why ?
It is all because of onclick function has become ‘closure‘.

input.onclick = function(event){
            alert(greet.msg);
};

It carries all the local variable that are in scope, here ‘greet’ object . As the value of ‘greet’ object change in every loop, all references in onclick callback closures also get same reference. So final reference in all the callback will be the last value of the greet in the loop.
I need not explains more about this typical problem, here is complete explanation available .

How to solve ?
One way to fix this issue by using functional scope. Lets wrap the callback function into another immediately invoked function with message as parameter. In this self invoked wrapper function with message as parameter, the callback function gets bind to wrapper function parameter , not the outside looping variable.

input.onclick = (function(msg){return function(event){
            alert(msg);
}})(greet.msg);

ES6 Way
ES6 has brought cleaner solution for this issue, let statement.

The let statement declares a block scope local variable, optionally
initializing it to a value.

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
In our script, we just need to use let statement instate of var while storing array object.

for(var i=0;i<greetings.length; i++){
        /*Using let instate of var*/
        let greet = greetings[i];
        var input = document.createElement("button");
        input.appendChild(document.createTextNode(greet.lang));
        input.setAttribute('class','btn btn-default btn-lg');
        input.onclick = function(event){
            alert(greet.msg);
        };
        document.body.appendChild(input);
  }

So all our greet button are started displaying messages in there respective languages

enter image description here

Here is the complete code snippet http://jsbin.com/xekexij/edit?html,output

Comments

  1. You can get all Latest Informational Blogs at Technology. So you can read and share with family and friends.

    ReplyDelete
  2. Wildscapes is a beautiful and realistic landscape mod for Android that gives your phone a unique look. The app is free to download and use, and it comes with a variety of different landscapes to choose from. You can also add your own landscapes if you want to create a truly unique experience. For more Info visit us at https://apkbattle.com/wildscapes-mod-apk/

    ReplyDelete

Post a Comment

Popular posts from this blog

State Design Pattern by Example

Eclipse command framework core expression: Property tester

Composite Design Pattern by example