4
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.

adi518
  • 863
  • 1
  • 11
  • 19

1 Answers1

2

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.

Community
  • 1
  • 1
mhodges
  • 10,938
  • 2
  • 28
  • 46
  • Thanks for your answer. I have a grasp of how scoping work and so when looking at primitives, it's easy to understand. Ahead of posting this question, I found a similar question with an answer similar to yours and the one you linked to. However, it was neg propped and so my confidence in adapting that answer dropped. I noticed [Pointy's](http://stackoverflow.com/users/182668/pointy) comment below the answer you linked to, indicating the reason behind objects being passed-by-reference is actually more intricate. – adi518 Aug 16 '16 at 20:56
  • @adi518 Yes, Pointy's comment was simply stating that pass-by-reference is not anything special of an object, rather it defines how the variable gets treated in its calling environment. In my answer I stated that technically there is no such thing as pass by reference in javascript. When an object gets passed as a parameter, you get a pass-by-value copy of the object's reference. Hence why if you change the reference itself, it does not persist, but if you change a property on that object, it does persist, because you are pointing at the actual reference to that object's property in memory. – mhodges Aug 16 '16 at 21:16
  • I see. Then, how can one modify jQuery and keep it non-persistent? is it even possible? – adi518 Aug 16 '16 at 22:58
  • You would have to create a deep copy of the jQuery parameter like so: http://stackoverflow.com/a/122704/4987197 -- see the demo here: http://jsbin.com/qebiziniba/1/edit?html,js,console – mhodges Aug 16 '16 at 23:00
  • Ah, so obvious that I overlooked it. – adi518 Aug 16 '16 at 23:13
  • While I understand your answers so far and things do make more sense now, the condition for a pass-by-value seems to defeat the purpose, isn't it? according to the example, you will be overriding the object and remain without it's initial value. Extend solves the problem as we know, but can you confirm my assumption? And when you say: "there is no pass by reference in javascript", do you mean there's no way to pass-by-ref/value explicitly as in some low-level languages? in other words, it's to say JS handles pass-by-ref/value completely behind-the-scenes.. as it's a loose-type language. – adi518 Aug 16 '16 at 23:31
  • 1
    @adi518: No. JavaScript is completely pass-by-value. The semantics of passing in JavaScript is identical to Java, which is also completely pass-by-value. There is no "behind the scenes" or not. – newacct Aug 17 '16 at 05:43
  • @newacct: so how do we explain the pass-by-ref example above? got me confused again. – adi518 Aug 17 '16 at 08:03
  • 1
    @adi518: There is no pass-by-reference example. Pass by reference means that an assignment to a parameter inside a function has the same effect as an assignment to the passed variable in the calling scope. The only assignment to a parameter is `x = 2;`, which does not assign to the passed variable in the calling scope. There is no assignment to the parameter `j`. It only reads `j` to access a property on the object pointed to by the reference `j`, and a setting of a property on the object pointed to by that. It would work identically in Java. – newacct Aug 17 '16 at 08:09
  • Then what is `j`? isn't that a classic example of passing by reference? you said there's no example, but then said "It only reads`j` to access a property on the object pointed to by the reference `j`". – adi518 Aug 17 '16 at 08:38
  • 1
    @adi518 Maybe this will clear it up for you: 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. – mhodges Aug 17 '16 at 15:24
  • @adi518 I have edited my answer to hopefully clear up any confusion – mhodges Aug 17 '16 at 15:31
  • 1
    Haha, I'm looking at this question now and I'm laughing. I went through it all again and realized I was lacking some context crucial to fully understand when you pointed out everything in JS is passed by-value and when I pass an object, it's essentially a reference, a semi-primitive that I could override and therefore won't persist in the calling scope or if I change a property, it will obviously alter it in the calling scope as well. Sweet! – adi518 Apr 29 '17 at 23:49
  • @adi518 Yup, you got it! – mhodges May 01 '17 at 00:56