Moshe
Moshe

Reputation: 5159

javascript scope and global variables

I'm trying to avoid using global variable when using functions within objects. I want to invoke a function inside other function and use a variable from the first function's scope. For example:

var showForecast = {
  'init': function () {
    this.getData();
  },
  'buildView': function(){
    var code = "Hey, you're from " + this.data.city;
    $('body').append(code);
  },
  'getData': function () {
    $.getJSON('http://ipinfo.io/', function (data) {
      console.log(data);
      showForecast.buildView();
    })
  }
}

Clearly it's not working. I want to use data inside buildView without making data a global variable. I thought using this would be the right course of action because I'm calling buildView from a function where data is defined. How can this be achieved? Thanks.

Upvotes: 0

Views: 812

Answers (4)

Thangaraja
Thangaraja

Reputation: 956

As you are trying to avoid global, you should consider using namespaces. There is no such thing called namespace in Javascript. But you can define yourself using small utility method mentioned here.

http://www.zachleat.com/web/namespacing-outside-of-the-yahoo-namespace/

A utility method which helps creating custom namespaces.

jQuery.namespace = function() {
    var a=arguments, o=null, i, j, d;
    for (i=0; i<a.length; i=i+1) {
        d=a[i].split(".");
        o=window;
        for (j=0; j<d.length; j=j+1) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }
    return o;
};

Define name space

jQuery.namespace( 'jQuery.showForecast' );    

Define methods using revealing module pattern

https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript

 jQuery.showForecast = (function() {

  var data;

  var init = function() { 
    getData();
  }

  var buildView = function() {
    var code = "Hey, you're from " + data.city;
    $('body').append(code);
  }

  var getData = function() {        
    $.getJSON('http://ipinfo.io/', function(_data) {
      console.log(data);
      data = _data;
      buildView();
    })

  }

  return {
    init: init
  };

})(); // Execute it immediately

Usage:

You can access only init method as it is exposed to outside.

jQuery.showForecast.init()

Define another namespace

jQuery.namespace( 'jQuery.showForecast.extended' );

jQuery.showForecast.extended = {
// Define some more

};

Upvotes: 0

D--
D--

Reputation: 86

I like Vinny's answer.

One round-bout way is to make a module out of it:

var showForecast = function(){
    var data;
    var init = function () {
        this.getData();
    };
    var buildView = function(){
        var code = 'Hey, you\'re from ' + this.data.city;
        $('body').append(code);
    };
    var getData = function () {
        $.getJSON('http://ipinfo.io/', function (data) {
            console.log(data);
            this.data = data;
            showForecast.buildView();
        })
    };
    return {
        'init': init,
        'buildView': buildView,
        'getData': getData
    };
}();

This way the scope of var data is limited to the function. It's like a private variable.

Upvotes: 1

Vinny M
Vinny M

Reputation: 770

You can pass the information along:

var showForecast = {
'init': function () {
    this.getData();
},
'buildView': function(data){
    var code = 'Hey, you\'re from ' + data.city;
    $('body').append(code);
},
'getData': function () {
    $.getJSON('http://ipinfo.io/', function (data) {
        console.log(data);
        showForecast.buildView(data);
    })
}

}

Upvotes: 2

Quentin
Quentin

Reputation: 944455

There is no way to access the data variable itself. That is locally scoped to the anonymous function you pass to getJSON (and getJSON passes it as an argument, which is beyond your control).

You have to copy the value somewhere.

In your particular example, there are no scopes shared between getData and buildView other than the global scope. So if you want to pass the value through scopes, then a global is your own (terrible) option.

You can simply pass it as an argument:

showForecast.buildView(data);

Or you can store it as a property:

showForecast.myData = data;

Upvotes: 1

Related Questions