How to Babel – Template Strings – Fat Arrows



How to Babel – Template Strings – Fat Arrows

0 0


ember-babel-talk

Tips for Babelifying your Ember.js code

On Github treyhunner / ember-babel-talk

How to Babel

Trey Hunner / @treyhunner

My name is Trey. I'm going to show you something.

ECMAScript History

Edition Release Date Browser Support a.k.a. 3 Dec. 1999 All ES3 5 Dec. 2009 IE 9, FF 4, Chrome 7 ES5 6 June 2015 Not supported yet ES2015

First, some terminology.

ECMAScript is the language specification that JavaScript is based on.

ECMAScript 6 is the new version that was finalized last month.

Earlier this year, ECMAScript 6 was renamed to ECMAScript 2015. During this talk I'll use ECMAScript 2015 and ECMAScript 6 interchangeably.

Use next generation JavaScript, today.

This new language spec isn't supported by browsers, but you can start using it today.

You just need to use Babel. Babel allows you to compile your ES6 code down to ES5 code, which is understood by all modern web browsers.

Let

// var: function scoping, variables are hoisted
var array = [];
console.log(i); // prints undefined
for (var i = 0; i < 10; i++) {
  array.push(Math.random());
}
console.log(i);  // prints 10

// let: block scoping, no hoisting
let array = [];
console.log(i);  // ReferenceError
for (let i = 0; i < 10; i++) {
  array.push(Math.random());
}
console.log(i);  // ReferenceError

You can now use "let" instead of "var" to define variables. Unlike var, let uses function-level scoping and does not hoist variables.

I highly recommend you switch all uses of "var" to "let".

We'll be using let in the rest of our code examples.

Template Strings

var pageNumber = "( " + getCount() + " / " + pageTotal + " )";

let pageNumber = `( ${getCount()} / ${pageTotal} )`;

If you have ever tried to stick a variable inside a string in JavaScript, you'll know it requires concatenation. Look at all those plus signs and quotes.

With template strings, you can include your variables right inside your string. Just use backticks and you've made a template string.

Template Strings

var poem = [
  "JavaScript = fun",
  "Uncaught ReferenceError",
  "fun is not defined"
].join("\n");

let poem = `JavaScript = fun
Uncaught ReferenceError
help is not defined`;

Template strings also support multi-line strings.

Previously you may have used string concatenation or the Array join method to make multi-line strings. Now you can just use template strings.

Methods

var Duck = Backbone.Model.extend({
  name: function name() {
    return "duck";
  },
  sound: function sound() {
    return "quack";
  }
});

let Duck = Backbone.Model.extend({
  name() {
    return "duck";
  },
  sound() {
    return "quack";
  }
});

With the new method syntax, you can remove a lot of the syntax cruft when defining methods. This is great for those of you using an MVC framework.

Notice the lack of the word function. Also note that the functions in these examples are named, not anonymous.

Fat Arrows

promise.then(function (data) {
  return data.response;
}).display();

promise.then(data => data.response).display();

JavaScript loves callbacks. Fat arrow functions allow you to make one-off callback functions with a much simpler syntax.

In this example, we've lost the parenthesis, the curly braces, the return statement, and the word function. This is so much shorter.

Fat Arrows & this

var self = this;
var getValue = function () {
  return self.value;
});

let getValue = () => this.value;

The this variable inside a fat arrow function refers to the same this as outside of it.

No more need to make self or that variables and no need to worry about function binding.

Fat Arrows

events.sort(function (e1, e2) {
  var d1 = new Date(e1.startDate);
  var d2 = new Date(e2.startDate);
  return d1 - d2;
});

events.sort((e1, e2) => {
  let d1 = new Date(e1.startDate);
  let d2 = new Date(e2.startDate);
  return d1 - d2;
});

Destructuring Arrays

var numbers = [1, 4, 2, 3];
var x = numbers[0];
var y = numbers[1];
var z = numbers[2];

