Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JavaScript for PHP Developers

JavaScript for PHP Developers

Given at PHP Benelux 2015

Daniel Cousineau

January 23, 2015
Tweet

More Decks by Daniel Cousineau

Other Decks in Programming

Transcript

  1. JAVASCRIPT FOR PHP DEVELOPERS

    View Slide

  2. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    @dcousineau

    View Slide

  3. @croscon

    View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BASICS

    View Slide

  8. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PRIMITIVE TYPES
    • number
    • string
    • boolean
    • object
    • array
    • null
    • undefined
    • NaN

    View Slide

  9. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    OPERATIONS
    • Arithmetic
    • + - / *
    • Bitwise
    • & | ^
    • Boolean
    • && || !

    View Slide

  10. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BOOLEAN OR
    var first = "truthy" || "something else";
    //first == "truthy"
    var second = false || "something else";
    //second == "something else";

    View Slide

  11. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    TYPEOF
    • Returns variable type as a string

    View Slide

  12. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    TYPEOF
    typeof true; //??
    typeof 12; //??
    typeof "string"; //??
    typeof []; //??
    typeof {}; //??
    typeof NaN; //??
    typeof null; //??
    typeof undefined; //??
    function Foo() {}
    typeof Foo; //??

    View Slide

  13. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    TYPEOF
    typeof true; //"boolean"
    typeof 12; //"number"
    typeof "string"; //"string"
    typeof []; //"object"
    typeof {}; //"object"
    typeof NaN; //"number"
    typeof null; //"object"
    typeof undefined; //"undefined"
    function Foo() {}
    typeof Foo; //"function"

    View Slide

  14. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    OBJECT OR ARRAY?
    var obj = {}
    , arr = [];
    typeof obj == typeof arr; //true
    obj instanceof Array; //false
    arr instanceof Array; //true

    View Slide

  15. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    OBJECT OR ARRAY?
    // Is a given value an array?
    // Delegates to ECMA5's native Array.isArray
    _.isArray = Array.isArray || function(obj) {
    return Object.prototype.toString.call(obj) == '[object Array]';
    };

    View Slide

  16. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    NUMBERS
    Objects!

    View Slide

  17. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    NUMBERS
    var integer = 5;
    var float = 0.567;
    var big = 1234567.89
    float.toPrecision(2); //"0.57"
    big.toLocaleString("en-us") //"1,234,567.89"
    big.toLocaleString("nl-be") //"1.234.567,89"

    View Slide

  18. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    STRINGS
    Objects!

    View Slide

  19. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    STRINGS
    "Foo" + "Bar"; //"FooBar"
    var str = "Lorem Ipsum Dolor Sit Amet";
    str.toLowerCase(); //"lorem ipsum dolor sit amet"
    str.toUpperCase(); //"LOREM IPSUM DOLOR SIT AMET"
    str.split(" "); //["Lorem", "Ispum", "Dolor", "Sit", "Amet"]
    str.substring(6,9); //"Ips"
    new String("Lorem Ipsum Dolor Sit Amet") == str; //true

    View Slide

  20. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    STRINGS – NUMBERS
    parseInt("56"); //56
    parseInt("42.567"); //42
    parseInt("asdf"); //NaN
    parseInt("5a6"); //5
    parseFloat("24.68"); //24.68
    parseFloat("asdf"); //NaN
    parseFloat("24a"); //24

    View Slide

  21. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    OBJECTS
    • “Dictionary” / “Associative Array”
    • key: value or 'key': value
    • Without ': A-Z0-9 only
    • Does not keep intrinsic ordering
    • Accessed keys using . (dot) or [] notation

    View Slide

  22. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    OBJECTS
    var object = {
    foo: 'value',
    'complex key': 0,
    bar: {
    nested: true
    }
    };
    object.foo; //'value'
    object['complex key']; //0
    object.bar.nested; //true
    object.bar['nested']; //true
    object['bar'].nested; //true
    object['bar']['nested']; //true

    View Slide

  23. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    IN
    var test = {
    foo: 'value',
    bar: 'value',
    baz: 'value'
    }
    for (var key in test) {
    console.log(key + ": " + test[key]);
    }
    //PRINTS:
    //foo: value
    //bar: value
    //baz: value

    View Slide

  24. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ARRAYS
    • Special Object
    • Numeric ‘keys’ only
    • keeps intrinsic ordering

    View Slide

  25. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ARRAYS
    var arrayShort = [
    'one',
    'two'
    ];
    arrayShort[2] = 'three';
    var arrayLong = new Array();
    arrayLong[0] = 'one';
    arrayLong[1] = 'two';
    arrayLong[2] = 'three';
    //arrayShort: ["one", "two", "three"]
    //arrayLong: ["one", "two", "three"]

    View Slide

  26. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ARRAYS
    var arr = [1,2,3,4,6];
    arr.indexOf(2); //1
    arr.join(':'); //"1:2:3:4:6"
    arr.pop(); //6
    //[1,2,3,4]
    arr.push(7); //5
    //[1,2,3,4,7]
    arr.reverse(); //[7,4,3,2,1]
    arr.shift(); //1
    //[2,3,4,7]
    arr.unshift(8); //5
    //[8,2,3,4,7]
    arr.slice(1); //[2,3,4,7]
    arr.slice(1,3); //[2,3]
    arr.slice(-3); //[3,4,7]
    arr.slice(-3,-1); //[3,4]

    View Slide

  27. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ARRAYS
    var arr1 = [1,2,3];
    var arr2 = [3,4,5];
    arr1.concat(arr2); //[1,2,3,3,4,5]

    View Slide

  28. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DELETE
    • Removes key from an object
    • Removes element from array

    View Slide

  29. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DELETE
    • Removes key from an object
    • Removes element from array
    just sets it to undefined!

    View Slide

  30. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DELETE + ARRAY
    var test = [0,1,2,3,4]; //[0, 1, 2, 3, 4]
    delete test[2];
    for (var i in test) { console.debug(i); } //0, 1, 3, 4
    for (var i = 0; i < test.length; i++) { console.debug(test[i]); }
    //0, 1, undefined, 3, 4
    test.length; //5

    View Slide

  31. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FUNCTIONS
    • Objects!
    • Elevated
    • Used a named function before it is defined
    • func definitions are elevated to the top

    View Slide

  32. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FUNCTIONS
    function Foo() {
    //...
    }
    Foo(); //valid
    Bar(); //valid
    function Bar() {
    //...
    }

    View Slide

  33. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FUNCTIONS
    function Foo() {
    //...
    }
    Foo.bar = "value";
    'bar' in Foo; //true
    Foo.bar == "value"; //true

    View Slide

  34. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FUNCTION ARGUMENTS
    • No default arguments
    • Arguments are not required
    • unset arguments become undefined

    View Slide

  35. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ARGUMENTS
    • Special variable found in a function
    • A not-quite array object containing all
    arguments

    View Slide

  36. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ARGUMENTS
    function sum() {
    var x = 0;
    for (var i = 0; i < arguments.length; ++i) {
    x += arguments[i];
    }
    return x;
    }
    sum(1, 2, 3); //6

    View Slide

  37. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    COMPARISON
    • a == b / a != b
    • A and B compared by value alone
    • 1 == “1” evaluates to true
    • a === b / a !== b
    • A and B compared by value and type
    • 1 === “1” evaluates to false

    View Slide

  38. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    WINDOW, DOCUMENT
    • Built in global objects in browser only
    • window
    • Provides access to browser window
    • Contains “global” variables
    • foo === window.foo
    • document
    • Provides access to current DOM

    View Slide

  39. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FUNCTIONS

    View Slide

  40. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FUNCTIONS
    • First-Class
    • Can be assigned to variables, pass as
    arguments, and return as values
    • Anonymous
    • Do not require a name
    • Closures
    • “closes over” variables defined outside
    itself

    View Slide

  41. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CLOSURE
    function Foo() {
    var count = 0;
    return function() {
    count = count + 1;
    return count;
    };
    }
    var bar = Foo();
    bar(); //1
    bar(); //2
    bar(); //3

    View Slide

  42. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CLOSURE
    function Foo() {
    var count = 0;
    return function() {
    count = count + 1;
    return count;
    };
    }
    var bar = Foo();
    bar(); //1
    bar(); //2
    bar(); //3
    var baz = Foo();
    baz(); //??

    View Slide

  43. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CLOSURE
    function Foo() {
    var count = 0;
    return function() {
    count = count + 1;
    return count;
    };
    }
    var bar = Foo();
    bar(); //1
    bar(); //2
    bar(); //3
    var baz = Foo();
    count = 10;
    baz(); //??

    View Slide

  44. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CLOSURE
    function createAdder(amount) {
    return function(input) {
    return input + amount;
    };
    }
    var add2 = createAdder(2);
    add2(2); //4
    add2(8); //10
    var add3 = createAdder(3);
    add3(3); //6
    add3(7); //10

    View Slide

  45. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    SCOPING

    View Slide

  46. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HEADLINE
    • Functions only way to create new scopes
    • Variables defined with var are local
    • Variables defined WITHOUT var are global
    • In Browser: global variables are members of
    the window object

    View Slide

  47. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    LOCAL & GLOBAL
    var outerScope = 10;
    var outerScope2 = 10;
    function Foo() {
    var outerScope = 20;
    var innerScope = 20;
    globalVariable = 30;
    outerScope2 = 40;
    }
    Foo();
    alert(outerScope); //10
    alert(outerScope2); //40
    alert(innerScope); //error
    alert(globalVariable); //30

    View Slide

  48. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    LEXICAL SCOPING
    function Foo() {
    var baz = 1;
    function Bar() {
    return baz;
    }
    return Bar();
    }
    Foo(); //1
    function Foo() {
    var baz = 1;
    return Bar();
    }
    function Bar() {
    return baz;
    }
    Foo(); //baz is not defined

    View Slide

  49. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    SCOPING
    function Outer() {
    myvar = "value1";
    function Inner() {
    return myvar;
    }
    return Inner;
    }
    var inner = Outer();
    inner(); //??
    myvar = "value2";
    inner(); //??

    View Slide

  50. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    SCOPING
    function Outer() {
    myvar = "value1";
    function Inner() {
    return myvar;
    }
    return Inner;
    }
    var inner = Outer();
    inner(); //"value1"
    myvar = "value2";
    inner(); //"value2"

    View Slide

  51. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HOISTING
    foo(); //called foo!
    function foo() {
    console.log('called foo!');
    }
    foo(); //called foo!
    bar(); //undefined is not a function
    var bar = function() {
    console.log('called bar!');
    }
    bar(); //called bar!

    View Slide

  52. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    THIS

    View Slide

  53. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    THIS
    var Foo = {
    bar: "bar",
    baz: function() {
    return this.bar;
    }
    };
    Foo.baz(); //"bar"
    Foo.bar = "bat";
    Foo.baz(); //"bat"
    var baz = Foo.baz;
    baz(); //undefined

    View Slide

  54. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    THIS
    var Foo = {
    bar: "bar",
    baz: function() {
    return this.bar;
    }
    };
    Foo.bat = function() {
    return this.bar + "bat";
    };
    Foo.bat(); //"barbat"

    View Slide

  55. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CALL & APPLY
    var Foo = {
    bar: "bar",
    baz = function(param1, param2) {
    return this.bar + param1 + param2;
    }
    };
    var Foo2 = {
    bar: "123"
    };
    Foo.baz("baz", "bat"); //"barbazbat"
    Foo.baz.apply(Foo2, "baz", "bat"); //"123bazbat"
    Foo.baz.call(Foo2, ["baz", "bat"]); //"123bazbat"

    View Slide

  56. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BINDING!
    function bind(func, context) {
    return function() {
    return func.call(context, arguments);
    }
    }

    View Slide

  57. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    THIS
    function RootLevelFunc() {
    console.debug(this); //??
    }

    View Slide

  58. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    THIS
    function RootLevelFunc() {
    console.debug(this); //undefined
    }

    View Slide

  59. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    THIS


    Hello PHPBenelux!


    <br/>console.debug(this); //??<br/>


    View Slide

  60. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    THIS


    Hello PHPBenelux!


    <br/>console.debug(this); //Window {top: Window, ...}<br/>


    View Slide

  61. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    EXCEPTIONS

    View Slide

  62. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    EXCEPTIONS
    try {
    funcDoesNotExist();
    } catch (e) {
    alert(e); //ReferenceError: funcDoesNotExist is not defined
    } finally {
    //Always Executes
    }

    View Slide

  63. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    EXCEPTIONS
    function Foo() {
    throw new Error("Message");
    }
    function Bar() {
    throw "Message";
    }
    try {
    Foo();
    } catch (e) {
    e.message == "Message"; //true
    }
    try {
    Bar();
    } catch (e) {
    e == "Message"; //true
    }

    View Slide

  64. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PROTOTYPE

    View Slide

  65. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HEADLINE
    function Foo() {
    //The "Constructor"
    }
    Foo.bar = function() {
    //A "Static" Function
    }
    Foo.prototype.baz = function() {
    //A "Method"
    };
    Foo

    ‣ bar

    ‣ prototype

    ‣ baz

    ‣ constructor

    ‣ __proto__

    View Slide

  66. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HEADLINE
    var instance = new Foo();
    instance.baz(); //works
    instance.bar(); //error
    Foo.bar(); //works
    Foo.baz(); //error
    Foo.prototype.baz(); //works
    instance

    ‣ __proto__

    ‣ baz

    ‣ constructor

    ‣ __proto__

    ‣ ...

    View Slide

  67. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HEADLINE
    instance.bat = function() {
    /* ... */
    }
    instance.bat(); //works

    Foo.bat(); //error
    Foo.prototype.bat(); //error
    instance

    ‣ bat

    ‣ __proto__

    ‣ baz

    ‣ constructor

    ‣ __proto__

    ‣ ...

    View Slide

  68. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HEADLINE
    function Foo(baz) {
    this.baz = baz;
    }
    Foo.prototype.bar = function() {
    return this.baz;
    };
    var foo1 = new Foo(1);
    var foo2 = new Foo(2);
    foo1.bar(); //1
    foo2.bar(); //2
    Foo.prototype.bar = function() {
    return this.baz * 2;
    };
    foo1.bar(); //2
    foo2.bar(); //4

    View Slide

  69. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ASYNC

    View Slide

  70. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    INTERVALS & TIMEOUTS
    var id = setInterval(function() {
    //Code to execute every 1000 milliseconds
    }, 1000);
    //clearInterval(id); to stop
    var id = setTimeout(function() {
    //Code to execute after 1000 milliseconds have passed
    }, 1000);
    //clearTimeout(id); to cancel

    View Slide

  71. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    INTERVAL RACE CONDITIONS
    setInterval()
    setTimeout()

    View Slide

  72. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    INTERVAL WORKAROUND
    var running = true;
    , interval = function() {
    if (!running) return;
    //do something
    if (running) setTimeout(interval, 1000);
    };
    interval();

    View Slide

  73. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DOM

    View Slide

  74. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DOM
    • Document Object Model
    • API to interact with browser’s
    representation of the HTML document

    View Slide

  75. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    SELECTION
    document.getElementById('foo');
    document.getElementsByClassName('.bar');
    document.getElementsByTagName('script');
    document.querySelectorAll('#foo .bar > script');

    View Slide

  76. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    View Slide

  77. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DOM: CREATION
    var paragraph = document.createElement('p');
    var content = document.createTextNode("Lorem Ipsum");
    paragraph.appendChild(content);
    paragraph.classList.add('my-class');
    document.body.appendChild(paragraph);

    View Slide

  78. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    View Slide

  79. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DOM: LOADING SCRIPT
    var script = document.createElement('script');
    script.src = "http://path.to/script.js";
    script.async = true;
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(script, s);

    View Slide

  80. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DOM: ADDING IMAGE
    var img = document.createElement('img');
    img.src = "http://path.to/some/image.jpeg";
    img.onload = function() { /** do something on load */ };
    document.body.appendChild(img);

    View Slide

  81. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BROWSER
    TOOLS

    View Slide

  82. SUBTITLE
    CHROME/SAFARI DEVEL. TOOLS

    View Slide

  83. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CONSOLE

    View Slide

  84. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BEWARE!
    • console.log is NOT BLOCKING
    • Sometimes value displayed is newer than
    value when log was called

    View Slide

  85. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    NETWORK TAB & XHR
    • displays all network requests
    • Chrome is better for network debugging
    • Shows full request & response headers and
    data
    • Can replay XHR requests!

    View Slide

  86. BREAK
    HAMMER TIME

    View Slide

  87. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    MODERN
    ARCH

    View Slide

  88. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HTML & DOM
    DOM: Object-Graph Representing Parsed HTML
    PRESENTATION LAYER
    HTML: The source

    View Slide

  89. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PRESENTATION LAYER

    View Slide

  90. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PRESENT
    ATION
    LAYER

    View Slide

  91. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CLASSIC JAVA GUI PROGRAMMING

    View Slide

  92. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CLASSIC JAVASCRIPT PROGRAMMING

    View Slide

  93. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CLASSIC JAVASCRIPT PROGRAMMING
    Just modify DOM
    What is the TextBox’s State Again?
    Start a jQuery animation

    View Slide

  94. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    MODERN JS
    State
    DOM
    Events
    Controllers
    Modify
    Events
    Modify
    Javascript
    Read

    View Slide

  95. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    WHERE IS YOUR DOM NOW?
    Everything is PUSHED into the DOM
    PRESENTATION LAYER

    View Slide

  96. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    WHERE IS YOUR DOM NOW?
    Data FROM the DOM comes in via Events ONLY
    PRESENTATION LAYER

    View Slide

  97. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CONTROLLERS
    Update the DOM based on state
    Update State based on User Interaction

    View Slide

  98. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    WIDGETS
    Pieces of functionality are self-contained
    Their State is self-contained

    View Slide

  99. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    View Slide

  100. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    underscorejs.org

    View Slide

  101. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    Functional, self-contained micro-library

    View Slide

  102. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FUNCTIONAL PROGRAMMING???
    Functions are mappings between input and output
    • They have no side-effects
    • Input A always results in output b

    View Slide

  103. +Functional Programming???
    Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
    function
    input output
    global state
    global state
    global state
    X X
    X

    View Slide

  104. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    UNDERSCORE.JS
    Collections
    •each
    •map
    •reduce
    •reduceRight
    •find
    •filter
    •reject
    •all
    •any
    •include
    •invoke
    •pluck
    •max
    •min
    •sortBy
    •groupBy
    •sortedIndex
    •shuffle
    •toArray
    •size
    Arrays
    •first
    •initial
    •last
    •rest
    •compact
    •flatten
    •without
    •union
    •intersection
    •difference
    •uniq
    •zip
    •indexOf
    •lastIndexOf
    •range
    Functions
    •bind
    •bindAll
    •memoize
    •delay
    •defer
    •throttle
    •debounce
    •once
    •after
    •wrap
    •compose
    Objects
    •keys
    •values
    •functions
    •extend
    •pick
    •defaults
    •clone
    •tap
    •has
    •isEqual
    •isEmpty
    •isElement
    •isArray
    •isObject
    •isArguments
    •isFunction
    •isString
    •isNumber
    •isFinite
    •isBoolean
    •isDate
    •isRegExp
    •isNaN
    •isNull
    •isUndefined
    Utility
    •noConflict
    •identity
    •times
    •mixin
    •uniqueId
    •escape
    •result
    •template
    Chaining
    •chain
    •value

    View Slide

  105. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    MAP
    Maps function to each element
    in the input collection
    var inp = [1, 2, 3]
    , out = _.map(inp, function(n)
    {
    return n*2;
    });
    //out = [2, 4, 6]

    View Slide

  106. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    REDUCE
    Reduces collection to a single
    value. mem is the initial state,
    each successive iteration must
    be returned
    var inp = [1, 2, 3];
    _(inp).reduce(function(mem, n){
    return mem + n;
    });
    //Iter 0: mem = 1 | n = 2
    //Iter 1: mem = 3 | n = 3
    //Returns: 6

    View Slide

  107. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PLUCK
    Iterates over a collection and
    extracts the values for the
    input key (assumes all elements
    in the collection are objects/
    arrays)
    var stooges = [
    {name: 'moe', age: 40}
    , {name: 'larry', age: 50}
    , {name: 'curly', age: 60}
    ];
    _.pluck(stooges, 'name');
    //Returns ["moe", "larry",
    "curly"]

    View Slide

  108. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    MIN/MAX
    Returns the max item in a
    collection.
    If second argument (iterator)
    provided, will use to produce
    the value to be compared
    var stooges = [
    {name: 'moe', age: 40}
    , {name: 'larry', age: 50}
    , {name: 'curly', age: 60}
    ];
    _.max(stooges, function(s){
    return s.age;
    });
    //Returns {name: 'curly', age:
    60}

    View Slide

  109. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    KEYS
    Returns the keys from a
    dictionary as an array
    _.keys({
    one: 1,
    two: 2,
    three: 3
    });
    //Returns ["one", "two",
    "three"]

    View Slide

  110. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    DEFAULTS
    Maps a duplicate input
    dictionary on top of a
    predefined “default” dictionary
    var iceCream = {
    flavor: "chocolate"
    };
    _.defaults(iceCream, {
    flavor: "vanilla"
    , sprinkles: "lots"
    });
    //Returns {flavor : "chocolate",
    sprinkles : "lots"}

    View Slide

  111. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    CHAINING
    When chain is initiated, method
    return a self-reference back to
    underscore but with the value
    attached [similar to opening
    with _(val)].
    The chain continues until the
    value is extracted using .value()
    var stooges = [
    {name: 'curly', age : 25},
    {name: 'moe', age : 21},
    {name: 'larry', age : 23}
    ];
    var youngest = _.chain(stooges)
    .sortBy(function(s){
    return s.age;
    })
    .map(function(s){
    return s.name + ' is ' +
    s.age;
    })
    .first()
    .value();
    //Returns "moe is 21"

    View Slide

  112. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    backbonejs.org

    View Slide

  113. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    micro-application framework

    View Slide

  114. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BACKBONE.JS
    • builds on jquery/Zepto and underscore.js
    • provides
    • views
    • models
    • collections
    • router

    View Slide

  115. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    MODEL
    var OurModel = Backbone.Model.extend({
    defaults: {
    foo: 'value'
    , bar: 1
    }
    });
    var instance = new OurModel();
    instance.on('change:foo', function(modelobj, foo) {
    console.log('foo is now ' + foo);
    });
    instance.set('foo', 'bat');
    //foo is now bat

    View Slide

  116. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    COLLECTION
    var OurModelCollection = Backbone.Collection.extend({
    model: OurModel
    });
    var inst1 = new OurModel()
    , inst2 = new OurModel()
    , coll = new OurModelCollection();
    coll.on('add', function(modelobj) {
    console.log('Added model ' + modelobj.cid);
    });
    coll.on('change:bar', function(modelobj, bar) {
    console.log('Bar for ' + modelobj.cid + ' is now ' + bar);
    });
    coll.add(inst1); coll.add(inst2);
    inst1.set('bar', 'baz');
    //Added model c4
    //Added model c5
    //Bar for c4 is now baz

    View Slide

  117. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    VIEW
    var OurView = Backbone.View.extend({
    className: 'our-view'
    , initialize: function() {
    this.listenTo(this.model, ’change:foo’, this.updateFoo,
    this);
    }
    , render: function() {
    this.updateFoo();
    return this;
    }
    , updateFoo: function() {
    this.$el.html(this.model.get('foo'));
    }
    });
    var modelInst = new OurModel({foo: 'bar'});
    , viewInst = new OurView({model: modelInst});
    $('#our-container').append(viewInst.render().el);
    modelInst.set('foo', 'baz');
    //bar
    //baz

    View Slide

  118. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ROUTER
    namespace.MessageCollectionRouter = Backbone.Router.extend({
    routes: {
    "page/:page": "viewPage"
    }
    , initialize: function(options){
    this.collection = options.collection;
    }
    , viewPage: function(page) {
    this.collection.setPage(page);
    }
    , setUrlToPage: function(page) {
    this.navigate('page/' + page, {trigger:false});
    }
    });
    Backbone.history.start({pushState: true, root: '/'});

    View Slide

  119. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    REFACTOR

    View Slide

  120. Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
    Because you’re not lucky
    enough right now to start a
    project from scratch.

    View Slide

  121. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FIRST STEPS




    View Slide

  122. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    LOOKING CONCEPTUALLY
    scaffolding
    DOM Management
    Event Management
    State Management
    Data Synchronization

    View Slide

  123. Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
    var defaults = {/* ... */};
    var methods = {
    init: function(options) {
    if (!$(this).is('textarea')) {
    $.error('Poster must be applied to a textarea');
    return undefined;
    }
    options = _.defaults(options, defaults);
    var poster = new Poster(this, options);
    $(this).data('_poster', poster);
    return this;
    }
    };
    $.fn.splash_poster = function(method) {
    //jQuery Plugin Boilerplate
    };
    function Poster($el, options) {
    this.$el = $el;
    this.init();
    }
    Poster.prototype.init = function() {
    var that = this;
    this.$el.on('change', jQuery.proxy(this.changeHandler, this));
    this.$el.on('keyup', jQuery.proxy(this.changeHandler, this));
    };
    Poster.prototype.changeHandler = function(e) {
    //Check character counts, identify urls
    };

    View Slide

  124. var defaults = {/* ... */};
    var methods = {
    init: function(options) {
    if (!$(this).is('textarea')) {
    $.error('Poster must be applied to a textarea');
    return undefined;
    }
    options = _.defaults(options, defaults);
    var poster = new Poster(this, options);
    $(this).data('_poster', poster);
    return this;
    }
    };
    $.fn.splash_poster = function(method) {
    //jQuery Plugin Boilerplate
    };
    function Poster($el, options) {
    this.$el = $el;
    this.init();
    }
    Poster.prototype.init = function() {
    var that = this;
    this.$el.on('change', jQuery.proxy(this.changeHandler, this));
    this.$el.on('keyup', jQuery.proxy(this.changeHandler, this));
    };
    Poster.prototype.changeHandler = function(e) {
    //Check character counts, identify urls
    };
    scaffolding

    View Slide

  125. var defaults = {/* ... */};
    var methods = {
    init: function(options) {
    if (!$(this).is('textarea')) {
    $.error('Poster must be applied to a textarea');
    return undefined;
    }
    options = _.defaults(options, defaults);
    var poster = new Poster(this, options);
    $(this).data('_poster', poster);
    return this;
    }
    };
    $.fn.splash_poster = function(method) {
    //jQuery Plugin Boilerplate
    };
    function Poster($el, options) {
    this.$el = $el;
    this.init();
    }
    Poster.prototype.init = function() {
    var that = this;
    this.$el.on('change', jQuery.proxy(this.changeHandler, this));
    this.$el.on('keyup', jQuery.proxy(this.changeHandler, this));
    };
    Poster.prototype.changeHandler = function(e) {
    //Check character counts, identify urls
    };
    DOM Management

    View Slide

  126. var defaults = {/* ... */};
    var methods = {
    init: function(options) {
    if (!$(this).is('textarea')) {
    $.error('Poster must be applied to a textarea');
    return undefined;
    }
    options = _.defaults(options, defaults);
    var poster = new Poster(this, options);
    $(this).data('_poster', poster);
    return this;
    }
    };
    $.fn.splash_poster = function(method) {
    //jQuery Plugin Boilerplate
    };
    function Poster($el, options) {
    this.$el = $el;
    this.init();
    }
    Poster.prototype.init = function() {
    var that = this;
    this.$el.on('change', jQuery.proxy(this.changeHandler, this));
    this.$el.on('keyup', jQuery.proxy(this.changeHandler, this));
    };
    Poster.prototype.changeHandler = function(e) {
    //Check character counts, identify urls
    };
    Event Management

    View Slide

  127. var defaults = {/* ... */};
    var methods = {
    init: function(options) {
    if (!$(this).is('textarea')) {
    $.error('Poster must be applied to a textarea');
    return undefined;
    }
    options = _.defaults(options, defaults);
    var poster = new Poster(this, options);
    $(this).data('_poster', poster);
    return this;
    }
    };
    $.fn.splash_poster = function(method) {
    //jQuery Plugin Boilerplate
    };
    function Poster($el, options) {
    this.$el = $el;
    this.init();
    }
    Poster.prototype.init = function() {
    var that = this;
    this.$el.on('change', jQuery.proxy(this.changeHandler, this));
    this.$el.on('keyup', jQuery.proxy(this.changeHandler, this));
    };
    Poster.prototype.changeHandler = function(e) {
    //Check character counts, identify urls
    };
    State Management

    View Slide

  128. var Poster = Backbone.View.extend({
    tagName: 'form'
    , events: {
    "change textarea.message": "eChangeBody"
    , "keyup textarea.message": "eChangeBody"
    }
    , initialize: function() {
    this.render();
    this.setModel(this.model);
    }
    , setModel: function(model) {
    this.model = model;
    this.listenTo(this.model, 'change:body', this.handleUpdateBody, this);
    this.handleUpdateBody(this.model, this.model.get('body'));
    }
    , handleUpdateBody: function(model, body) {
    this.$body.val(body).change();
    }
    , eChangeBody: function(e) {
    //Check character counts, identify urls
    }
    , render: function() {
    this.$el.append(this._renderBody());
    this.$body = this.$('textarea.message');
    return this;
    }
    });

    View Slide

  129. var Poster = Backbone.View.extend({
    tagName: 'form'
    , events: {
    "change textarea.message": "eChangeBody"
    , "keyup textarea.message": "eChangeBody"
    }
    , initialize: function() {
    this.render();
    this.setModel(this.model);
    }
    , setModel: function(model) {
    this.model = model;
    this.listenTo(this.model, 'change:body', this.handleUpdateBody, this);
    this.handleUpdateBody(this.model, this.model.get('body'));
    }
    , handleUpdateBody: function(model, body) {
    this.$body.val(body).change();
    }
    , eChangeBody: function(e) {
    //Check character counts, identify urls
    }
    , render: function() {
    this.$el.append(this._renderBody());
    this.$body = this.$('textarea.message');
    return this;
    }
    });
    Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
    scaffolding

    View Slide

  130. var Poster = Backbone.View.extend({
    tagName: 'form'
    , events: {
    "change textarea.message": "eChangeBody"
    , "keyup textarea.message": "eChangeBody"
    }
    , initialize: function() {
    this.render();
    this.setModel(this.model);
    }
    , setModel: function(model) {
    this.model = model;
    this.listenTo(this.model, 'change:body', this.handleUpdateBody, this);
    this.handleUpdateBody(this.model, this.model.get('body'));
    }
    , handleUpdateBody: function(model, body) {
    this.$body.val(body).change();
    }
    , eChangeBody: function(e) {
    //Check character counts, identify urls
    }
    , render: function() {
    this.$el.append(this._renderBody());
    this.$body = this.$('textarea.message');
    return this;
    }
    });
    dom management

    View Slide

  131. var Poster = Backbone.View.extend({
    tagName: 'form'
    , events: {
    "change textarea.message": "eChangeBody"
    , "keyup textarea.message": "eChangeBody"
    }
    , initialize: function() {
    this.render();
    this.setModel(this.model);
    }
    , setModel: function(model) {
    this.model = model;
    this.listenTo(this.model, 'change:body', this.handleUpdateBody, this);
    this.handleUpdateBody(this.model, this.model.get('body'));
    }
    , handleUpdateBody: function(model, body) {
    this.$body.val(body).change();
    }
    , eChangeBody: function(e) {
    //Check character counts, identify urls
    }
    , render: function() {
    this.$el.append(this._renderBody());
    this.$body = this.$('textarea.message');
    return this;
    }
    });
    ]
    event management

    View Slide

  132. var Poster = Backbone.View.extend({
    tagName: 'form'
    , events: {
    "change textarea.message": "eChangeBody"
    , "keyup textarea.message": "eChangeBody"
    }
    , initialize: function() {
    this.render();
    this.setModel(this.model);
    }
    , setModel: function(model) {
    this.model = model;
    this.listenTo(this.model, 'change:body', this.handleUpdateBody, this);
    this.handleUpdateBody(this.model, this.model.get('body'));
    }
    , handleUpdateBody: function(model, body) {
    this.$body.val(body).change();
    }
    , eChangeBody: function(e) {
    //Check character counts, identify urls
    }
    , render: function() {
    this.$el.append(this._renderBody());
    this.$body = this.$('textarea.message');
    return this;
    }
    });
    state management

    View Slide

  133. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM






    Item 1 Delete






    <br/>$('#widget .item a').click(function(e){<br/>var $this = $(this)<br/>, $parent = $this.parent()<br/>, id = $parent.data('id');<br/>$.ajax(/* … */);<br/>});<br/>//...<br/>

    View Slide

  134. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM






    Item 1 Delete







    <br/>$(function(){<br/>var view = new Foo.Bar({el: $('#widget')}).render();<br/>});<br/>

    View Slide

  135. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    (function(namespace, $){
    namespace.Bar = Backbone.View.extend({
    events: {
    'click .item a': 'clickItemDelete'
    }
    , initialize: function() {
    }
    , render: function() {
    //...
    return this;
    }
    , clickItemDelete: function(e) {
    var $this = $(e.currentTarget)
    , $parent = $this.parent()
    , id = $parent.data('id');
    $.ajax(/* … */);
    }
    });
    })(window.Foo = window.Foo || {}, jQuery);

    View Slide

  136. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    MODULES

    View Slide

  137. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    REQUIRE.JS
    define('MyModule', ['dependency', 'jquery'], function(Dep, $) {
    //Do Stuff
    return MyExports;
    });
    require(['MyModule'], function(MyExports) {
    //Do Stuff With MyExports
    });

    View Slide

  138. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BROWSERIFY
    var Dep = require('dependency')
    , $ = require('jquery');
    //Do Stuff
    module.exports = MyExports;

    View Slide

  139. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    BUILD

    View Slide

  140. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    GULP
    var gulp = require('gulp'),
    uglify = require('gulp-uglify'),
    browserify = require('browserify'),
    hbsfy = require('hbsfy'),
    rename = require('gulp-rename'),
    jshint = require('gulp-jshint'),
    less = require('gulp-less'),
    sourcemaps = require('gulp-sourcemaps'),
    source = require('vinyl-source-stream'),
    buffer = require('vinyl-buffer');
    gulp.task('default', ['watch'], function(){});
    var rootPath = __dirname + '/static';

    View Slide

  141. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    GULP
    gulp.task('watch', ['browserify', 'less-lays'], function() {
    gulp.watch(rootPath + '/js/**/*.js', ['browserify']);
    gulp.watch(rootPath + '/js/**/*.hbs', ['browserify']);
    gulp.watch(rootPath + '/less/**/*.less', ['less']);
    });

    View Slide

  142. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    GULP
    gulp.task('browserify', function () {
    var b = browserify({
    paths: [rootPath + '/js'],
    debug: true
    });
    b.add(rootPath + '/js/__init__.js');
    return b
    .transform(hbsfy)
    .bundle()
    .pipe(source('output.js'))
    .pipe(buffer())
    .pipe(gulp.dest(rootPath + '/dist'))
    .pipe(uglify())
    .pipe(rename({extname: '.min.js'}))
    .pipe(gulp.dest(rootPath + '/dist'));
    });

    View Slide

  143. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    GULP
    gulp.task('less', function(){
    gulp.src(rootPath + '/less/core.less')
    .pipe(sourcemaps.init())
    .pipe(less())
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest(rootPath + '/css'));
    });

    View Slide

  144. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERF

    View Slide

  145. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    Prefer singular built file
    Single HTTP request
    Single Cache Entry

    View Slide

  146. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    Minify/Uglify
    Ensure comments dropped
    Pre-compile templates & Include in build
    gzip!

    View Slide

  147. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    Preload as much data as possible
    Reduce round trips on load

    View Slide

  148. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PRELOAD
    <br/>Blueprint = window.Blueprint || {};<br/>Blueprint._config = ...;<br/>Blueprint._current_user = ...;<br/><?php if (isset($preload)): ?><br/>Blueprint._preload = <?php echo json_encode($preload); ?>;<br/><?php endif; ?><br/>

    View Slide

  149. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PRELOAD
    /**
    * @param {Backbone.Model} model Model object to fill
    * @param {Function} identifier accepts model, raw preload: returns relevant data
    * @param {Function} success Triggers on successful preload or successful fetch
    * @param {Function} error Triggers on failed fetch
    */
    preloadOrFetchModel: function(model, identifier, success, error) {
    if (Util.hasLocalPreloadData()) {
    var preload = Util.getLocalPreloadData()
    , preloadData = identifier(model, preload);
    if (preloadData) {
    model.set(preloadData, {silent: true});
    success();
    return;
    }
    }
    model.fetch({
    success: success
    , error: error
    });
    }

    View Slide

  150. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    Prefer DOM updates to overwrites
    Make DOM changes in batches, small as possible
    Beware: Will this Repaint or Reflow?
    Prefer adding and removing classes

    View Slide

  151. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    taking measurements forces reflow!
    Cache Measurements
    Measure only on browser events

    View Slide

  152. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    var $ = require('jquery')
    , Backbone = require('backbone')
    , $window = $(window);
    var dim = _.extend({
    window: { width: 0, height: 0 }
    , viewport: { top: 0, bottom: 0 }
    }, Backbone.Events);
    var resizeCallback = _.throttle(function() {
    dim.window.width = document.documentElement.clientWidth;
    dim.window.height = document.documentElement.clientHeight;
    dim.trigger('resize', dim);
    }, 1);
    var scrollCallback = _.throttle(function() {
    dim.viewport.top = $window.scrollTop();
    dim.viewport.bottom = dim.viewport.top + dim.window.height;
    dim.trigger('scroll', dim)
    }, 1);
    // Singular browser event listener for resize and scroll events
    $window.on('resize.myapp-dim', resizeCallback);
    $window.on('scroll.myapp-dim', scrollCallback);
    module.exports = dim;

    View Slide

  153. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    Animate in CSS
    Animate in CSS
    If not, animate using absolute or fixed positioning

    View Slide

  154. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PERFORMANCE
    var start = null;
    var element =
    document.getElementById("SomeElementYouWantToAnimate");
    function step(timestamp) {
    if (!start) start = timestamp;
    var progress = timestamp - start;
    element.style.left = Math.min(progress/10, 200) + "px";
    if (progress < 2000) {
    window.requestAnimationFrame(step);
    }
    }
    window.requestAnimationFrame(step);

    View Slide

  155. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    WHAT NEXT:
    ES6

    View Slide

  156. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PROMISES
    • fulfilled - Succeed
    • rejected - Failed
    • pending
    • settled - Completed

    View Slide

  157. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PROMISES
    var promise = new Promise(function(resolve, reject) {
    // do a thing, possibly async, then…
    if (/* everything turned out fine */) {
    resolve("Stuff worked!");
    }
    else {
    reject(Error("It broke"));
    }
    });

    View Slide

  158. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PROMISES
    promise.then(function(result) {
    console.log(result); // "Stuff worked!"
    }, function(err) {
    console.log(err); // Error: "It broke"
    });

    View Slide

  159. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PRACTICAL
    var loaders = [];
    images.forEach(function(image, index){
    //...
    var loader = $.Deferred();
    loaders.push(loader);
    img.onload = function() {
    loader.resolve();
    };
    img.src = image.src;
    if (img.complete) {
    img.onload = null;
    loader.resolve();
    }
    //...
    });
    $.when.apply(null, loaders).done(hideLoader);

    View Slide

  160. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    PRACTICAL
    var loaders = [];
    images.forEach(function(image, index){
    //...
    loaders.push(new Promise(function(resolve, reject){
    img.onload = function() {
    resolve();
    };
    img.src = image.src;
    if (img.complete) {
    img.onload = null;
    resolve();
    }
    }));
    //...
    });
    Promise.all(loaders).then(hideLoader);

    View Slide

  161. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    YIELD
    function *foo() {
    yield 1;
    yield 2;
    }
    var it = foo();
    console.log( it.next() ); // { value:1, done:false }
    console.log( it.next() ); // { value:2, done:false }
    console.log( it.next() ); // { value:undefined, done: true }

    View Slide

  162. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    HEADLINE
    function *foo() {
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    yield 5;
    yield 6;
    }
    for (var v of foo()) {
    console.log(v);
    }
    //1 2 3 4 5 6

    View Slide

  163. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    LET/CONST
    const x = "value";
    x = "another value"; //Error
    x; //"value"
    {
    let x;
    x = 5;
    x = 7;
    x; //7
    }
    x; //"value"

    View Slide

  164. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    FAT ARROW
    var adder = function(x) { return x + 1; }
    adder(1); //2
    adder(2); //3
    var fatAdder = (x) => x + 1;
    fatAdder(1); //2
    fatAdder(2);

    View Slide

  165. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    import User from 'user';
    var evilTrout = new User(35);
    MODULES
    var localVariable = 123;
    export default function User(age) {
    this.age = age;
    };

    View Slide

  166. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    import {sum, pi} from 'math';
    console.log(sum(pi, pi));
    MODULES
    export var pi = 3.141593;
    export function sum(x, y) {
    return x + y;
    };

    View Slide

  167. THANKS.
    FOR YOUR ATTENTION
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    View Slide

  168. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    https://joind.in/13105

    View Slide

  169. +
    DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
    ADDITIONAL RESOURCES
    • http://todomvc.com/ – Todo app written in
    popular frameworks
    • http://marionettejs.com/ – Extensions for
    Backbone.js

    View Slide