开发者

closure not working

var Dog = function() {

    var _instance = 'hello world';

    return function() {
        console.log(this._instance);
    }
} (); //note that it is self invoking function

var l = new Dog(); //#> undefined 

In the above case I was expecting an output of:

'hello world'

Why is this._instance not accessing the the variable which should be a开发者_StackOverflowccessible by virtue of closure? I tested this in FF and am getting undefined.


You don't assign _instance to the object, it's just a closure variable, and should be accessed without using this:

var Dog = function() {

    var _instance = 'hello world';

    return function() {
        console.log(_instance);
    }
} (); //note that it is self invoking function

var l = new Dog();

I'd probably write it like so instead:

var Dog = (function() {

    var defaults = {
       name: 'Rags'
    };

    var Dog = function (options) {
        // Take options as a constructor argument, this
        // allows you to merge it with defaults to override
        // options on specific instances
        this.setOptions(options);
    };

    Dog.prototype = {
       // Common methods for Dogs here
       setOptions: function (options) {
          // Declare all variables in the beginning of the method because
          // JavaScript hoists variable declarations
          var key = null;
          // First assign all the defaults to this object
          for ( key in defaults) {
             this[key] = defaults[key];
          }
          // Now override with the values in options:
          if (options && options.hasOwnProperty) {
             for ( key in options ) {
                this[key] = options[key];
             }
          }
       }
    };

    return Dog; // Return the constructor method 
} ()); // wrap the self-invoked function in paranthesis to visualize that
       // it creates a closure

var buster = new Dog({name: 'Buster'}),
    unnamed = new Dog();

alert(buster.name); // Alerts 'Buster'
alert(unnamed.name); // Alerts 'Rags'

Note that I have not tried to compile the above code, so it might contain a few mistakes. Nothing JsLint can't handle though!

You might want to consider adding filtering to the setOptions method so that it doesn't assign properties you don't want, or filter out methods etc declared in the options-parameter.

Additionally, if you use JQuery, or similar library, there are (often) utility functions for merging objects, making it trivial to write the setOptions-method:

function setOptions (options) {
   // I assume JQuery here
   // true as the first argument gives us a recursive merge
   var mergedOptions = $.extend(true, defaults, options);
   for (var key in mergedOptions ) {
      if(this.checkAllowedProperty(key, typeof(mergedOptions[key])) {
         this[key] = mergedOptions[key];
      }
   }
}

/**
 * This method checks if propertyName is an allowed property on this object.
 * If dataType is supplied it also checks if propertyName is allowed for
 * dataType
 * @return true if propertyName, with type dataType, is allowed on this object,
 * else false
 */
function checkAllowedProperty (propertyName, dataType);


Your problem is this.


Change this._instance to _instance. You may also want to wrap your self-invoking function in parentheses like (function() { ... })(); for maximum browser compatibility.


As the others have said, you need to remove "this." from your function.

The reason for the problem is down to the binding of the "this" keyword in the two function contexts. Inside the closure, "this" refers to the function that is being returned, and not to the outer function. You could resolve this by doing the following:

var Dog = function() {

    var _instance = 'hello world';
    var that = this;  //Assign "this" to "that"

    return function() {
        console.log(that._instance);  //Use reference to "that"
    }
} ();

var l = new Dog();

You could also probably do something closer with the function.apply() method, but I'll leave that to you.

I hope that helps.


Perhaps you are satisfied by removing "this.", but you may be interested to learn that "this" doesn't refer to what you wanted it to anyway. What it refers to really depends on how the function is called. It does not necessarily refer to an instance of an object constructed by the function you returned, or its container function, or to any other object. By default, if you merely call the function as a normal function, "this" will refer to the global window context.

What you must do to have "this" be bound to any specific object is to call the function as a method of that object, or of its prototype. e.g. foo.myMethod(). Another way is that you can use the apply or call method, passing in the object you want it to apply to. e.g. anyFunction.apply(foo).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