Functions For Everything
Funcitonal Programming is an approach to programming. The principle of fp is that everything is a function.
Creating data? Use a function.
Need an object? Get it from a function.
Have repeatative tasks? Wrap them in functions.
Need a function? Wrap it in a function that creates and returns functions.
- Functions For Everything
Functions Have "this"
this
is a special keyword in javascript when leveraging functions.
this
is a variable that refers to the "parent" that the function is used by. More on this
later on.
// here, 2 functions bound to keys in an object
// 1 as an arrow function variant
// 1 as the more classical named variant
const obj = {
arrowFn: () => { let id = 123; console.log(`arrow this.id: ${this.id}`); console.log(`arrow inner id: ${id}`) },
classicalFn: function classical(){ let id = 123; console.log(`classical this.id: ${this.id}`); console.log(`classical inner id: ${id}`) },
id: 234
}
obj.arrowFn()
// arrow this.id: undefined
// arrow inner id: 123
// undefined
obj.classicalFn()
// classical this.id: 234
// classical inner id: 123
// undefined
The arrow style of functions do not refer to the parent this
in the same way that a named function expression does.
Here, the obj
is the this
for the expression-style of a function. When referring to this.id
, the expression version uses the obj.id
rather than the id
declared in the classical function itself.
Pure Functions
Pure functions are functions that
- have no "side effects": they do one thing and one thing only
- return the same result based on the same input
- in practice take an input, a parameter
- in practice return a result
Functions Returning Functions
This can be a handy application of functions.
Here, a function can return a function.
// inspecting this principle
/*
1. here, a function
*/
function add(a,b){ return a + b}
console.log(add.toString())
// function add(a,b){ return a + b}
/*
2. here a function that returns a function
*/
function curryAdd(val){
return function add(valTwo){ console.log(val + valTwo) }
}
console.log(curryAdd.toString())
// function curryAdd(val){
// return function add(valTwo){ console.log(val + valTwo) }
// }
/*
3.
*/
const addTwo = curryAdd(2)
console.log(addTwo.toString())
// function add(valTwo){ console.log(val + valTwo) }
addTwo(3)
// 5
function prefixer(strOne){
return function (strTwo){
return `${strOne} ${strTwo}`
}
}
// first application of the "prefixer" function
// used in 2 different ways
// these are variables that contain functions with 1 string "loaded" into the function
const sayHiTo = prefixer('Hello')
const sayByeTo = prefixer('Goodbye')
console.log(sayHiTo('Sal'))
// 'Hello Sal'
console.log(sayHiTo('Mark'))
// 'Hello Mark'
console.log(sayByeTo('Quang'))
// 'Goodbye Quang
Functions Are Expressed And Declared
Function Expressions With The Function Keyword
// un-named
function(params){
// function statement goes here
}
// named
function doSomething(params){
// function statement goes here
}
Functions With The Arrow Declaration
Functions can be declared as arrow functions.
These have been reffered to as "fat arrow" functions.
These have also been reffered to as "lambda" functions.
const add = (valOne, valTwo) => { return valOne + valTwo }
// a version with the implicit return
with no curly brackets,
const add = (valOne, valTwo) => valOne + valTwo
Functions Can Be Hoisted
Hoisintg is when the js interpreter "moves" declared functions to the "top" of the logic.
Hoisting With Declarations
runnable()
// logs "hello there!"
function runnable(){
console.log('hello there!')
}
Not Hoisting With Expressions
runnable()
// Uncaught ReferenceError: runnable is not defined
const runnable = () => console.log('hello there!')
Functions Have Arguments
The Arguments Objects
An arguments variable is available in functions - arguments
is a reserved word in js!
function add(){
console.log(arguments)
console.log(arguments[0] + arguments[1])
}
add(3,9)
// [Arguments] { '0': 3, '1': 9 }
// 12
Rest Parameters
Rest Parameters are another way to access function arguments.
function add(...args){
console.log(args)
console.log(args[0] + args[1])
}
add(3,9)
// [ 3, 9 ]
// 12
There are a few details to note with the rest parameters.
Rest Parameters can be used with named parameters. When other parameters are named, the "spread" style "rest" parameters MUST BE NAMED LAST, as sort of a "catch all" for "all remaining parameters".
function add(firstArg, ...otherArgs){
console.log(otherArgs)
console.log(firstArg + otherArgs[0])
}
add(12, 19)
// [ 19 ]
// 31
Three Helpful Methods - Call, Bind, And Apply
Each function instance include these methods, along with other details.
Call
Call can be used to pass along a function's this context.
Call is a method on a function.
Call can take params.
.call
can be thought of as "call the function and pass these params".
//
// example 1
//
function returnThisId() { console.log(this.id) }
function buildIdOffsetterFn() {
return (offset) => {
console.log(this.id + offset)
}
}
const smallObj = { id: 999 }
const smallObj2 = { id: 2 }
returnThisId.call(smallObj2)
// 2
returnThisId.call(smallObj)
// 999
returnThisId.call({id: 'hand-coded in param'})
// hand-coded in param
const returnIdAndOffset = buildIdOffsetterFn.call(smallObj)
returnIdAndOffset(1) // prints 1000 (999 + 1)
//
// example 2
// passing a starting "this" reference AS the first parameter of the call method
//
function addToThis(a){
return this.num + a;
}
function addAFew(a,b,c){
return this.num + a + b + c;
}
let o = {
num: 2
}
let bigO = {
num: 14
}
let newNum = addToThis.call(o,4);
console.log({newNum})
// { newNum: 6 }
// params can be added as a list
let biggerNum = addAFew.call(o,2,3,4)
console.log({biggerNum})
// { biggerNum: 11 }
Bind
Bind is a method that makes a new function.
The
/*
does NOT give the result when logged result
below, returns a "bound" function
binds the function to the bind recieved object
*/
function addToThis(a){
return this.num + a;
}
let o = {
num: 2
}
let bindOne = addToThis.bind(o,4);
let addToO = addToThis.bind(o);
console.dir(addToO)
console.dir(addToO.toString())
let addedTo0 = addToO(14)
console.log({addedTo0})
let personOne = {
name: 'Joe',
job: 'mailman'
};
let personTwo = {
name: 'Wanda',
job: 'wizard'
};
function talkAboutMe(favFood){
console.log(`** TALK ABOUT ME THIS`)
console.log(this)
return `I'm ${this.name} and I'm a ${this.job} and my fav food is ${favFood}`
}
const aboutJoe = talkAboutMe.bind(personOne)
console.log(aboutJoe('pizza'))
const aboutWanda = talkAboutMe.bind(personTwo)
console.log(aboutWanda('salmon'))
Apply
Appply is similar to the .call()
method.
The apply method, though, tales an array of arguments instead of a bunch of params:
let o = {
num: 2
}
let bigO = {
num: 14
}
function addAFew(a,b,c){
return this.num + a + b + c;
}
let argsArr = [3,4,5]
let bigAgain = addAFew.apply(o,argsArr);
console.log({bigAgain})
// { bigAgain: 14 }
let biggerArgsArr = [4,5,6]
let evenBig = addAFew.apply(bigO,biggerArgsArr);
console.log({evenBig})
// { evenBig: 29 }