Skip to content

Instantly share code, notes, and snippets.

@schicks
Last active January 23, 2019 13:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schicks/eac5e9ed97cdf2451c90e9e2088ca075 to your computer and use it in GitHub Desktop.
Save schicks/eac5e9ed97cdf2451c90e9e2088ca075 to your computer and use it in GitHub Desktop.
default arguments

How Does Javascript Handle Default Arguments?

My first experience with default arguments was from Python, where there are dire warnings of the dragons you will encounter if you supply mutable values as default arguments. The concern is in Python, these arguments are initialized at the time that the function is created, which can give you some very surprising results.

def greeter(greetings={}):
  print(greetings.get('hi', 'No such greeting')
  greetings['hi'] = 'Hello World!'

When you first run this function with no arguments, 'No such greeting' will be printed on the screen, because it will be looking in the empty dictionary for the key 'hi'. However, every time you run the function after that, 'Hello World!' will be printed on the screen. This happens because we modified the default argument, which is the same dictionary every time you run the function.

Because of this experience, I had been very wary of default arguments in javascript, where mutable objects are common and it seemed rife with danger. However, when I started noticing that some of my peers used this pattern fairly regularly, I went looking to try and replicate the problems that we see in Python.

const greeter = (greetings={}) => {
  console.log(greetings.hi || 'No such greeting')
  greetings.hi = 'Hello World!'
}

Much to my surprise, this function very reliably outputs 'No such greeting'. This was very surprising to me. If the arguments of the function weren't initialized when the function was called, when were they initialized?

const shouter = (arg=console.log('Hello World!')) => null

When you write this function, no log appears, so the arguments definitely aren't initialized when the function is written. Instead, each time you run the function, the log appears, suggesting that the arguments are reinitialized with each invocation of the function. This solves the complications we saw in python, where the values were shared across function calls, but also introduces another question. If the arguments are initialized when the function is called, what is in scope for them? Can they depend on each other?

const interdependent = (one, two=one+1) => two 
// interdependent(1) === 2
// interdependent(2) === 3

It turns out that javascript default arguments are in the same namespace as the body of the function, with the added restriction that they can only refer to arguments which came before them in the argument list. For instance, (one=two-1, two) => one will return a ReferenceError whenever the first argument is undefined, because it tries to reference the variable two before it is defined. Another way to understand this is to look at how defaulting arguments in javascript used to work.

function interdependent(one, two) {
  var two_within_the_function
  if (typeof two === 'undefined') {
    two_within_the_function = one + 1
  } else {
    two_within_the_function = two
  }
  return two_within_the_function
}

Imagine that each default argument is in such a block, where we are within the function body and checking if the variable is undefined. In this case it is clearer why we couldn't refer to the arguments out of order. the within_the_function versions of the later variables simply would not have been defined yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment