adi518
adi518

Reputation: 862

Why do changes to an Object passed to a closure persist in global scope?

var x = 1;

(function(x, j) {
    j.fn.cool = function() {};
    x = 2;
})(x, jQuery);

console.log(jQuery.fn.cool); // returns the newly added function

I noticed this happens not with just jQuery, so I assume it's related to passing specifically objects (perhaps arrays too). This doesn't happen with primitives.

Upvotes: 4

Views: 222

Answers (1)

mhodges
mhodges

Reputation: 11116

Okay, so when you have the parameter x, your x = 2 refers to the local copy that is scoped to your IIFE, it does not refer to the x that exists on the global scope. This is because the compiler always looks at the closest scope for the existance of a variable before moving outward. The reason that you can set the property on the "j" parameter, however, is because javascript treats primitives differently than objects/arrays when passed as parameters.

Any parameter in javascript is passed by value - always. Meaning that if you were to assign a new value to that parameter, it will remain unchanged in the calling scope. HOWEVER, there are a couple exceptions (objects/arrays) in that if you assign a PROPERTY of that object, or a specific ELEMENT of the array, it WILL reflect the change in the calling scope. Some people call it "pass-by-reference" because it behaves similarly, but technically speaking, the variable itself is still passed by value in every situation.

As per this stack answer, these are the rules.

  1. Primitive type variables like strings and numbers always remain unchanged in their calling scope.
  2. Arrays and Objects are changed in their calling scope based on these conditions:

    If you are assigning a new value to an object or array it is UNCHANGED in the calling scope

    object1 = {prop: "car"};
    array1 = [1,2,3];
    

    If you are assigning a new value to a PROPERTY of an object, or ELEMENT of an array then it WILL BE CHANGED in the calling scope

    object1.prop = "car";
    array1[0] = 9;
    

You can see proof of this HERE by removing x from your argument/parameter list, and leaving everything else the same. Since there is no local variable with an identifier of x that is scoped to your IIFE, the compiler goes out and checks on the global scope, where it will find your var x declaration, and it will use that and set it equal to 2.

Great question! Hope that helps clear things up! Let me know if you need any more clarification.

Upvotes: 2

Related Questions