let numbers = [1, 4, 2, 3];
let [x, y, z] = numbers;

Grabbing a sequence of elements out of an array used to be awkward in JavaScript.

With array destructuring, it's really easy.

This syntax works on anything you can iterate over.

Object Destructuring

var emoji = {category: "people", char: "😁", name: "smile"};
var char = emoji.char;
var name = emoji.name;
var category = emoji.category;

let emoji = {category: "people", char: "😁", name: "smile"};
let {name, category, char} = emoji;

I just showed you array destructuring. We have a new object destructuring syntax now too.

This new syntax allows you to assign multiple variables to the values of multiple attributes of an object.

Object Restructuring

function makeEmoji(name, char, category) {
  return {name: name, char: char, category: category};
}

function makeEmoji(name, char, category) {
  return {name, char, category};
}

I just showed you array destructuring. We have a new object destructuring syntax now too.

This new syntax allows you to assign multiple variables to the values of multiple attributes of an object.

For..Of

var coordinates = [{x: 1, y: 4}, {x: 3, y: 3}, {x: 2, y: 2}];
var timesAtHome = 0;
var c;

for (var i = 0; i < coordinates.length; i++) {
  c = coordinates[i];
  if (c.x === 3 && c.y === 3) {
    timesAtHome += 1;
  }
}

let coordinates = [{x: 1, y: 4}, {x: 3, y: 3}, {x: 2, y: 2}];
let timesAtHome = 0;

for (let {x, y} of coordinates) {
  if (x === 3 && y === 3) {
    timesAtHome += 1;
  }
}

Let's loop through an array of coordinates and count the number of times we visit our home base. We'll use a variable i as a counter, starting it at 0, and incrementing it after each loop. We'll end the looping when i equals the length of the array. Each time we loop, we'll assign c to the ith element of our array. When c.x and c.y are both 3 then we'll increment timesAtHome.

Let's loop through an array of coordinates and count the number of times we visit our home base. We'll loop over each element of the array, getting the x and y attributes from it. When x and y are both 3 we'll increment timesAtHome.

This new "for of" syntax, combined with object destructuring, makes our code much shorter.

Rest

function say(phrase, ...folks) {
  folks.forEach(function (folk) {
    console.log(folk + ' said "' + phrase + '"');
  });
}

function say(phrase, ...folks) {
  folks.forEach(folk => console.log(`${folk} said "${phrase}"`));
}

The rest operator is kind of like Ruby's splat operator.

When defining a function, we can use the rest operator (notated by three dots) to capture the rest of our function arguments.

Spread

var people = ["Bigelow", "Quil", "Trady Blix", "Lara"]
shout.apply(undefined, "hello world", people)

let people = ["Bigelow", "Quil", "Trady Blix", "Lara"];
shout("hello world", ...people);

The spread operator is also like the splat operator.

When calling a function, we can use the spread operator to place an array of items into separate arguments in a function call.

Capture Remaining Values

var numbers = [1, 2, 3, 4];
var first = numbers[0];
var remaining = numbers.slice(1);
var numbers = [1, 2, 3, 4];
var [first, ...remaining] = numbers;

You can combine the spread operator with iterable unpacking to capture remaining values in an array.

For example here the variable remaining would be the array [2, 3, 4].

Combine Arrays

var first = [1, 2];
var second = [3, 4];
var combined = [].concat(first, second);
var first = [1, 2];
var second = [3, 4];
var combined = [...first, ...second];

You can also use the spread operator to unpack one array inside of another array. This allows us to combine arrays easily.

So in this example combined would be the array [1, 2, 3, 4].

Default Arguments

function greet(name) {
  name = name || 'there';
  console.log('Hello ' + name + '!');
}

function greet(name='there') {
  console.log(`Hello ${name}!`);
}

JavaScript functions can now have default argument values.

This does not mean JavaScript has keyword arguments. It doesn't. These are just default values for positional arguments.

Example 1: Before

import Ember from 'ember';

