Functional JavaScript

Building Modern User Interfaces



alecaivazis.github.io/functional-javascript

Alec Aivazis


alec.aivazis.com
github.com/AlecAivazis

roadmap

  • Basic modern javascript (es6) syntax
  • Functional JavaScript
  • Quick intro to functional React

Syntax

Variable Declaration

// hoisted, functionally scoped
var asdf = 'asdf'
// cannot be re-declared, block scoped
let foo = 'bar'
// cannot be changed (sometimes), block scoped
const bar = 'baz'

Better Mental Model

// cannot be re-declared
let foo = 'bar'
// cannot be changed (sometimes)
const bar = 'baz'

Functions

// does this act like a var, let,
// or const?
function myFunc(name) {
    return 'Hello ' + name
}
// now we can decide!
const myNewFunc = (name) => (
    'Hello ' + name
)

// sometimes implicit returns are bad
const myNewestFunc = (name) => {
    return 'Hello ' + name
}

destructuring

var data = {'key': 'bar'}
// grab the key entry
var key = data.key
function myFunc(args) {
    console.log(args.foo)
}
myFunc({foo: 'bar'})
const data = {'key': 'bar'}
// grab the key entry
const {key} = data
function myFunc({foo}) {
    console.log(foo)
}
myFunc({foo: 'bar'})

spread

var mockData = {
    foo: 'bar',
}
var newObject = Object.assign({},
    mockData,
    {
        'bar': 'baz'
    }
)
const mockData = {
    foo: 'bar',
}

const newObj = {
    ...mockData,
    'bar': 'baz',
}

Functional Programming

in

JavaScript

The Function Object

const myFunction = () => 'hello world'

console.log(myFunction)

combinators

map

[1,2,3][1,4,9]

In the imperative world...
// we want to square each number in this list
const numbers = [1,2,3]

// we'll collect the results in this list
const results = []

// visit each element in the array
for (const i = 0 ; i < numbers.length ; i ++) {
    // grab the i'th entry
    const entry = numbers[i]
    // add the square to the list
    result.append(entry * entry)
}

// results == [1, 4, 9]
With our fancy new combinator
// we want to square each number in this list
const numbers = [1,2,3]
// square each number in a single line!
const results = numbers.map(entry => entry * entry)

// results == [1, 4, 9]

filter

[1,2,3,4][2,4]

Then
// we want to grab every even number in this list
const numbers = [1,2,3,4,5,6]
// collect results here
const results = []

// go over every number
for (const i = 0 ; i < numbers.length ; i ++) {
    // grab the i'th number
    const number = numbers[i]
    // if the number is even
    if (number % 2 == 0) {
        // add it to the list
        results.append(number)
    }
}

// results == [2, 4, 6]

Now
// we want to grab every even number in this list
const numbers = [1,2,3,4,5,6]

// collect results here
const results = numbers.filter(entry => entry % 2 == 0)

// results == [2, 4, 6]

reduce

[1,2,3]6

The imperative way
// now we want to add each number in the list
const numbers = [1,2,3]

// start off at zero
const total = 0

// visit every item
for (const i = 0 ; i < numbers.length ; i ++) {
    // grab the i'th number
    const number = numbers[i]
    // add the number to the running total
    total += number
}

// total == 6
Using reduce,
// the numbers to sum up
const numbers = [1,2,3]

// combine them in a single line
const total = numbers.reduce((sum, entry) => sum + entry, 0)

// total == 6

building web interfaces

HTML

<div>
    <button id="button">
        hello
    </button>
</div>

jQuery

<div>
    <button id="button">
        hello
    </button>
</div>
$("#button").on('click', (event) => {
    this.style('color', 'red')
})

React
 
<div>
    <div class="navigation">
        <ul>
            <li> nav1 </li>
            <li> nav2 </li>
            <li> nav3 </li>
        </ul>
    </div>
    <div class="content">
    </div>
    <div class="footer">
    </div>
</div>

<script src="app.js">
<div>
    <Navigation>
    <Content>
    <Footer>
</div>

More syntax!

import React from 'react'
import ReactDom from 'react-dom'
const MyElement = () => (
    <div>
        hello world!
    </div>
)
// rendered with <MyElement />
// render over a specific element
ReactDom.render(<MyElement />, document.getElementById('app'))
// rendered with <MyElement />

inline expressions

const name = "John"

const MyElement = () => (
    <div>
        Hello {name}!
    </div>
)

// rendered with <MyElement />

passing data

const MyElement = ({name}) => (
    <div>
        Hello {name}!
    </div>
)

// rendered with <MyElement name="John"/>


using map in jsx

// <MyElement numbers={[1,2,3]} />

const NumberList = (numbers) => (
    <ul>
        { numbers.map(number => (
            <li>
                {number}
            </li>
        ))}
    </ul>
)

putting it all together

const itemsInCart = [{name: 'Steak', price: 25.75}, ...]
// the root shopping cart component
const ShoppingCart = (items) => {
    // figure out the total cost of all the recipes
    const totalCost = items.reduce((sum, {price}) => sum + price, 0)
    // render the component
    return (
        <div>
            {items.map(cartItem => (
                <ShoppingCartItem {...cartItem}/>
            ))}
            <div>
                Total cost: {totalCost}
            </div>
        </div>
    )
}

// rendered with <ShoppingCart items={itemsInCart} />
const itemsInCart = [{name: 'Steak', price: 25.75}, ...]

// ...
const totalCost = items.reduce((sum, {price}) => sum + price, 0)

// ...
<div>
    {totalCost}
</div>
const itemsInCart = [{name: 'Steak', price: 25.75}, ...]

// ...
{items.map(cartItem => (
    <ShoppingCartItem {...cartItem}/>
))}
// ...
const ShoppingCartItem = ({name, date}) => (
    <div>
        <div> {name} </div>
        <div> {date} </div>
    </div>
)
Thanks!