Basic Syntax: Functions

Unlocking the power of reusable code with functions.

Scroll down...

Content

Resources

Comments

It should already be clear that functions are all over the place in JavaScript. As a language that's built around asynchronous processing, that should make intuitive sense. You can probably imagine that you might have to package up functions and save them for later, even if you don't really know how that works yet.

Functions in JavaScript are "first class citizens", meaning you can do pretty much anything with them. In JavaScript, functions can be run immediately or saved to variables or passed around anonymously.

One of the key mental leaps for beginners learning JavaScript is understanding functions, specifically how they are defined, when they get passed around, and when they actually get called. So spend some time to really understand them!

In this lesson, we'll look into how JavaScript defines and uses its functions. You'll want to pay attention here because functions and objects form the backbone of JavaScript. Observe how they handle arguments and when they are actually executed.

This is one of those lessons where it will be quite useful to follow along with the provided code examples, poking and prodding until you understand.

Codecademy Interlude

Before you continue on here, get your hands on some code by completing Codecademy’s Functions section https://www.codecademy.com/en/tracks/javascript-combined. See you soon!

Functions

Functions are objects. In some ways, they're like grenades. That means they can be passed around and inspected and generally poked, prodded, and tweaked without anything happening. Until they're activated, they behave like any other variable. To actually run ("Invoke") the function, you need to manually pull its pin. We'll cover all this below.

Declaring Functions

There are two equivalent ways to declare a named function, and you'll see both of them used often -- either:

  • "constructor style" a.k.a. a "Function Declaration" (FD), or
  • applying it directly to a variable via a "Function Expression" (FE).

They accomplish the same thing:

// Function declaration
//   (also called a "constructor")
// Note: Typically capitalizes the function
//   name only when it is used as a constructor
function foo(){
  // ...do stuff
}

// Named function expression
var foo = function(){
  // ...do stuff
}

// function with arguments
var foo = function(x, y){
  // ...do stuff
}

// setting `bar` equal to the `foo` function itself
var bar = foo;

// setting `bar` equal to the RETURN VALUE
// of the `foo` function
var bar = foo();

In both types of declarations, the function is added as a property to the current scope like any other variable. If you're in the web browser, you can call something like window.foo to view the function. As you'll see in a future lesson, that's the equivalent of just calling foo.

