Function Declaration:
function myFunction() {}
const myFunction = function(){};
Function Expression:
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.
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!
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.
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) {
...
}
function greet(name, age) {
console.log('My name is ' + name + ' and I am ' + age + ' years old.');
}
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 );
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!!)
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
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());
Everything after this is a bonus!
(And things I wished people had told me!!)
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 =>
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
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.
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!!'
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'));
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.
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.)
function doSomething(){
if(processNeedsRestarting){
doSomething();
}
}
function needsReWriting(){
if (needsChange) {
needsReWriting = function(){
// new code
};
}
}
BEWARE infinite loops!!!!!
function myFunction() {} // <-- Function
var myObj = {
greet: function(){} // <-- Method
};
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'
call, apply and bind are demonstrated later in the context section
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);
}
}
let shared = 0; // <-- can be shared
function add(n) {
let inner = 0; // <-- cannot be shared
shared += n;
}
function minus(n) {
shared -= n;
}
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'
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
Taken from 'JavaScript: The Weird Parts'
Affects var and function declarations only
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
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);
To ensure consistency, we should make our objs with a function!
// 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!'"
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
function Dog(name){this.name = name}
const benji = new Dog('Benji');
console.log(benji instanceof Dog); // true
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'"
So, let's have a look at what that looks like...