4,461
edits
functional programming with JavaScript heading |
|||
Line 518: | Line 518: | ||
= Functional Programming with JavaScript = | = Functional Programming with JavaScript = | ||
== What it means to be functional == | |||
JavaScript functions are first-class citizens -- they can do the same things that variables can do. Functions can represent data. e.g. | |||
<source lang="js"> | |||
var log = function(msg) { | |||
console.log(msg); | |||
} | |||
log("In JS functions are variables"); | |||
// equivalent to | |||
const log = msg => { console.log(msg); }; | |||
</source> | |||
we can add them to objects: | |||
<source lang="js"> | |||
const obj = { | |||
msg: "they can be added to objects like variables", | |||
log(msg) { console.log(msg); } | |||
}; | |||
obj.log(obj.msg); | |||
</source> | |||
add them to arrays: | |||
<source lang="js"> | |||
const messages = [ | |||
"They can be inserted into arrays", | |||
message => console.log(message), | |||
"like variables", | |||
message => console.log(message) | |||
]; | |||
messages[1](messages[0]); // They can be inserted into arrays | |||
messages[3](messages[2]); // like variables | |||
</source> | |||
send to other functions as arguments: | |||
<source lang="js"> | |||
const insideFn = logger => { | |||
logger("They can be sent to other functions as arguments"); | |||
}; | |||
insideFn(message => console.log(message)); | |||
// They can be sent to other functions as arguments | |||
</source> | |||
returned from other functions: | |||
<source lang="js"> | |||
const createScream = function(logger) { | |||
return function(message) { | |||
logger(message.toUpperCase() + "!!!"); | |||
}; | |||
}; | |||
const scream = createScream(message => console.log(message)); | |||
scream("functions can be returned from other functions"); | |||
scream("createScream returns a function"); | |||
scream("scream invokes that returned function"); | |||
// FUNCTIONS CAN BE RETURNED FROM OTHER FUNCTIONS!!! | |||
// CREATESCREAM RETURNS A FUNCTION!!! | |||
// SCREAM INVOKES THAT RETURNED FUNCTION!!! | |||
// equivalent to (using arrows) | |||
const createScream = logger => message => { | |||
logger(message.toUpperCase() + "!!!"); | |||
}; | |||
// when more than one arrows exist, there's a higher-order function | |||
</source> | |||
</source> | |||
== Imperative versus declarative == | |||
<source lang="js"> | |||
// making a string URL friendly | |||
// IMPERATIVE way | |||
const string = "Restaurants in Hanalei"; | |||
const urlFriendly = ""; | |||
for (var i = 0; i < string.length; i++) { | |||
if (string[i] === " ") { | |||
urlFriendly += "-"; | |||
} else { | |||
urlFriendly += string[i]; | |||
} | |||
} | |||
console.log(urlFriendly); // "Restaurants-in-Hanalei" | |||
// DECLARATIVE way | |||
const string = "Restaurants in Hanalei"; | |||
const urlFriendly = string.replace(/ /g, "-"); | |||
console.log(urlFriendly); | |||
</source> | |||
Reference: [http://c2.com/cgi/wiki?DeclarativeProgramming Declarative Programming wiki -- more about d/p paradigm] | |||
Declaring DOM example | |||
<source lang="js"> | |||
// imperative approach | |||
const target = document.getElementById("target"); | |||
const wrapper = document.createElement("div"); | |||
const headline = document.createElement("h1"); | |||
wrapper.id = "welcome"; | |||
headline.innerText = "Hello World"; | |||
wrapper.appendChild(headline); | |||
target.appendChild(wrapper); | |||
// declarative approach using a React component | |||
const { render } = ReactDOM; | |||
const Welcome = () => ( | |||
<div id="welcome"> | |||
<h1>Hello World</h1> | |||
</div> | |||
); | |||
render(<Welcome />, document.getElementById("target")); | |||
</source> | |||
== Functional concepts == | |||
core concepts: immutability, purity, data transformation, higher-order functions, and recursion | |||
=== Immutability === | |||
data mutation | |||
<source lang="js"> | |||
let color_lawn = { | |||
title: "lawn", | |||
color: "#00ff00", | |||
rating: 0 | |||
}; | |||
function rateColor(color, rating) { | |||
color.rating = rating; | |||
return color; | |||
} | |||
console.log(rateColor(color_lawn, 5).rating); // 5 | |||
console.log(color_lawn.rating); // 5 | |||
</source> | |||
in JS, function arguments are references to the actual data; we can rewrite so it does not harm the original data | |||
<source lang="js"> | |||
const rateColor = function(color, rating) { | |||
return Object.assign({}, color, { rating: rating }); // take a blank object, copy copy to that object, and overwrite rating on the copy | |||
}; | |||
console.log(rateColor(color_lawn, 5).rating); // 5 | |||
console.log(color_lawn.rating); // 4 | |||
// use the spread operator to copy the color into a new object and then overwrite its rating: | |||
// equivalent to | |||
const rateColor = (color, rating) => ({ | |||
...color, | |||
rating | |||
}); | |||
</source> | |||
adding elements to an array | |||
<source lang="js"> | |||
let list = [{ title: "Rad Red" }, { title: "Lawn" }, { title: "Party Pink" }]; | |||
const addColor = function(title, colors) { | |||
colors.push({ title: title }); | |||
return colors; | |||
}; | |||
console.log(addColor("Glam Green", list).length); // 4 | |||
console.log(list.length); // 4 | |||
// use Array.concat to keep immutable | |||
const addColor = (title, array) => array.concat({ title }); | |||
console.log(addColor("Glam Green", list).length); // 4 | |||
console.log(list.length); // 3 | |||
// equivalent using spread operator | |||
// copies the original list to a new array and then adds a new object containing the color's title to that copy. | |||
const addColor = (title, list) => [...list, { title }]; | |||
</source> | |||
=== Pure Functions === | |||
function that returns a value that is computed based on its argument; treats arguments as immutable data, so nothing else is changed about the application | |||
e.g. impure function | |||
<source lang="js"> | |||
const frederick = { | |||
name: "Frederick Douglass", | |||
canRead: false, | |||
canWrite: false | |||
}; | |||
function selfEducate() { | |||
frederick.canRead = true; | |||
frederick.canWrite = true; | |||
return frederick; | |||
} | |||
selfEducate(); | |||
console.log(frederick); | |||
// {name: "Frederick Douglass", canRead: true, canWrite: true} | |||
// still the same result | |||
const selfEducate = person => { | |||
person.canRead = true; | |||
person.canWrite = true; | |||
return person; | |||
}; | |||
console.log(selfEducate(frederick)); | |||
console.log(frederick); | |||
// {name: "Frederick Douglass", canRead: true, canWrite: true} | |||
// {name: "Frederick Douglass", canRead: true, canWrite: true} | |||
</source> | |||
have this function take an argument (now pure) | |||
<source lang="js"> | |||
const frederick = { | |||
name: "Frederick Douglass", | |||
canRead: false, | |||
canWrite: false | |||
}; | |||
const selfEducate = person => ({ | |||
...person, | |||
canRead: true, | |||
canWrite: true | |||
}); | |||
console.log(selfEducate(frederick)); | |||
console.log(frederick); | |||
// {name: "Frederick Douglass", canRead: true, canWrite: true} | |||
// {name: "Frederick Douglass", canRead: false, canWrite: false} | |||
</source> |