Though you can pass a function around by name regardless of how you define it, setting it to a variable with a Function Expression makes it clearer that you might want to do this. Unless you are actually using your function as a constructor (which we'll cover later), we recommend declaring your functions Function Expression style (var myFunc = function(){...}).

Note: Your functions are on the same level as variables. If you later decide to set foo = 123, you'll lose the function you'd previously saved to it.

Invoking Functions

The key feature to understand about JavaScript functions is that you can either call them immediately or pass them around like any other object or variable. You'll see this pattern is all over the place.

In both of the function declarations in the examples above, the function is effectively saved to the variable foo. If you inspect foo in the console, it actually just returns the whole definition of the function instead of running it:

// declare it again to demonstrate
function foo(){
  return "hi";
}

// Now inspect it
foo;
//=>  function foo(){
//      return "hi";
//    }

You can call a function several ways:

  1. Call it with parentheses, e.g. foo(arg1, arg2...)
  2. If it's stored inside an object, invoke it as a method on that object, e.g. someObj.foo(arg1, arg2...), which we saw previously. In this case, you're essentially first pulling the function definition out of the object like any other property and then running it with the parentheses.
  3. Invoke it by running the call or apply methods on it, which essentially change what base object is calling the function. The difference between them is merely in their inputs — call takes its arguments as one long comma-separated list, while apply allows you to pass the arguments that you want the function to have via a single array (of comma-separated arguments). Using either of these functions has some implications which we'll explore shortly, and you'll become more familiar with them later on.
// a top-level function
var foo = function(words){
  return words;
}


// inspecting the function
foo;
/*
function foo(words) {
  return words;
}
*/

// Running the function "normally"
//   aka "function style"
foo("hi");
//=> "hi"


// Using `call` (which takes a list of arguments)
//   first arg is the caller of `foo`
//   and it's okay to pass `undefined`
// This isn't totally crazy,
// It's just more common to run more "normal"
//   functions in JS without a caller.
foo.call(undefined,"other words");
//=> "other words"


// using `apply` (which takes a array of arguments)
// first arg is the caller of `foo`
foo.apply(undefined,["too many words"]);
//=> "too many words"


// storing a function in the object
var bar = { baz: function() { return "bazzzz" } };


// running the function "method-style" by
// extracting it from the object with dot-notation
// and invoking it with parentheses
bar.baz();
//=> "bazzzz"

JavaScript functions allow you to pass any number of variables and they will just ignore the extra ones.

Return Values

Unless explicitly specified, the result or return value of a function will be undefined. Some programming languages will implicitly return the last statement in a function, but in JavaScript, we must be explicit about what you want to return:

// without 'return'
function oops() {
  "bar";
}

oops();
//=> undefined

// with 'return'
function foo(){
  return "bar";
}

foo();
//=> "bar"

You can actually return another function, which isn't even that uncommon:

function foo() {
  return function() {
    return "flubber";
  }
}

// inspect the returned function
foo();
/*
function() {
  console.log("flubber")
}
*/

// run the returned function in one go
foo()();
//=> "flubber"

Immediately Invoked Functions

You've seen how parentheses will invoke a function. A really nifty feature is that you can actually force a function to evaluate immediately by wrapping it in parentheses and calling it. This is called an "Immediately-Invoked Function Expression":

// wrap the whole thing in parentheses,
// add an extra () on the end,
// and supply any arguments you need
(function() {
  var foo = "Hello world";
  return "I'm a function!";
})();
//=> "I'm a function!"

// variables declared inside the function
// are not available outside that function
console.log( foo );
//=> undefined!

This is a common pattern when you want something evaluated immediately without declaring a bunch of variables into the global namespace. The use for this will become more clear when you start applying JavaScript asynchronously and working with closures.

Passing Functions as Arguments

The fun doesn't stop there. You'll also see functions passed as arguments to other functions all the time. This is frequently used in the "callback" pattern, where you tell the initial function to execute another function when it's finished doing its task. Again, this is necessary when we start working asynchronously.

You can pass in an existing named function or, more commonly, just define the function you want to run directly in the arguments area of another function:

// creating an outer function for use below
var outerFunction = function(functionArg) {
  return functionArg();
};

// Here we're passing in an anonymous
// function to the one we defined above
// The anonymous function will be run by
// `outerFunction` and its return value
// will be directly returned by `outerFunction`.
outerFunction( function() {
  return "Hello, World!";
});
//=> "Hello, World!"

// let's create a named function
var namedFunction = function(){
  return "Goodbye!";
};

// Pass the named function in the same way
// ...there's effectively no difference
// between this and the anonymous one
// except that, if you don't use the named
// one again, it's just sitting around in
// your namespace being useless while the
// anonymous one would be cleared out as
// soon as it is run
outerFunction( namedFunction );
//=> "Goodbye!"

This pattern may seem strange to you now but, again, wait until we get into using asynchronous functions and need to give them callbacks.

As a sneak preview, imagine that you've got a function called fadeIn which fades in an element on the web page over 500 milliseconds. When that animation is complete, you want to add a class to the element with jQuery's addClass method. To do so, you would need to pass in an anonymous function to fadeIn which would then call addClass inside of it. That anonymous function will be run upon completion of fadeIn:

$("p").eq(3).fadeIn(500, function(){
  $(this).addClass("some-class");
});

Passing Arguments to Functions

In JavaScript, it doesn't actually matter how many arguments a function takes. So myMethod and myMethod(arg1) are the same thing.

This has some interesting (crazy?) and important implications. It means the following:

  1. You can pass as few or as many arguments as you want to a JavaScript function and the interpreter will never throw an error at you. Instead, it passively sets any undefined arguments to, well, undefined.
  2. As you can imagine, there is no way to have "required" versus "optional" arguments just based on the function signature.
  3. You can access the passed-in arguments directly using the arguments property which is present inside of every function in JavaScript. It acts like an array (though it technically is not one).

Let's see this in action:

// It doesn't matter how many arguments a
//   function "is supposed to" have
myFunc = function( arg1, arg2 ){
  console.log( "arg1 is " + arg1 );
  console.log( "arg2 is " + arg2 );
}


// If we pass it too few arguments, it will
//   set any additional ones to `undefined`
myFunc( "foo" )
// arg1 is foo
// arg2 is undefined


// If we pass too many arguments, it doesn't
//   really care
myFunc( "foo", "bar", "baz" )
// arg1 is foo
// arg2 is bar


// We can define a function which dynamically
//   accesses the args it was passed by using
//   the `arguments` pseudo-array
// Note that we iterate through `arguments`
//   the long way because you don't actually
//    have access to any convenience helpers like
//   `forEach` since it's not really an array
var myDynamicArgsFunc = function() {
  for( var i = 0; i < arguments.length; i++ ) {
      arg = arguments[i];
      console.log( "arg" + i + " is " + arg );
    }
}


// Now it can work with any number of arguments
myDynamicArgsFunc( "foo", "bar", "baz" )
// arg0 is foo
// arg1 is bar
// arg2 is baz

Even though JavaScript gives you the power to be very sloppy about how you define your functions and the arguments they take, don't get sloppy yourself!!! It means that you need to be extra careful to make sure you aren't accidentally ignoring an argument or failing to define it.

Code Review

The important bits of code from this lesson

// "Constructor" style declaration
function foo(arg1, arg2...){ ... }

// Named function declaration
var foo = function(arg1, arg2...){ ... }

// a top-level function in the global space
var foo = function(words){ return words; }

// running it "function style"
foo("hi");                    //=> "hi"

// running it with `call` and `apply`
foo.call(any_caller, "other words");      //=> "other words"
foo.apply(any_caller, ["too many words"]);  //=> "too many words"

// storing a function in the object
var bar = { baz: function() { return "bazzzz" } };

// running the function "method-style" on the object
bar.baz();                    //=> "bazzzz"

// immediately invoke a function
(function() {
  var foo = "Hello world";
  return "I'm a function!";
})();
//=> "I'm a function!"

// Passing in an anonymous function
outerFunction( function() {
  return "Hello, World!";
});

// Pass in a named function
outerFunction(someNamedFunction)

// Iterate through all arguments passed to a function
var myDynamicArgsFunc = function() {
  for( var i = 0; i < arguments.length; i++ ) {
      arg = arguments[i];
      console.log( "arg" + i + " is " + arg );
    }
}

Wrapping Up

Part of the trick with learning JavaScript is just teaching your eye to start seeing the functions and separating when they are being passed around versus when they are actually called. That takes some time in the trenches... which you'll get soon enough.



Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

There are no additional resources for this lesson just yet!

Sorry, comments aren't active just yet!

Next Lesson: Basic Syntax: Looping