A Wizard Did It
A Wizard Did It

Reputation: 3674

Javascript closures, maintain outside object

I've been using Javascript closures to protect variables by making them local to the returned functions, such as:

closure = function() {
    var secretVar = 'Secret';

    return {
        "msg" : function() {
            console.log(secretVar);
        }
    };
}();
console.log(closure.secretVar); // Undefined
closure.msg(); // Secret

I feel that I have a pretty good grasp of that, giving me the ability to control how inner variables are accessed if at all.

I'm now running into this problem

closure = function() {
    var secretVar = ['Secret One','Secret Two'];

    return {
        "del" : function(modMe) {
            modMe = secretVar;
            modMe.slice(1,1);
            console.log(modMe);
        }(secretVar),
        "secretVar" : function() {
            console.log(secretVar);
        }
    };
}();
closure.del(); // Secret One
closure.secretVar(); // Secret One

I want closure.del() to return Secret One, but I want the secretVar to remained untouched, however, it's not. The del() function is modifying the reference rather than a copy, and I'm unsure of how to get it to copy secretVar and modify that.

I'm guessing it's going to be in the form of

(function(y) {
    //..Body that changes y
})(secretVar)

but I haven't been able to get that to work. Any ideas?

Upvotes: 4

Views: 639

Answers (4)

Matt
Matt

Reputation: 41832

Your problem actually has nothing to do with closures. When you do:

modMe = secretVar;

You are just creating a new variable pointing to the same array. What you do to one will be reflected in both variables, since they are pointing to the same thing.

If you want to perform some sort of modification on the array (while maintaining the original), you need to copy it first:

var copied = [];
for(var i = 0; i < secretVar.length; i++){
    copied.push(secretVar[i]);
}

Edit: As an aside, when you say you are using closures to "protect variables" you're not protecting them from being modified by the returned functions that performed the closure. You just made it so that the variable is inaccessible from outside the scope of those functions. But, inside the scope, such as when you do your slice, the variable is there and accessible to that function and is not "protected" just because it is a closure.

Edit 2: If you are going to be copying the array frequently, you can eliminate some of the irritation of the iteration by creating a closured function to do the copying for you:

closure = function() {
    var secretVar = 'Secret';
    var clone = function(){
        var clonedArr = [];
        var length = secretVar.length;
        for(var i = 0; i < length; i++){
            clonedArr.push(secretVar[i]);
        }
        return clonedArr;
    }

    return {
        "msg" : function() {
            var duplicate = clone();
        }
    };
}();

Upvotes: 7

Sean Vieira
Sean Vieira

Reputation: 159995

Very close -- the form is:

(function(y) {
    return function() {
        // that changes y
    };
})(secretVar)

NOTE: This will still pass a reference, and any destructive or altering operations will still affect secretVar. You'll need to make a deep copy of secretVar if you want to avoid altering it.

SEE: http://jsfiddle.net/B3ryr/2/

Upvotes: 1

cdhowie
cdhowie

Reputation: 169143

Your function(modMe) { ... }(secretVar) expression is actually returning a value and not a function, since you are executing the function. Coupled with Matt's answer, you need to:

  1. Remove the (secretVar) component from the expression as well as the modMe parameter -- the function already has access to secretVar anyway.
  2. Actually copy the array before working on it. See Matt's answer.

Upvotes: 0

Harmen
Harmen

Reputation: 22438

Robert made a good comment, but also note that closure.del is not a function, but undefined. Take another look at your code. The function is automatically executed and returns nothing, so del will be nothing.

var del = function(modMe) {
  modMe = secretVar;
  modMe.slice(1,1);
  console.log(modMe);
}(secretVar);

It's generally this form:

var a = function(b){
  console.log(b)
}('input');

console.log( typeof a ); // undefined
a(); // error: a is not a function

Upvotes: 0

Related Questions