DEV Community

Cover image for Weird behaviors of javascript: Primitive Types and Reference Types
MutluDev
MutluDev

Posted on • Originally published at blog.mutlu.dev

Weird behaviors of javascript: Primitive Types and Reference Types

I recently learned a difference between Primitive types and Reference types.
I thought it would be great to write a blog post about this topic.

Let's start with a code snippet

let a = 1;
let b = a;

console.log(b); // 1

a = 2;

console.log(b); // 1

Well this looks okay let's do the same thing with an object

let a = {
  someText: 'Hello',
};

let b = a;

console.log(b); // { someText: "Hello" }

a.someText = 'Hi';

console.log(b); // { someText: "Hi" }

This performed unexpected isn't it?
You will understand why this happens by the end of this post, let's dive into it.

What are primitive and reference types

In Javascript, we have 6 primitive types

  1. String
  2. Number
  3. Boolean
  4. null
  5. undefined
  6. Symbols (ES6)

and 3 Reference types

  1. Object
  2. Array
  3. Function

Primitive types

Primitive types stored in a fixed-sized memory,
so exact value of "a" stored in memory,
I think an example would be more helpful here

We created variable "a", it placed into memory like this

Alt Text

then we copied memory value of variable "a" to variable "b"

Alt Text

That seems okay let's see what happens with Reference types

Reference Types

Reference types are more complex and take up more space compared to Primitive types.
They can't be stored in fixed memory so they stored in a random location in memory
let's see it's diagram

Alt Text

Notice that the value stored in memory is not the real value itself, its reference to real value.
When we copy the variable "a" to "b" we copy the Memory value (Reference to real object). That's why they're called reference values.
When we copy the variable "a" we don't copy the real value, we copy reference to real value.

Alt Text

That's why "b" is also changed when we change the property of "a".

Source

Thanks for reading

If you have any questions feel free to ask below.

Top comments (37)

Collapse
 
alexanderjanke profile image
Alex Janke

Animation

Collapse
 
pentacular profile image
pentacular

There is no pass by reference in this article.

Collapse
 
alexanderjanke profile image
Alex Janke

Not pass by (no functions in this article) but it's basically the same principle for types and assigning objects to new variables- still just a reference

Thread Thread
 
pentacular profile image
pentacular • Edited

Not in the least.

let a = { v: 1 };
const foo = (p) => { p = { v: 2 }; };
foo(a);
console.log(a);

What does this print?

Which of your images does it reflect?

Thread Thread
 
alexanderjanke profile image
Alex Janke • Edited

What does this print?

An error. That is syntactically wrong

Thread Thread
 
pentacular profile image
pentacular

Try now ;)

Thread Thread
 
alexanderjanke profile image
Alex Janke • Edited

Well yea, you are kind of comparing apples with pears.
The same thing would happen without the function. For the reference to not break, you cannot reassign p - only it's properties.

let a = { v: 1 };
const foo = (p) => { p.v = 2 };
foo(a);
console.log(a);
// prints { v: 2 }

.. this would still be a reference and mutate a.

Same gist, but without the function

let c = {
  someText: "Hello"
}
let d = c;
d = {
  v: 2
}
console.log(c);
// prints { someText: "Hello" }
Thread Thread
 
mutludev profile image
MutluDev

Yeah, when you assigning { v: 2 } to p you're actually changing value of p with completely new one, this diagram can be helpful

When you changed value of p with p = { v: 2 }; it becomes like this

memory location #234 will be inaccessible and garbage collected, I hope this helps

Thread Thread
 
pentacular profile image
pentacular

So.

Pass by value, then? :)

Can you take a look in the ecmascript spec and show me where it talks about a being a reference?

I can only see it referring to object values.

Thread Thread
 
mutludev profile image
MutluDev • Edited

Not exactly think it like this

let a = "Hello";

function addHi(x){
 x+="Hi"
}

addHi(a)

a // "hello"

in this code you pass the value of the a and it acts like addHi("Hello")
so you're not changing the a

let a = { v: 1 };
const foo = (p) => { p = { v: 2 }; };
foo(a);
console.log(a);

