views:

134

answers:

3

I'm working on a jQuery plugin, but am having some trouble getting my variables properly scoped. Here's an example from my code:

(function($) {

$.fn.ksana = function(userOptions) {
    var o = $.extend({}, $.fn.ksana.defaultOptions, userOptions);

    return this.each(function() {
        alert(rotate()); // o is not defined
    });
};

function rotate() {
    return Math.round(o.negRot + (Math.random() * (o.posRot - o.negRot)));
};

$.fn.ksana.defaultOptions = {
    negRot: -20,
    posRot: 20
};

})(jQuery);

I'm trying to get the private function rotate to be able to see the o variable, but it just keeps alerting 'o is not defined'. I'm not sure what I'm doing wrong.

+5  A: 

The o variable is locally scoped inside the $.fn.ksana function, in order to allow the rotate to reach it, you should either:

  • Simply pass the o variable to it as an argument.
  • Define that function within ksana.
  • Define o in the outer scope.

IMO, passing it as an argument is enough clean:

(function($) {
  $.fn.ksana = function(userOptions) {
    var o = $.extend({}, $.fn.ksana.defaultOptions, userOptions);

    return this.each(function() {
        alert(rotate(o)); // pass o
    });
  };

  function rotate(o) { // use passed object
    return Math.round(o.negRot + (Math.random() * (o.posRot - o.negRot)));
  }
//...
})(jQuery);
CMS
+1 Since you're not exposing `rotate` outside the plugin, may as well pass the variable in as an argument to avoid having unnecessary global state.
R0MANARMY
This may be a daft question, but doesn't that create additional overhead? I'm going to have a lot of functions that will need access to o, so I'd be passing o around a lot...
safetycopy
@safetycopy: Well, you can expose `o` to more functions, like I say in my third approach, defining `o` in the enclosing scope.
CMS
So, that would look like: `(function($) { var o = null; $.fn.ksana = function(userOptions) { o = $extend({}, $.fn.ksana.defaultOptions, userOptions);`Is that correct?
safetycopy
@safetycopy: Right, by the way, you don't really need to assign `null` (unless you specifically want), if you don't assign anything, i.e. `var o;` o will contain `undefined`, also you [don't need semicolons](http://stackoverflow.com/questions/1834642/best-practice-for-semicolon-after-every-function-in-javascript) after function declarations (like `rotate` in your original post).
CMS
Hmmm, ok. I think I prefer the option of moving the rotate function to within `ksana`. Thanks for your input and the semicolons advice :-)
safetycopy
+2  A: 

You have to put o in the scope that is surrounding for both rotate and ksana - i.e. in your root function($) scope. Like this:

(function($) {

var o;

$.fn.ksana = function(userOptions) {
   o = $.extend({}, $.fn.ksana.defaultOptions, userOptions);

But why don't you just make it an argument of rotate? Why do you need to make it kind of "global"?

Fyodor Soikin
o is merged from the user and default options. If it's in the root function's scope, I don't think I'd be able to achieve that.
safetycopy
+2  A: 

You can either put the rotate-function in the same scope as o:

(function($) {

$.fn.ksana = function(userOptions) {
    var o = $.extend({}, $.fn.ksana.defaultOptions, userOptions);

    function rotate() {
        return Math.round(o.negRot + (Math.random() * (o.posRot - o.negRot)));
    };

    return this.each(function() {
        alert(rotate()); 
    });
};

Or, simply pass it to rotate:

(function($) {

    var o;
    $.fn.ksana = function(userOptions) {
        o = $.extend({}, $.fn.ksana.defaultOptions, userOptions);

        return this.each(function() {
           alert(rotate(o)); 
        });
    };

function rotate(o) {
    return Math.round(o.negRot + (Math.random() * (o.posRot - o.negRot)));
};
truppo
Would moving the rotate function to the same scope as o keep rotate private?
safetycopy