Functions in Javascript

JS types

  1. there are 5 primitives in Javascript:
    • Boolean
    • null
    • undefined
    • String
    • Number
  2. there are 3 non-primitive types in Javascript:
    • Array
    • Function
    • Object
    • ... but they are all actually Objects

main difference

when you assign non-primitive types to variables, javascript references them, therefore changes to the reference will change all other variables referencing it.

let arr = [1]
let arr2 = arr
console.log(arr) // [1]
console.log(arr2) // [1] -> this is a reference

arr.push(2)
console.log(arr) // [1,2]
console.log(arr2) // [1,2] -> and it got affected by arr.push()

function parameters

when you pass primitives into functions as parameters...

let one = 1
let two = 2
function add(x, y) {
  return x + y
}
add(one, two)

in the above code, the function add does this internally:

let x = one // thereby copying the value, NOT referencing it
let y = two

therefore functions that take primitives as parameters are pure functions

when you pass non-primitives into functions as parameters...

it does NOT copy values, but references them. therefore when you update values inside the parameters, e.g.:

the original gets changed (usually this is not your intention)

example from the article:

function changeAgeImpure(person) {
  person.age = 25
  return person
}
var alex = {
  name: 'Alex',
  age: 30,
}
var changedAlex = changeAgeImpure(alex)
console.log(alex) // -> { name: 'Alex', age: 25 }
console.log(changedAlex) // -> { name: 'Alex', age: 25 }

A function that takes in an Object, however, can mutate the state of its surrounding scope. If a function takes in an array reference and alters the array that it points to, perhaps by pushing to it, variables in the surrounding scope that reference that array see that change. After the function returns, the changes it makes persist in the outer scope. This can cause undesired side effects that can be difficult to track down. ref

therefore, many native array functions, like Array.map and Array.filter are written as pure functions

to do the same thing as a pure function:

function changeAgePure(person) {
  var newPersonObj = JSON.parse(JSON.stringify(person))
  newPersonObj.age = 25
  return newPersonObj
}
var alex = {
  name: 'Alex',
  age: 30,
}
var alexChanged = changeAgePure(alex)
console.log(alex) // -> { name: 'Alex', age: 30 }
console.log(alexChanged) // -> { name: 'Alex', age: 25 }

an here's something unexpected

function changeAgeAndReference(person) {
  person.age = 25 // this affects the passed in object
  person = {
    name: 'John',
    age: 50,
  } // this is a brand new object that is reassigned to the variable 'person'

  return person // what gets passed out is the new object (John)
  // while the original object also has its age altered
}
var personObj1 = {
  name: 'Alex',
  age: 30,
}
var personObj2 = changeAgeAndReference(personObj1)
console.log(personObj1) // -> { name: "Alex", age: 25 }
console.log(personObj2) // -> { name: "John", age: 50}

the author explains it in a very clear way with this equivalent code

var personObj1 = {
  name: 'Alex',
  age: 30,
}
var person = personObj1
person.age = 25
person = {
  name: 'john',
  age: 50,
}
var personObj2 = person
console.log(personObj1) // -> { name: 'Alex', age: 25 }
console.log(personObj2) // -> { name: 'John', age: '50' }

and:

The only difference is that when we use the function, person is no longer in scope once the function ends.

ref