In this code it acts like you passed the memory location foo(#234)
when you change it with p = { v: 2 }; you're creating entire new object { v: 2 } this objects has a new memory location like #427, you're changing value #234 with #427 so a is also changing

Edit: value of a is not changing

let a = { v: 1 }

function changeValue(x){
  x = { v: 2 } // you're not really changing value of a you're changing value of x which is reference to value of a 
}

changeValue(a)

a // { v: 1 }

a changes when you change property of a in function

let a = { v: 1 }

function changeProp(x){
  x.v = 2 // a is also changing because by changing property you're not changing reference to `{ v: 1 }`, you're changing value inside that reference
}

changeProp(a)

a // { v: 2 }

Thread Thread
 
pentacular profile image
pentacular

When I run

let a = { v: 1 };
const foo = (p) => { p = { v: 2 }; };
foo(a);
console.log(a);

the output is

{v: 1}

I don't see a changing.

So, which bit of this isn't just pass-by-value in both cases?

Thread Thread
 
alexanderjanke profile image
Alex Janke

Again, you reassign the variable. That is indeed pass-by-value but only because you reassign the variable, which in any case you shouldn't do because it's semantically confusing.
Have a look at the style guide by AirBnB for example on this topic (github.com/airbnb/javascript#es6-d...)
mutate arguments

In other words (not mine):

if your value is an object, then the new scope gets a copy of the reference. If you modify the object by dereferencing (that is, a.foo = "bar";), your modifications to the object persist outside of your scope.

Your change does not persist outside of the function-scope because you did not mutate the original reference to a. You created a "temporary" version of it inside the function, which doesn't exist anymore because of the function scope. If you want the change to persist (pass-by-ref), you have to dereference it (look at my earlier example).

Thread Thread
 
pentacular profile image
pentacular

Ok, so we can all agree that it is pass by value.

And that there is no pass by reference happening anywhere here.

Which means that what we're passing is the value of the object.

All well and good.

Can you show me where it says that object values are references, in the ecmascript standard?

I'm having trouble finding support for that claim.

Thread Thread
 
willsmart profile image
willsmart

JS always passes function args by value.

const a = something
function foo(value) {codez}
foo(a) // <= this could never change the type of a, since foo never sees a itself, just its value

function bar(pram) {
  pram.baz = 1 // <= this isn't mutating pram, but the details of the object pram refers to.
  pram = 1 // <= this mutates pram itself. Were it pass-by-reference then the argument to the function in the caller scope would now read as 1, because it's the thing we just set to 1
} 

But, the "value" of a variable for a JS object is a reference to the underlying object.
This is the same with strings, but they're immutable so their references cause less confusion

const a = "hi"
const b = "hi"
// a and b are two values that refer to the one common string in memory
a = "bye"
// reassigning a doesn't change b

So, functions pass by value, but in many cases that value is just a reference to some common structure.

Thread Thread
 
pentacular profile image
pentacular

I'm glad we can all agree that javascript doesn't support pass by reference.

Now, could you show me where in the ecmascript standard it talks about an object value being a reference?

Thread Thread
 
danielw profile image
Daniel Waller (he/him)

Hey man, just some feedback on your conversational style:
I find it's quite confrontational and irritating to read.
To me it feels like your trying to score internet points for being right and aren't actually interested in explaining things to people.
It reads more like a Twitter or Reddit thread and I was always very happy to see a calmer more constructive and friendly style on here.

I really don't mean this as a personal attack, I just feel the conversation could have a lot more merit to others (especially newcomers) if it were more constructive.

Thread Thread
 
pentacular profile image
pentacular • Edited

Could you be more specific about the confrontational style that you perceive?

Thread Thread
 
willsmart profile image
willsmart

A bit of constructive editing...

I'm glad we can all agree that javascript doesn't support pass by reference.

Now, could you show me where in the ecmascript standard it talks about an object value being a reference?

vvvvvv

(First para deleted)

What do you mean that object values are references? I've been fishing through the spec (ecma-international.org/ecma-262/11...) and can't find anything saying so.

Same info and questions, but the second one is showing you're willing to put in some work to find your answer, that we probably fundamentally agree but are stuck on terminology, and will ultimately avoid nag replies like this.
If the language doesn't come naturally to you, try your best to fake it til you make it.

I had a squiz at the spec, couldn't find a specific mention, and ran out of motivation to dig further. The proof though is

a = {}
b = a
a.foo = 1
console.log(b.foo) // <- prints "1"

For a time, the values of a and b refer to the same object. They hold two pointer-like structures that reference the same shared underlying object. In programming this is often called a reference.

Thread Thread
 
willsmart profile image
willsmart

I'm signing off the thread btw

Thread Thread
 
pentacular profile image
pentacular • Edited

That's fine, but for future reference, let's note that a.foo doesn't mean that a is a reference.

It does mean that a and b have the same value, and it does mean that that value is used to find a particular property named 'foo' and modify that.

Which makes me think that all of this 'reference' stuff is something someone made up to try to explain javascript in terms of a language with references, like C++, but which doesn't quite fit.

Collapse
 
tbroyer profile image
Thomas Broyer

+1 it's passing a reference by value.

Thread Thread
 
pentacular profile image
pentacular

Could you show me where in the ecmascript standard it talks about an object value being a reference?

Thread Thread
 
tbroyer profile image
Thomas Broyer

Wow, for some reason, reading on mobile, I totally missed the article, mixing it up with @alexanderjanke 's comment! (so, my comment was actually discussing the GIF, which depicts passing a reference, not passing by reference). Only now realizing my mistake as I read this on a bigger screen.

As to your specific question, the ECMAScript spec is a bit too hard to read in details for me to prove you that you only ever hold a reference to an object value (or anything equivalent: e.g. in Go, a map or a slice is passed by value, but it itself holds a reference to its data, so all those map values you pass by copy everywhere allow you to access and modify the same data, therefore sharing the data between them, as if you had a hold on a reference to the map directly; implementation details are difference, but the big picture is equivalent)

Thread Thread
 
pentacular profile image
pentacular

If it depicts 'passing a reference' not 'passing by reference', the large red text saying "pass by reference" is a bit confusing.

As far as I am aware, Go also does not have reference values.

Let me know if you can find mention of one in golang.org/ref/spec

I have a feeling that there is the same root confusion in both cases.

That is that people are introducing references as a way to explain things in terms of another language, say, C++, rather than understanding the language in its own terms.

Thread Thread
 
tbroyer profile image
Thomas Broyer

If it depicts 'passing a reference' not 'passing by reference', the large red text saying "pass by reference" is a bit confusing.

That was the sense of my initial comment: pointing the error in that image.
(remember I though the image "was" the post, and your comment criticizing that there was no "pass by reference" here, to which I replied that I agreed and it was actually "pass a reference")

As far as I am aware, Go also does not have reference values.

Go has pointers, which are references to other values (that can also be pointers). IIRC they are stricter than C++ pointers as you can make a pointer point to an arbitrary address in memory (so in Go it works more as a reference than a pointer to some memory address), and allow pass by reference by way of passing a reference by value (because Go also has address operators that allow you to get a pointer to variable). But Go also have slices where multiple slices can share the same underlying array: how do you can that if not "they all reference the same array"?

Maybe you're reading too much into the word "reference", or maybe others are reading to much into it. It's just a concept after all. What's your precise definition of "reference value" that makes you say that neither JS nor Go have that concept?

In the case of JS, it doesn't really matter whether primitive values are hold by references to them (and thus can possibly be "interned") or copied around, because they're immutable values anyway. It's more important to understand it for composite values like objects, particularly if they're mutable (if they're immutable, it doesn't matter semantically, but as an optimization on memory usage and the time it can take to copy those objects).

Sure there are different reading levels, and many people use those terms/concepts that allow them to port their knowledge of one language (and how it deals with memory) to another, but in the case of this article, it's a bit different: when doing a = b, it's important to understand that both variables share (or reference) the same underlying value (composite, mutable) in memory, so modifying that value through one variable means that the modification can be seen through the other variable.
The post is likely wrong actually for primitive values, depending on the JS runtime implementation: they're most probably not copied, but shared as well, but it doesn't really matter as, as I said, they're immutable anyway.

That is that people are introducing references as a way to explain things in terms of another language, say, C++, rather than understanding the language in its own terms.

People each have their own mental model of how things work, at different levels of abstractions depending on the language and where they come from. What does it mean to "understand the language in its own term" wrt the behavior the OP notices? He explains it with "reference values" vs "primitive values", which one mental model; possibly not accurate depending on how things are actually implemented, but conceptually OK anyway.

My own mental model of this is that there are values that exist "somewhere" in memory, and variables are labels that you put on them (or you could think of yarns, threads, wires attached to them on one end, and to the "code" on the other end; so when entering a function, you put your current state –local variables– aside and hold different yarns in your hands). That works very well for programming languages that abstract away the actual memory allocations, it certainly doesn't work that well for C/C++, but still allows you to understand most C/C++ code anyway.

Thread Thread
 
pentacular profile image
pentacular

but in the case of this article, it's a bit different: when doing a = b, it's important to understand that both variables share (or reference) the same underlying value (composite, mutable) in memory

So what this mental model does is to say that the values of a and b aren't the real values of a and b, they're special reference values, and the real, underlying value, is something else which depends on the properties.

Which leads to people making errors like saying that foo(a) is pass by reference because the value you're passing is a 'reference value' and the 'real value' of a is determined by the set of its properties.

But there's nothing in javascript which represents this 'real value', and no operators that operate on this 'real value' (because it doesn't actually exist in the language).

So you end up demoting the actual accessible value and promoting an imaginary and inaccessible kind of value and confusing the language with things like pass-by-copy-of-reference (which seems to be the fallback position when it's pointed out that it isn't actually pass-by-reference).

But at least I'm starting to get an idea of where these peculiar ideas are coming from -- so thanks for being able to talk through it. :)

Collapse
 
vinceramces profile image
Vince Ramces Oliveros

I really like the demonstration here. I remember C#(VB.NET) does have both pass by value & pass by reference. they use ByVal and ByRef each parameter in a function.

Collapse
 
shane profile image
Shane McGowan

Nice write up! I wouldn't call these behaviors weird though, this is by far the least weird behavior of JavaScript

Collapse
 
mutludev profile image
MutluDev

Thanks, you're right this is not that weird, i wanted to make this as a series, i don't know what i will post next but i definitely consider "why 0.1 + 0.2 = 0.30000000000000004" which is lot weirder than this

Collapse
 
nombrekeff profile image
Keff • Edited

It is weird. I'd love to know more about this!

It's not that weird actually. It's just how computers handle floating numbers.

Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. Although there are infinitely many integers, in most programs the result of integer computations can be stored in 32 bits. In contrast, given any fixed number of bits, most calculations with real numbers will produce quantities that cannot be exactly represented using that many bits.

Excerpt from this post: docs.oracle.com/cd/E19957-01/806-3...

Really interesting read, I had not read much about this whole topic!

Thread Thread
 
mutludev profile image
MutluDev

Yeah, it feels weird when you see it first, after you've learned how numbers work in JavaScript, everything makes sense.

youtube.com/watch?v=MqHDDtVYJRI
This video really helped me to grasp how numbers work in JavaScript,.

Excerpt from this post: docs.oracle.com/cd/E19957-01/806-3...

This looks really interesting I will read this, thanks.

Collapse
 
selahattinunlu profile image
Selahattin Ünlü

Hi, I realized that you're looking for a job! We're hiring. But I could not see any information to contact with you. Can you please share an information to contact with you? :)

Collapse
 
pentacular profile image
pentacular

Could you point out where 'reference types' are mentioned in the ecmascript spec?

I can't find it anywhere in the language specification.

Collapse
 
mutludev profile image
MutluDev

es5.github.io/#x8.7 this might help

Collapse
 
pentacular profile image
pentacular

It would if it talked about object references.

The references mentioned there are property references.

For example, in delete a.b; a.b is a reference.

"The Reference type is used to explain the behaviour of such operators as delete, typeof, and the assignment operators."

Can you find anything about 'reference types' that's relevant to object values or anything mentioned in this article?

To be specific, anywhere that supports the claim that objects, functions, and arrays are reference type values.

Collapse
 
halildndar profile image
Halil Dündar

Very helpful and informative article, thank you for posting that.

Collapse
 
mutludev profile image
MutluDev

Thank you for your polite comment.