Serious JavaScript



Underpinnings of the great web experience

About me


Raymond Julin

Lead Product Developer at Keyteq Labs

& eZ Exceed lead ++

<3 JS


@nervetattoo (Twitter,Github)


JavaScript 1-2-3


  • Prototypial language
  • First class functions
    • Context
  • Closures
  • Where are my privates!?
  • Functional programming

Prototypial inheritance


  • Every value has a prototype
  • Prototype properties are accessible on the concrete value
    • "foo" => String.prototype
  • OOP can be implemented using prototypes
  • "foo".match(/f/); "Translates" to:
    • String.prototype.match.call('foo', /f/)
var myProto = {foo:function(){return'bar';}};var Class = function() {};Class.prototype = myProto;
var obj = new Class();
obj.foo();
obj.bar(); Checks obj for bar first, then prototypes, so adding myProto.bar at this point will make obj.bar() work.
Metaprogramming!
Easily misused; Don't modify or overwrite prototypes of builtins

First class functions

  • A function is just a value, no different from a string
  • var foo = function() {};
  • bar = foo; // Passes a reference to same function
  • foo = null;
  • bar() still works

Function context

  • this is not idempotent as in PHP
  • Context (this) is defined at call-time:
var obj = {  greeting: 'Hello world',  hello: function() {    console.log(this.greeting);  }};// Lets break thingsobj.hello(); // So far so goodvar em = new require('events').EventEmitter();em.on('hello', obj.hello);em.emit('hello'); // Oh hello errorobj.hello.call({greeting:'Oh no'})var boundHello = obj.hello.bind(obj); // Always Hello world

Functions are closures

var foo = 'bar';var f = function() { return foo; }foo = 'foo';f(); // bar

Where are my privates?

obj.greeting = 'mwhahaha';obj.hello();
No builtin way of defining greeting as private or protected.
Use the power of closures!
var obj = (function() {  var greeting = 'Hello world';  return {    hello: function() {      console.log(greeting);    }  };}());

Functional programming

  • Functions are first class citizens
  • == pleasant functional programming in JavaScript

forEach over for

Processing an array, multiplication
var arr = [0,1,2];var result = [];for (var i = 0, l = arr.length; i < l; i++) {  result.push(arr[i] * 2);}
vs.
arr.forEach(function(item) {   result.push(item * 2);});

Creating arrays from arrays

  • Using forEach was undoubtedly cleaner
  • But this is so important it has its own method
var result = arr.map(function(num) { return num * 2 });
Filtering out some items
var arr = [1,2,3,'a'];var justNumbers = arr.filter(function(item) {  return typeof item === 'number';});
Chaining it
var result = arr.filter(function(item) {  return typeof item === 'number';}).map(function(item) {  return item * 2;});

Promotes reuse

var isNumber = function(num) {  return typeof num === 'number';};var multiplyByTwo = function(num) {  return num * 2;};var result = arr.filter(isNumber).map(multiplyByTwo);

Higher order functions

Functions that create functions
var createMultiplicator = function(factor) {  return function(num) {    return num * factor;  }};
Lets fix our silly multiplyByTwo
var result = arr.filter(isNumber).map(createMultiplicator(2));

Reduce

Reducing arrays to simple values
var add = function(a,b) { return a + b; };[1,2].reduce(add);arr.filter(isNumber).reduce(add);

Concats strings as well
["Hello"," world"].reduce(add);arr.reduce(add);

Live coding

Calculation example

Javascript


  • The experience happens in the browser
  • JS, CSS and the browser itself shapes this

Then

JavaScript used to be horrendous:

 <input type="button" onclick="javascript:doFoo();">

And completely ruled by chaos and hidden dependencies:
 <script src="jquery.js"></script> <script src="jquery.plugin.js"></script>
<script src="scripts.js"></script>
There was very little regard for decent programming like you all do in modern PHP apps (or any proper server side language/framework)

Future

You will soon (ES6) be writing this:

