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. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

    OPERATIONS • Arithmetic • + - / * • Bitwise • & | ^ • Boolean • && || !
  3. + 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";
  4. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    TYPEOF • Returns variable type as a string
  5. + 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; //??
  6. + 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"
  7. + 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
  8. + 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]'; };
  9. + 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"
  10. + 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
  11. + 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
  12. + 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
  13. + 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
  14. + 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
  15. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    ARRAYS • Special Object • Numeric ‘keys’ only • keeps intrinsic ordering
  16. + 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"]
  17. + 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]
  18. + 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]
  19. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

    DELETE • Removes key from an object • Removes element from array just sets it to undefined!
  21. + 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
  22. + 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
  23. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

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

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

    ARGUMENTS • Special variable found in a function • A not-quite array object containing all arguments
  27. + 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
  28. + 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
  29. + 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
  30. + 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
  31. + 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
  32. + 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(); //??
  33. + 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(); //??
  34. + 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
  35. + 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
  36. + 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
  37. + 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
  38. + 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(); //??
  39. + 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"
  40. + 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!
  41. + 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
  42. + 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"
  43. + 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"
  44. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

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

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

    THIS <html> <head> <title>Hello PHPBenelux!</title> </head> <body> <script> console.debug(this); //?? </script> </body> </html>
  48. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    THIS <html> <head> <title>Hello PHPBenelux!</title> </head> <body> <script> console.debug(this); //Window {top: Window, ...} </script> </body> </html>
  49. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    EXCEPTIONS try { funcDoesNotExist(); } catch (e) { alert(e); //ReferenceError: funcDoesNotExist is not defined } finally { //Always Executes }
  50. + 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 }
  51. + 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__
  52. + 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__ ‣ ...
  53. + 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__ ‣ ...
  54. + 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
  55. + 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
  56. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    INTERVAL RACE CONDITIONS setInterval() setTimeout()
  57. + 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();
  58. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

    SELECTION document.getElementById('foo'); document.getElementsByClassName('.bar'); document.getElementsByTagName('script'); document.querySelectorAll('#foo .bar > script');
  60. + 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);
  61. + 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);
  62. + 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);
  63. + 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
  64. + 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!
  65. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    HTML & DOM DOM: Object-Graph Representing Parsed HTML PRESENTATION LAYER HTML: The source
  66. + 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
  67. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

    WHERE IS YOUR DOM NOW? Everything is PUSHED into the DOM PRESENTATION LAYER
  69. + 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
  70. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

    WIDGETS Pieces of functionality are self-contained Their State is self-contained
  72. + 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
  73. +Functional Programming??? Daniel Cousineau // follow me : @dcousineau or

    http://dcousineau.com function input output global state global state global state X X X
  74. + 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
  75. + 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]
  76. + 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
  77. + 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"]
  78. + 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}
  79. + 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"]
  80. + 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"}
  81. + 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"
  82. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    BACKBONE.JS • builds on jquery/Zepto and underscore.js • provides • views • models • collections • router
  83. + 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
  84. + 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
  85. + 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'); //<div class="our-view">bar</div> //<div class="our-view">baz</div>
  86. + 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: '/'});
  87. Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Because

    you’re not lucky enough right now to start a project from scratch.
  88. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    FIRST STEPS <script src="./scripts/jquery-1.9.1.js"></script> <script src="./scripts/underscore.js"></script> <script src="./scripts/backbone.js"></script> <script src="./scripts/jquery-1.9.1.js"></script>
  89. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    LOOKING CONCEPTUALLY scaffolding DOM Management Event Management State Management Data Synchronization
  90. 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 };
  91. 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
  92. 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
  93. 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
  94. 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
  95. 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; } });
  96. 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
  97. 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
  98. 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
  99. 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
  100. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM <html>

    <!-- ... --> <div id="widget"> <ul> <li> <span class="item" data-id="1"> Item 1 <a href="/url/del">Delete</a> </span> </li> <!-- ... --> </ul> </div> <!-- ... --> <script> $('#widget .item a').click(function(e){ var $this = $(this) , $parent = $this.parent() , id = $parent.data('id'); $.ajax(/* … */); }); //... </script> </body> </html>
  101. DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM <html>

    <!-- ... --> <div id="widget"> <ul> <li> <span class="item" data-id="1"> Item 1 <a href="/url/del">Delete</a> </span> </li> <!-- ... --> </ul> </div> <!-- ... --> <script src="./post.js"></script> <script> $(function(){ var view = new Foo.Bar({el: $('#widget')}).render(); }); </script> </body> </html>
  102. 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);
  103. + 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 });
  104. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    BROWSERIFY var Dep = require('dependency') , $ = require('jquery'); //Do Stuff module.exports = MyExports;
  105. + 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';
  106. + 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']); });
  107. + 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')); });
  108. + 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')); });
  109. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

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

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

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

    PRELOAD <script> Blueprint = window.Blueprint || {}; Blueprint._config = ...; Blueprint._current_user = ...; <?php if (isset($preload)): ?> Blueprint._preload = <?php echo json_encode($preload); ?>; <?php endif; ?> </script>
  113. + 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 }); }
  114. + 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
  115. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    PERFORMANCE taking measurements forces reflow! Cache Measurements Measure only on browser events
  116. + 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;
  117. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    PERFORMANCE Animate in CSS Animate in CSS If not, animate using absolute or fixed positioning
  118. + 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);
  119. + DANIEL COUSINEAU // FOLLOW ME : @DCOUSINEAU OR HTTP://DCOUSINEAU.COM

    PROMISES • fulfilled - Succeed • rejected - Failed • pending • settled - Completed
  120. + 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")); } });
  121. + 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" });
  122. + 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);
  123. + 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);
  124. + 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 }
  125. + 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
  126. + 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"
  127. + 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);
  128. + 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; };
  129. + 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; };
  130. THANKS. FOR YOUR ATTENTION DANIEL COUSINEAU // FOLLOW ME :

    @DCOUSINEAU OR HTTP://DCOUSINEAU.COM
  131. + 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