Functions

Why functions?

  • Allows us to write a plan for handling things
  • Allows that plan to be passed around to where it is needed
  • Has a mechanism to trigger the enacting of that plan 
    • Without them we couldn't DO anything
  • Has the ability to abstract away details we don't know yet so we can provide them at the time that we run the code
  • Functions promote code re-use
    • As opposed to writing the same code over and over we write it once and call it into play in all the places we need it

How should I think of functions

  • Functions are a PLAN
  • They are a plan for how to handle something that will happen at a later date
    • in circumstances we don't know (context)
    • using information we don't yet have (arguments & parameters)
  • The are a way of performing the same actions again and again.
    • Results may vary depending on input

So, functions...

  • Are there to make code run
  • Can be used to suspend that code and run it later

What runs my code? **

  • We said that "functions run code" but I didn't write one and my code still runs! How??
  • Similar to other languages, the browser runs a function called 'Main'
  • It runs the code you put in your 'entry point' .js file as if it were a part of that main function body
    • So if you just write code into a file the 'Main' function runs that code for you
      • (N.B. you don't write a main function, the browser will)

How do I make one?

Function Declaration:

  function myFunction() {}  
  const myFunction = function(){};  

Function Expression:

How do I reference it?

Once you declare a function you can reference it by its name the same way you do with variables:

function add(a, b) {
    return a + b;
}

console.log(add.name); // 'add'
console.log(add); // See below...

This will print out the your function in the console as it is written in your script (because log calls add's toString method).

> function add(a, b) {
    return a + b;
}

This ability to pass functions around to be executed later is what we mean when we say that 'functions are first class citizens'. It is critical to JavaScript programming.

Anonymous functions

You can declare a function without a name:

function(a, b) {
    return a + b;
}
button.addEventListener('click', function(){...});

Usage:

BUT you will get '(anonymous function)' in the call stack, which isn't helpful when debugging!

How do I make it run?

You call a function using its name followed immediately by two parenthesis:  name();

function greet() {
    console.log('Hello!');
}

const greet2 = function greet2() {
    console.log('Hello World!');
}

greet(); // Writes 'Hello!' to the console.
greet2(); // Writes 'Hello World!' to the console.

Parameters and Arguments

Parameters

When you create your function (plan) and you don't know certain key pieces of information yet, you can create the plan and leave placeholders for the info you'll get later. These are called parameters:

function greet(name, age) {
    ...
}
  • Here name and age are parameters
  • Note that they are a comma separated list!
  • You can then use them like pre-declared variables inside the function:
function greet(name, age) {
    console.log('My name is ' + name + ' and I am ' + age + ' years old.');
}

Arguments

When it comes to executing your function (plan), assuming you now know the information you required, you can now call your function and pass that information in as arguments:

greet('James', 39);

Which will result in:

'My name is James and I am 39 years old.'

being written out to the console.

 

N.B. Arguments, like parameters, are a comma separated list.


function myFunc(parameter0, parameter1) { ... }
                   ^                 ^
                   |                 |
       myFunc( arguments0 ,  arguments1 );
  • Arguments go into their parameter slots in order
    • arguments[0] goes into parameter slot 0, etc.
    • (unless you give it to a third-party's function, then it might not be that way)
  • JS doesn't have 'named arguments'. We achieve this by passing objects as arguments *

The arguments object

  • You can see what arguments a function was called with using the arguments object, which is provided for you when the function is called:
function greet(){
    console.log(arguments);
}

greet('James', 39);

Which will result in:

['James', 39]

being written out to the console.

(The arguments object does not exist in ES6 Arrow functions!!)

The return keyword

If you want a function to do more than just incidental code, but instead to 'give you something back', to produce/construct something, then we use the return keyword:

function tellMePassword() {
    const secretPassword = 'Balloon';
    return 'The secret password is: ' + secretPassword;
}

const phrase = tellMePassword();
console.log(phrase); // 'The secret password is: Balloon'

Nothing executes AFTER a return statement - it causes the function to exit. It is like break in a loop

Resolving

A major concept in code is that functions produce an output - or to put it another way - executing a function RESOLVES to something!

function toBeResolved(){
    return <what this resolves to>;
}

console.log(toBeResolved());
  • In the code above, executing toBeResolved results in *SOMETHING*: Maybe it's a string, or a number, etc.
  • It resolves to what it returns 
    • If nothing is returned then it resolves to undefined
  • In the code above, whatever executing toBeResolved resolves to, that product is then passed to the console's log function to be toString()'ed and written out to the console itself.

END OF BASIC INTRO

Everything after this is a bonus!

(And things I wished people had told me!!)

Other types of function

NOT normal functions

This is an es6 generator function and an es6 arrow function, we will cover them later, but for now understand that they don't work like a normal functions.

function* idMaker() {
  let index = 0;
  while (index < 3) {
    yield index++;
  }
}

Look out for the * (and the yield keyword)

// ES6 Arrow Function
(a,b) => a + b;

Many different formats! Look out for the =>

More NOT normal functions

es7 async await also operates differently. Async functions are there to make asynchronous programming easier to follow.

async function myAsyncFunction(){
    const people = await axios.get('/people')
    const vacancies = await axios.get('/vacancies')
    // etc. 
}

Look out for the async (before the function) and await (internal) keywords

Different ways of 'calling' (executing) functions

IIFEs - functions that run automatically

You can use an IIFE (Immediately Invoked Function Expression) which will execute right there and then and resolve to its product:

// Basic IIFE
(function(){
    ...
}()); // })(); is also OK

//Passing arguments to IIFE
(function(parameter){
    ...
}('argument'));
window.JMS = window.JMS || {};
JMS.ATM = (function(){

    function privateFunction () {
        //...
    }

    function publicMethod () {
        console.log(privateFunction());
    }

    return {
        publicMethod: publicMethod
    };
}());

Write a function declaration; put executing parenthesis after it; wrap the whole lot in a set of parenthesis (Grouping Operator: https://goo.gl/TccpFu); add semicolon after.

Sequenced Execution

(for currying)

If the product of a function is another function then you can execute that immediately, like so:

const sentence = part1('Hello')('world'); // <-- Sequenced execution

console.log(sentence); // 'Hello world!!'

Function Factories

 
const thingsILike = function(newThing){
  return function(nextNewThing) {
    const result = 'I like '.concat(newThing)
        .concat(' and ').concat(nextNewThing);
    return result;
  };
};

const koala = thingsILike('koalas');
console.log(koala('tapirs'));
console.log(koala('dolphins'));

Function Chaining

If the function is correctly setup and executed, you can execute it repeatedly with what's called function chaining, like so:

take(5).add(2).multiply(3).value();

//or

$('#main')
    .find('.points')
    .css('border', '1px solid red')
    .end();

We'll need to learn about this and the pseudo-classical pattern and the Constructor pattern before we know how to do it ourselves, and we'll cover that on slides 41-43.

Callback Functions

With callbacks, you give (pass) your function to another function [as an argument] and it uses it, calling it when it is ready:

function myFunc() { .... }

$('.doButton').on('click.do', myFunc);

// OR

$('.doButton').on('click.other', function(){
  ...
});

These functions are also known as handler functions (click handler, etc.)

  • N.B. You can call a function from within itself
  • A function can over/rewrite itself
function doSomething(){
    if(processNeedsRestarting){
        doSomething();
    }
}
function needsReWriting(){
    if (needsChange) {
        needsReWriting = function(){
            // new code
        };
    }
}

BEWARE infinite loops!!!!!

Some minor points

More about functions

Difference between a method and a function

  • function - the language construct - the object with the execution context.
  • Method is a special kind of function which has two special attributes:
    • Method is a function associated with an Object, a Constructor Function or a Class
    • A Method function has access to the data present in its Construction function
function myFunction() {} // <-- Function

var myObj = {
    greet: function(){} // <-- Method
};

arguments looks like an array!

Is it??

  • No! (Although it looks like one if you console log it!)
  • It's because the way the toString method was written.
  • It's actually described as an 'array-like object'
  • Here's a demo of how to fix it:

Passing arguments

  • side-note: object family quirk
  • JS is both 'pass-by-value/copy' (for primitives) AND 'pass-by-reference' (for object family members)
  • Why that's important:
 let primitive = 2;
 let obj = { name: 'frank' };

function addOne(num) {
  num += 1; // changes value of num but not primitive
}

addOne(primitive);
console.log(primitive); // 2

function changeName(person, name) {
  person.name = name;
  // person = { id: 123 } //changes person NOT obj (it changees the object held in person)
}

changeName(obj, 'dave');
console.log(obj.name); // 'dave'

The Function structure

Function Attributes

  • length - shows the amount of parameters a function was declared with (aka 'arity')
  • name - tells you the name you gave it
  • prototype - we'll use this later. This is part of how JavaScript's Prototype system - a unique feature of the language
  • Other ones are bad.
  • You can add your own properties because functions are objects under the hood
    • in OOP those are called 'static'

Function Methods

  • call - executes a function (with optional context and a list of comma-separated arguments)
  • apply - same as call, except it lets you pass arguments as an array
  • bind - creates a new function with a context locked in, and any additional arguments you want it to be passed on execution
  • toSource -  returns a string representing the source code of the object (NON-STANDARD - Don't Use!)
  • isGenerator - tells you if the function is an es6 generator (NON-STANDARD - Don't Use!)
  • toString - writes the function out as a string

 

call, apply and bind are demonstrated later in the context section

Error Handling

What if it goes wrong?

If you're not sure about whether something will work you can use error handling

try {
    // An error occurs whilst executing this function. It 'throws an exception'.
    toDoSomething();
} catch (err) {
    console.log('Error: ' + err.message); // handle the error
} finally { // optional: executes whether error or not BEFORE anything afterwards does
    console.log('Either way, I\'m glad thats\'s over!');
}
try {
    var x = toDoSomething();
    if (typeof x !== 'string') throw new Error('BAD_FORMAT');
} catch (e) {
    if (e.message === 'BAD_FORMAT') {
        x = correctValue(x);
    } else {
        console.log('Error: ' + e.message);
    }
}

Try/Catch Continued: finally

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#The_finally_clause

  • Finally is for code you NEED to run REGARDLESS of an error
  • Example: You try to programmatically read a file. Regardless of whether it works or not you need to turn the reader off afterwards
  • Code you put after the block may not run if:
    • unhandled error
    • a return value happens
  • finally is guaranteed to happen
    • and actually lets us override return values
    • pen

Scope

Function Scope

  • Initial scope is the global scope (window, global, self or globalThis)
  • Each function creates its own internal scope (local scope), creating a scope chain
  • From inside a function you can reference entities;
    •  outside your function body (going up the scope chain)
    • immediately inside your function body
    • BUT you cannot reference entities created in a 'nested' function inside of your function  (down the scope chain)
  • ​​SO, it matters where you write things in JS, that's called 'Lexical Scoping'
    • ​Remember that let and const are 'block-scoped', so they won't reach the window (unless intentionally you put them there).

Function Scope Visualised

Sharing Data between Functions

  • If you want to use variables in different functions then make sure that they are declared one level above those functions in the scope chain (known as 'raising up')
let shared = 0; // <-- can be shared

function add(n) {
  let inner = 0; // <-- cannot be shared
  shared += n;
}

function minus(n) {
  shared -= n;
}

Closures

  • A closure (https://goo.gl/smW0Ki) occurs when a function references something in the upper scope.
  • It can be very useful for privacy
  • This can cause problems with loops
function foo(secret) { // passed as argument
    var secret = 1; // or you could have as a contained variable
    
    return function getSecret(){  // <-- This function is a closure
        console.log('inside the closure', secret); 
        return secret; // <-- Because it 'closes over' secret, from the parent scope
    }
}
// secret doesn't exist out here...

let functionWithSecretIn = foo('cat');
console.log('getting secret: ', functionWithSecretIn()); // 'cat'

Hiding Data: Encapsulation

  • JavaScript has no private or protected keywords, and we often need to hide data. (You wouldn't want a user to be able to manually adjust their credit balance?!)
  • Encapsulation, or 'information hiding' is what we use instead when we
    • lock information inside the scope of a main function,
    • but then use a closure to make it accessible to another sub-function declared in the same scope,
    • and have that sub-function publicly available.
function Bank() {
    let balance = 1;
    let authToken = null;
    return {
       getBalance: function getBalance(){  // <-- This function is a closure
        if (authToken) {
          console.log(balance); // <-- Because it 'closes over' balance, from the parent scope (line 2)
        }
       },
       authenticate: function authenticate(token){
          if (tokenValid(token)) authToken = token; // tokenValid would be somewhere else in the code
       },
       signOut: function signOut(){ authToken = null; }
    };
}

const myBank = Bank();
Bank.balance = 4; // <-- can't see balance to alter it
myBank.balance; // undefined
myBank.getBalance(); // undefined
myBank.authenticate(validToken);
myBank.getBalance(); // 1

Scope In Javascript

Scope Side-notes

The Two Phases of JS Functions

Taken from 'JavaScript: The Weird Parts'

Affects var and function declarations only

Hoisting

  • When JavaScript function is run it is done in two phases:
    • declaration (reserving memory for variables and functions)
    • initialisation (assigning values)
  • During the first phase the variables and functions declarations are created, so essence they appear before anything else, effectively like they were written at the top of the function - this is called hoisting.
  • https://codepen.io/jmsherry/pen/yMbKKe
  • Function expressions aren’t hoisted (because their variable declaration is instead of the function)

Implicit Globals

  • When creating variables be sure to create with the var, let and const keywords, otherwise scope will write and create variables in the global namespace - this is NOT GOOD!!

Variable/Parameter Visibility (inc. shadows)

const a = 'a';
const c = 'c';

function fn1(a) {
  console.log(a);
  // is 'b' not 'a' because the parameter 
  // 'shadows'/blocks the outer var

  const c = 'otherc'; // block-scoped, so won't collide with line 2
  console.log(c); // 'otherc'
}

fn1('b');

When variable/function/param has the same name as something higher up the scope chain it is called a 'shadow' and it is confusing

Context

A question:

function makePerson(firstName, lastName) {
  return {
    firstName: firstName,
    lastName: lastName,
    greet: function greet(me) {
      console.log(`Hi! I'm ${me.firstName} ${me.lastName}!`)
      // How can I access the rest of 
      // this object from inside this method?
    },
  };
}

const james = makePerson('James', 'Sherry');
const ian = makePerson('Ian', 'Whiscombe');

// to avoid having to do this, because error prone!
james.greet(james);
  • and how do I avoid creating multiple greet functions?
  • Answer: context (the 'this' keyword)

To ensure consistency, we should make our objs with a function! 

Function Context

  • Whenever a function is called it is given a context by the system
    • ​this is called the 'implicit context'
  • The context is an injected variable (like arguments) which is aimed at the object that the function is a sited on
  • This allows introspection of those objects from within those methods
  • The default context is the global context (window object in the browser; global in node, self in worker) is the default. (globalThis will refer to the right one)
  • Context then changes depending where something is written
    • ​aka its 'call-site'
  • A link to the context object that that a function is operating on is provided through the this keyword

The this keyword

  • The 'this' keyword is a reference to the 'context'
    • just writing this in code will get you the object that function is on
  • Its bindings are NOTORIOUSLY BAD (and may make you cry!)
  • As we said, if no other context is provided then this points to the global object (window/global/self)
    • EXCEPT if strict mode (https://goo.gl/vVS7Or) is in play, when it becomes undefined (demo)
      • Strict mode code has the 'use strict'; pragma at the top of the function or script (depending)
      • es6+ modules are automatically in strict mode
  • ​​The binding is updated dependant on the 'call site' (see next)

this keyword call-site

Bad binding example

Changing the context

  • You can change the function context yourself
  • This is known as setting 'Explicit context'
  • You can use:
    • call
      • allows you to inject a different value for this when executing
      • arguments are passed as you would to a function
        • fn.call(thisArg, arg1, arg2);
    • apply
      • same as call but arguments are passed as an array
        • fn.apply(thisArg, [arg1, arg2]);
    • bind
      • creates a new function with the value of this locked to a particular value

Constructor Functions

Constructors

  • Constructor functions create other things
  • They start (by convention) with a capital letter
  • The use the this keyword to bind things into what they create
  • They are often used with the new keyword (https://goo.gl/HOoMq1)
  • They are a major part of OOP (Object-Oriented Programming)
  • They are often an artificial  replacement for classes in other languages
// This is an object constructor
// (class) used in OOP

function Dog(name) {
    this.name = name;
}

var benji = new Dog('benji');
var spot = new Dog('spot');

console.log(benji); // { name:  'benji' }
console.log(spot); // { name:  'spot' }


// You may also see

Dog.prototype.talk = function talk(){
    console.log(this.name + " says 'Woof!'");
};

benji.talk(); // "benji says 'Woof!'"
spot.talk(); // "spot says 'Woof!'"

instanceof

If you say typeof on any object you'll get 'object' BUT you want to know what 'type' (or 'species') of object it is. Is it a 'dog object' or a 'person object'. So you need a keyword to tell what it's constructor was:

 

The instanceof keyword will return true or false as to whether an object is an instance of a particular class

 

function Dog(name){this.name = name}
const benji = new Dog('Benji');
console.log(benji instanceof Dog); // true

What the new keyword does

  1. A call to "new Foo()" creates a new object instance, said to be of type "Foo"
  2. The object instance is assigned a prototype of "Foo.prototype" (So the prototype remains in the constructor function and the object gets a reference to it, __proto__, or 'dunder proto')
  3. The "Foo" function is then invoked with the new object instance assigned to "this" in that function invocation
  4. The "Foo" object instance is returned from the function call

 

  • this in constructors is bound to the new object instance
  • You can test if an object is of a certain type, using the instanceof keyword:
function Dog(name){this.name = name}
const benji = new Dog('Benji');
console.log(benji instanceof Dog); // true

Method Chaining - How to...

  • The pattern you just saw on the previous two slides is known as the pseudo-classical pattern.
  • If you use that pattern and return this (which would be that object instance) in each of the methods, then you can chain function calls
function Dog(name) {
    this.name = name;
    // use of the new keyword
    // implicitly returns 'this' here
}

Dog.prototype.talk = function(){
    console.log(this.name + " says 'Woof!'");
    return this;
};

Dog.prototype.sleep = function(){
    console.log(this.name + " says 'ZZzzzz'");
    return this;
};

const benji = new Dog('benji');

benji.talk().sleep();

// "benji says 'Woof!'"
// "benji says 'ZZzzzz'"

Asynchronicity

Stack & StackTrace

  • In JavaScript, code naturally runs SYNCHRONOUSLY, line by line
  • Functions, as we've seen, can be called by one another
  • As each new function is called, the caller function pauses
  • The callee function then starts to run
  • This results in a 'stack' (You can see it in your dev tools)
  • When it goes wrong, JavaScript gives you a 'stack trace', showing you the path that it went down until it hit the exception point.
    • (N.B. Stack traces are not unique to javascript)
  • When the final function (with no children) is called, the stack resolves in reverse order back to an empty stack

A Stack Trace

Going Asynchronous!

  • When we run a javascript task it blocks the browser from doing other things. (Links don't work, etc.)
  • So on things which are long or uncertain (clicks, server calls, etc.) we use web APIs to put them in a queue so that they don't do block interaction.
  • This is called asynchonous code
  • JavaScript's event loop allows them back when the stack is clear (i.e. not busy!)

 

So, let's have a look at what that looks like...

Asynchronous Visualised

Asynchronous cont...

Using this in Asynchronous code

  • Here's the problem with this (and how to fix it):  https://codepen.io/jmsherry/pen/rymGpX
    • The red errors are the codepen console trying not to log the entire window object, so red error = window

The End.

JS: Functions

By James Sherry

JS: Functions

A good look at functions

  • 3,093