export default Ember.Component.extend({
  // ...
  dragEnter: function dragEnter() {
    Ember.run(function () {
      this.incrementProperty('hoverCounter')
    });
  },
  dragLeave: function dragEnter() {
    Ember.run(function () {
      this.decrementProperty('hoverCounter')
    });
  },
  // ...
});

Say something here.

Example 1: After

import Ember from 'ember';

export default Ember.Component.extend({
  // ...
  dragEnter() {
    Ember.run(() => this.incrementProperty('hoverCounter'));
  },
  dragLeave() {
    Ember.run(() => this.decrementProperty('hoverCounter'));
  },
  // ...
});

Say something here.

Example 2: Before

actions: {
  addFile: function addFile (data) {
    var file = data.file;
    var name = data.fileName;
    var password = data.password;
    var uploader = Uploader.create();
    uploader.upload(name, file).then(function () {
      this.sendAction('fileUploadedAction', {
      file: file,
      password: password,
    });
    }.bind(this));
  },
}

Say something here.

Example 2: After

actions: {
  addFile({file, fileName, password}) {
    let uploader = Uploader.create();
    uploader.upload(fileName, file).then(() => {
      this.sendAction('fileUploadedAction', {file, password});
    });
  },
}

Say something here.

Example 3: Before

import Ember from 'ember';

export default Ember.Component.extend({
  // ...
  flashCopyMessage: function(copied, char) {
    var flashMessages = Ember.get(this, 'flashMessages');
    var charHTML = '<span class="emoji-text">' + char + '</span>';
    flashMessages.clearMessages();
    if (copied) {
      flashMessages.success('Copied ' + charHTML);
    } else {
      flashMessages.danger("Couldn't copy " + charHTML);
    }
  },
});

Say something here.

Example 3: After

import Ember from 'ember';

export default Ember.Component.extend({
  // ...
  flashCopyMessage(copied, char) {
    const flashMessages = Ember.get(this, 'flashMessages');
    const charHTML = `<span class="emoji-text">${char}</span>`;
    flashMessages.clearMessages();
    if (copied) {
      flashMessages.success(`Copied ${charHTML}`);
    } else {
      flashMessages.danger(`Couldn't copy ${charHTML}`);
    }
  },
});

Say something here.

Example 4: Before

encryptFile: function () {
  var promise = new Ember.RSVP.Promise(function (resolve, reject) {
    var key = this.get('key');
    readFile(this.get('file')).then(function (file) {
      file.data = encrypt(key, file.data);
      this.sendAction('encryptedAction', {file: file, key: key});
      resolve(file);
    }).catch(function (event) {
      reject('An error occurred reading the file.');
    });
  });
  return DS.PromiseObject.create({promise: promise});
},

Say something here.

Example 4: After

encryptFile() {
  let promise = new Ember.RSVP.Promise((resolve, reject) => {
    let key = this.get('key');
    readFile(this.get('file')).then(file => {
      file.data = encrypt(key, file.data);
      this.sendAction('encryptedAction', {file, key});
      resolve(file);
    }).catch(event => {
      reject('An error occurred reading the file.');
    });
  });
  return DS.PromiseObject.create({promise});
},

Say something here.

ES6 Tools for Ember-CLI

Something

Go Forth and Babel

Learn ES2015: https://babeljs.io/docs/learn-es2015/

Try it out: http://babeljs.io/repl

Okay. I have a mission for you now.

After you go home tonight I want you all to go to Babel website and try it out. Type in some code using the new JavaScript syntax and see what it will convert it to. If you don't have time tonight, try it out this weekend.

You can also go to trey.in/everyday-es6-demo to see how Babel transpiles the ES6 code examples in this talk.

Don't worry, I'll post a link to my slides to Meetup.

Final

Trey Hunner / @treyhunner

Web consultant available for hire http://truthful.technology

Any questions?

How to Babel Trey Hunner / @treyhunner My name is Trey. I'm going to show you something.