1

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.

Roman
  • 19,581
  • 6
  • 68
  • 84
safetycopy
  • 554
  • 4
  • 15

3 Answers3

6

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);
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • +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. – Roman May 28 '10 at 20:45
  • 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 May 28 '10 at 20:52
  • @safetycopy: Well, you can expose `o` to more functions, like I say in my third approach, defining `o` in the enclosing scope. – Christian C. Salvadó May 28 '10 at 20:57
  • So, that would look like: `(function($) { var o = null; $.fn.ksana = function(userOptions) { o = $extend({}, $.fn.ksana.defaultOptions, userOptions);` Is that correct? – safetycopy May 28 '10 at 21:01
  • @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). – Christian C. Salvadó May 28 '10 at 21:04
  • 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 May 28 '10 at 21:10
2

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
  • 78,590
  • 9
  • 125
  • 172
  • 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 May 28 '10 at 20:54
2

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)));
};
Roman
  • 19,581
  • 6
  • 68
  • 84
mthurlin
  • 26,247
  • 4
  • 39
  • 46