Pass by Value (PBV) in JavaScript: Understanding Primitive Types and Objects

Selina Byeon
4 min read2 days ago

--

One important concept to refresh before diving into algorithm problems is understanding how JavaScript handles function arguments with Primitive Types and Objects. In other programming languages, you might encounter the terms Pass by Value (PBV) and Pass by Reference (PBR). However, in JavaScript, everything is passed by value. Nonetheless, this process is nuanced when it comes to complex types like objects and arrays.

Note: The term “Pass by Reference” is commonly misunderstood. JavaScript always passes arguments by value; for objects, this value is a reference that allows shared access to the underlying object data.

Primitive Types in JavaScript:

  • String
  • Number
  • Boolean
  • Undefined
  • Null
  • ES6 Symbols

Complex Types in JavaScript:

  • Objects (including arrays)
  • Functions

Primitive vs. Complex Types Behavior

Primitive Types:

In JavaScript, for primitive variables, the actual value stored in the variable is passed to another variable or function, creating a new copy. This means changes to the parameter within the function do not affect the original variable.

let originalFruit = 'Apple';
let newFruit = originalFruit;

newFruit = 'Pineapple';

// originalFruit remains 'Apple' because a copy was passed to newFruit
console.log(originalFruit); // Apple
console.log(newFruit); // Pineapple

Complex Types:

When you assign or pass complex values like objects or arrays, JavaScript passes a copy of the reference by value. This means that both the original and new variable refer to the same object in memory. Therefore, changes to the object through the new variable affect the original object.

let originalFruitBasket = ['Apple'];
let newFruitBasket = originalFruitBasket; //['Apple']

newFruitBasket[0] = 'Orange';
// Both references point to the same object, hence originalFruitBasket is updated

console.log(originalFruitBasket); // ['Orange']
console.log(newFruitBasket); // ['Orange']

newFruitBasket = ['Banana'];
// Reassignment breaks the reference; originalFruitBasket remains unchanged

console.log(originalFruitBasket); // ['Orange']
console.log(newFruitBasket); // ['Banana']

Understanding References:

If a variable stores a reference to an object, any modification to the properties of the object via this reference also reflects on the original object.

let originalList = [1, 2];
let newList = originalList; // newList now stores a copy of the reference

newList.push(3);
// Changes in newList affect originalList
// because they share the same reference

console.log(originalList); // [1, 2, 3]
console.log(newList); // [1, 2, 3]

If we want to create a new array with the same values rather than sharing the reference, we can use the .slice() method.

let originalList = [1, 2];
let newList = originalList.slice(); // Creates a new array, breaking the reference
// or let newList = [...originalList]

newList.push(3);
// Changes in newList do not affect originalList
console.log(originalList); // [1, 2]
console.log(newList); // [1, 2, 3]

Passing Values into Functions

Passing Primitive Values:

Notice that the value of the original primitive remains unchanged.

let age = 25;

function addYears(age) {
age += 5;
return age;
}

let newAge = addYears(age);

console.log(age); // 25
console.log(newAge); // 30

Passing Complex Values:

The reference to the complex value is passed, so modifications affect the original object.

let ageList = [25];

function addYearsToList(list) {
list.push(30);

return list;
}

let newAgeList = addYearsToList(ageList);

console.log(ageList); // [25, 30]
console.log(newAgeList); // [25, 30]
// ageList and newAgeList are pointing to the same array

Importantly, reassigning the parameter to a new object or array within the function does not change the original reference outside the function.

let ageList = [1, 2, 3];

function modifyArray(arr) {
// This modifies the array contents, affecting the original array
arr.push(4);
console.log('Inside function after push:', arr); // [1, 2, 3, 4]

// *** Reassigning the parameter to a new array ***
arr = [5, 6, 7];
console.log('Inside function after reassignment:', arr); // [5, 6, 7]
}

modifyArray(ageList);

console.log('Outside function:', ageList); // [1, 2, 3, 4]
  • arr.push(4) : Modification of properties or elements within a function affects the original object or array since both references point to the same memory location.
  • arr = [5, 6, 7] : Reassignment of the parameter within the function does not affect the original reference outside the function, as reassignment only changes the local copy of the reference within the function scope.

Equality Operators and PBV:

The strict equality operator === compares complex values by reference, not by value.

let numbers1 = [1, 2, 3];
let numbers2 = [1, 2, 3];

console.log(numbers1 === numbers2); // false (not same reference)

let numbers3 = numbers1;
console.log(numbers1 === numbers3); // true (same reference)

Conclusion:

In JavaScript, all function arguments are passed by value. However, for complex types such as objects and arrays, this value is actually a reference to the object or array in memory. This means that while you can modify the properties of an object or the elements of an array within a function (thus affecting the original object or array), reassigning the parameter to a new object or array within the function does not change the original reference outside the function. Understanding this distinction is crucial for writing efficient and bug-free code.

Thanks! Happy coding!

--

--

Selina Byeon
Selina Byeon

Written by Selina Byeon

Aspired to become a Staff Engineer. Intrigued to create innovative solutions to complex problems, and doing so in a lens of making an impact.

No responses yet