import $ from "jquery";import {View, Model, Controller} from "framework-x";class Widget extends View {  render() {    // rendering  }}
HTML5 brings great APIs and further browser standardisations for JS/CSS.
Media, offline, web workers, RTC, pushstate, animations, transformations.

Today

We have the tools to write proper JavaScript applications without tearing our hair out.
  • DOM: jQuery, zepto
  • MV*: Backbone,Angular,Ember++
  • Modules: AMD, CommonJS
  • Templating: Mustache,Handlebars
  • Utilities: Underscore (Lodash)
  • Packages: Bower, Jam, Component
  • CSS libs: Bootstrap, Foundation, Pure
  • Testing: Mocha, Jasmine, PhantomJS, Casper

Hello world

document.getElementById('#message').innerHTML = '<strong>Hello world</strong>';

Nope, not that easy.
You wouldn't do this:
 <?php echo '<strong>Hello world</strong>';
Would you? 

AMD

Asynchronous Module Definition
define(['dependency1', 'dependency2'], function(Dep1, Dep2) {  return myModule;});
require(['mymodule'], function(App) { App.run();});
Verbs: define and require

A module / Why modules


  • Unnamed or named
  • URI path, or path mapping, used to load unnamed modules
  • Callback triggered once dependencies are loaded
  • Loader takes care of resolving dependencies
  • Optimizable (combine and uglify)
  • Doesn't break the save/reload cycle
  • Easily achieve lazy loading

Code

  • Fork/clone eZJSFunBundle and install into src/nervetattoo/
    • git@github.com:nervetattoo/eZJSFunBundle.git
  • Enable it in EzPublishKernel.php
  • Symlink assets ezpublish/console assets:install web --symlink
  • Create a new object and take note of its ID
  • Edit Resources/config/overrides.yml and replace the Location Id

Hello world module

Resources/public/js/hello_world.js

define(['jquery'], function($) {  return function(selector) {    $(selector).text('Hello world');  };});

Run it

In dev tools:

require(['hello_world'], function(hello) {  hello('body');});
What have we achieved?
  • No global scope pollution
  • While using hello_world we do not have to provide its jquery dependency — we don't know about it and it can be refactored.
  • Loose coupling, can rewire hello_world to something API compliant:
requirejs.config({  paths: {    'hello_world': 'hello_moon'  }});

Initial Require.js configuration

requirejs.config({  baseUrl: '/ezjsfunbundle/js',  paths: {    jquery: "libs/jquery/jquery.js",
    underscore: "libs/lodash/dist/lodash.compat.js",
    backbone: "libs/backbone/backbone"
  }
});
Path mappings provide shorter names

Bower

  • JavaScript package manager for the frontend
  • Think npm for the client
  • bower install jquery
  • bower search backbone
  • Easier third-party dependency management
  • bower update
  • Updates according to .bowerrc
  • Require.js path mappings?
  • bower list --paths --json
  • bower.io

Task

Creating our first module.

  1. Install required packages from bower
  2. Create public/js/main.js
  3. Configure require.js path mappings from bower
  4. Create a hello world module that depends on jquery

Automation with ...


A JavaScript task runner


grunt.initConfig({  //task: {configobj}  uglify: {
    build: {
      src: 'Resources/public/js/main.js',
      dest: 'Resources/public/js/main.min.js'
    }
  }});
> grunt uglify

Lots of existing tasks:


  • Sass, Less, Stylus, Uglify, JSHint, Handlebars, HTMLMin, Imagemin, LiveReload, Watch, Requirejs +++
  • 1314 existing tasks
  • Write your own if somethings missing
  • Validating and building frontend code is best done using JS

Installing


Depends on Node.js + NPM

  • sudo npm install -g grunt-cli
  • npm install --save-dev grunt

Installing plugins

  • npm install --save-dev grunt-contrib-jshint grunt-contrib-uglify


Node.js

  • Chromes V8 ripped out into a non-blocking I/O-platform
  • Fast and popular — JS on the server — JS everywhere!
  • Node Packaged Modules: 40k modules raring to go
  • Whatever your needs, NPM got you covered.
  • (From AST based code transformations to flying drones)
  • package.json in your repo describes your project + deps
    • npm init . to generate it

Task

Building our module

  • Using r.js to trace dependencies and combine
    • Via grunt-contrib-requirejs

Live coding

Serious JavaScript

By nervetattoo

Serious JavaScript

  • 2,969