www.webdeveloper.com
Results 1 to 4 of 4

Thread: why closure works in constructor but not in prototype?

  1. #1
    Join Date
    Apr 2010
    Posts
    26

    why closure works in constructor but not in prototype?

    Hey all,

    I'm not sure why this:

    Code:
    console.log(instance2.getAnotherKey()());
    returns Object { a = "a" }

    when I clearly reassigned the value of the a property.

    Reassigning the property caused change in this line:

    Code:
    ref.a = 'abc'; 
    console.log(inst.getZ()); //Object {a="abc"}
    So why does it work when you call an instance method in the constructor (e.g. this.getZ())

    but doesn't work when you call the instance method in prototype?

    Here's the full example

    Code:
    function Love(){
    	//var this = Object.create(Love.prototype);	
     	this.getObj = function(){
    		return {a : 'a'};
    	}
    	
    	var z = {a : 'a'};
     	this.getZ = function(){
    		return z;
    	}
     	//return this;  
    }
    
    Love.prototype = {
    	getKey : function(){
    		return {key : 'value'};
     	},
    	getAnotherKey : function(){
    		var x = {a : 'a'};
    		return function(){
    			return x;
    		}
    	}
    }
    
    
    
    var instant = new Love();
    var instant_obj = instant.getObj();
    console.log(instant_obj);
    var instant_obj2 = instant_obj;
    instant_obj.a = 'd';
    console.log(instant.getObj()); //Object {a="a"} - This returns original object and its key/value pairs because the object literal in the inner function may have access to the variables of outer function after function exits, courtesy of closure, but the variables in the inner function are themselves destroyed when function exits, meaning that when we call the function again, it returns a new object every single time. Now if we store the returned object in a variable, and then reference it in another object, then that object will be passed by reference and modifying one will modify the other. Check end notes
    console.log(instant_obj2.a); //'d' - objects passed by reference change
    
    var inst = new Love();
    var ref = inst.getZ();
    console.log(ref);
    ref.a = 'abc'; 
    console.log(inst.getZ()); //Object {a="abc"} - The reason why this returns "abc" is because the variable z is a closure (outside of the immediate function), so when we call the function again, changes to it are remembered. It has nothing to do with passing an object by referenece. However, reassigning "this" to other variables will mean that those other variables will reflect the changes since they are now pointing to "this"'s returned object.
    
    var inst2 = new Love();
    var ref2 = inst2.getZ();
    console.log(ref2); // Object {a="a"} - When we create a new instance (a new "this" object), it RECREATES the variables again, so rather than returning "abc", it returns "a" again.
     
    var instance2 = new Love();
    var instance_variable = instance2.getKey();
    var instance_variable2 = instance_variable;
    instance_variable.key = "property's value";
    console.log(instance2.getKey()); //Object {key="value"} - This returns original object and its key/value pairs because the object literal in the inner function may have access to the variables of outer function after function exits, courtesy of closure, but the variables in the inner function are themselves destroyed when function exits, meaning that when we call the function again, it returns a new object every single time. Now if we store the returned object in a variable, and then reference it in another object, then that object will be passed by reference and modifying one will modify the other. 
    //So the question remains how does the instance have access to getKey() if getKey() is part of the prototype of Love. The answer: The "new" operator will invoke your constructor passing in the specified parameters. A special variable named "this" will exist within the scope of the constructor (var this). "this" points to the current object instance (given that "this" is returned at the end of the constructor expression: return this;). Using "this" you can add and modify properties on the instance being created by the constructor (e.g. this.a = 'a'; this.fun = function(){}). all JS objects have a private prototype property. I say "private" because this property is not directly visible or accessible from the object itself (e.g. you don't say instance.prototype.getB()). When the runtime performs a lookup on a property, it first looks at the instance to see if that property is defined there. If it is not defined there, the runtime looks for the property on the object's private prototype. So how does the instance have access to getB() if it was initialized in the constructor's prototype? When new operator is invoked, it not only creates the new object, but it also assigns the constructor's prototype property (the publicly accessible one) to the new instance's private prototype. By defining properties on the constructor's prototype, like Love.prototype.getB(), we are creating properties that will be in the private prototype chain of each new instance. These become, in effect, instance methods. 
    console.log(instance_variable2.key); //property's value - objects passed by reference change
    
    console.log(instance2.getAnotherKey()); //function() - So what we return here is simply just a reference to a function, since when we call getAnotherKey, we return a reference to function
    
    var closure = instance2.getAnotherKey();
    console.log(closure()); // Object {a ="a"} - We assigned the function reference to closure, so when we call closure, it returns the function reference, which is an object - declared in our local variable "var x".
    console.log(instance2.getAnotherKey()()); //Or we can call the object and then execute the inner function via the same function
    
    var result = instance2.getAnotherKey()();
    var result2 = result;
    result.a = "xyz";
    console.log(instance2.getAnotherKey()()); // Object {a ="a"} - Why does it return the original object when we clearly changed the value. Aren't closures preserved just as they were preserved when I called inst.getZ() above?
    console.log(result2); // Object {a ="xyz"}
    Thanks for response.

  2. #2
    Join Date
    Jul 2008
    Location
    urbana, il
    Posts
    2,787
    everytime you call instance2.getAnotherKey()(), it returns a new object, the local "x" created by calling the function "instance2.getAnotherKey".

    you change one object, but then grab a new one and look for the change you made to the first; they ain't there.

    you can see how you did modify result2, which is the same as result.

  3. #3
    Join Date
    Apr 2010
    Posts
    26
    Thanks for response. So it's impossible to create the closure I am looking for when assigning an object literal to prototype property?

    Will I be forced to do this?
    Code:
    Love.prototype = (function () {
     
    var x = "a";
     
    return {
    getAnotherKey: function () {
    return x;
    }
    };
    }());
    Since this obviously doesn't create a private variable:

    Code:
    Love.prototype = {
             x : 'a',
    	getAnotherKey : function(){
    		return x;
    	}
    }
    And this creates problem I outlined in initial post:

    Code:
    Love.prototype = {
    	getAnotherKey : function(){
    		var x = {a : 'a'};
    		return function(){
    			return x;
    		}
    	}
    }

  4. #4
    Join Date
    Apr 2010
    Posts
    26
    eh, I guess the first option technically does what the other two do anyway. Since it's self-executing and returns an object literal.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles