
Jordan Kasper | @jakerella
What's the "ES" and how did we get to 6...
or 2015... or whatever?
http://caniuse.com/#feat=es6-module
They're awesome, and you should look into them...
... especially in combination with the
new fetch API
(versus traditional Ajax libraries).
class Todo {
constructor(text) {
this.text = text;
this.isComplete = false;
}
}
let item = new Todo('finish presentation');
class Todo {
constructor(text, dueDate) {
this.text = text;
this.isComplete = false;
this._dueDate = dueDate;
}
}
class Todo {
constructor(text, dueDate) {
this.text = text;
this.isComplete = false;
// Default dueDate is tomorrow
this._dueDate = dueDate || Date.now() + 86400000;
}
}
class Todo {
constructor(text = 'stuff', dueDate) {
this.text = text;
this.isComplete = false;
this._dueDate = dueDate || Date.now() + 86400000;
}
}
class Todo {
constructor(text='stuff', dueDate = Date.now() + 86400000) {
this.text = text;
this.isComplete = false;
this._dueDate = dueDate;
}
}
class App {
constructor() {
// kick off all the things...
}
}
new App(); // Get things started
class App {
constructor() {
document.querySelector('.create-todo')
.addEventListener('submit', (eventObj) => {
eventObj.preventDefault();
// do the work!
});
}
}
this)// ES5 callback
someFunction() {
someNode.addEventListener("click", function (eventObj) {
console.log(this); // someNode
});
}
// ES6 arrow function
someFunction() {
someNode.addEventListener("click", (eventObj) => {
console.log(this); // whatever someFunction's `this` is
});
}
The question is: do you need it?
[1, 2, 3].map(num => {
console.log(num); // 1 (then 2, then 3)
console.log(this); // undefined
})
someNode.addEventListener("click", (eventObj) => {
console.log(this); // undefined
console.log(eventObj.target); // someNode (probably)
});
One note: if you delegate events this gets tricky!
[1, 2, 3].map( num => num * num ); // [1, 4, 9]
Note there are no curly braces!
num => num * num
Only when there is no code block (curly braces).
class App {
constructor() {
document.querySelector('.create-todo')
.addEventListener('submit', (eventObj) => {
eventObj.preventDefault();
App.createTodo();
});
}
static createTodo() {
const text = document.querySelector('.todo-text').value;
const item = new Todo(text);
}
}
let AND consta short tangent
let and block scopinglet z = 1;
if (x === y) {
let z = 10;
z++;
}
// what is `z` now?
`z` will be 1 at the bottom!
let and block scopingfor (var i=0; i<10; i++) {
// do some stuff...
}
console.log(i); // i is 10 outside the loop!
for (let i=0; i<10; i++) {
// do some stuff...
}
console.log(i); // ReferenceError!
let vs constlet counter = 1;
counter++; // totally fine
const index = 1;
index++; // TypeError
let vs constconst o = {};
o.foo = 'bar'; // totally fine
constant reference vs. constant value
class App {
constructor() { /* ... */ }
static createTodo() {
const text = document.querySelector('.todo-text').value;
const item = new Todo(text);
// put it into the DOM...
}
}
class Todo {
constructor() { /* ... */ }
render() {
return
`<li>
<p>${this.text}</p>
<time>${this._dueDate}</time>
</li>`;
}
}
class Todo {
constructor() { /* ... */ }
render() {
return
`<li class="${(this.isComplete) ? 'complete' : ''}">
<p>${this.text}</p>
<time>${this._dueDate}</time>
</li>`;
}
}
class App {
// ...
static createTodo() {
const text = document.querySelector('.todo-text').value;
const item = new Todo(text);
const list = document.querySelector('.todo-list');
list.innerHTML += item.render();
}
}
<li class="">
<p>finish presentation</p>
<time>1508702292483</time>
</li>
(Yes, that spacing is intentional, but do you care?)
class Todo {
constructor() { /* ... */ }
render() { /* ... */ }
get dueDate() {
return (new Date(this._dueDate)).toLocaleString();
}
set dueDate(value) {
this._dueDate = (new Date(value)).getTime();
}
}
render() {
return
`<li class="${(this.isComplete) ? 'complete' : ''}">
<p>${this.text}</p>
<time>${this.dueDate}</time>
</li>`;
}
Look closesly at the <time> element!
<li data-id="1234567890">
<p>finish presentation</p>
<time>10/22/2017, 4:07:55 PM</time>
</li>
I don't like repeating myself...
this.text
this.dueDate
this.isComplete
render() {
const { text, dueDate, isComplete } = this;
return
`<li class="${(isComplete) ? 'complete' : ''}">
<p>${text}</p>
<time>${dueDate}</time>
</li>`;
}
What about saving the Todo Item?
Resource
| |
Todo User
|
Admin
class Resource {
constructor() { } // You need this, but it can be blank
save() { }
get() { }
destroy() { }
}
class Todo extends Resource {
constructor( /* params... */ ) {
super();
// your other code...
}
}
function Todo() {
Resource.prototype.constructor.apply(this, Array.from(arguments));
// ...
}
Todo.prototype = Object.create(Resource.prototype);
Todo.prototype.constructor = Todo;
...which is fine,
but you still need to know about prototypes!
class Resource {
constructor() { }
save() { } // let's look closer at this...
get() { }
destroy() { }
}
class Resource {
constructor() { }
save() {
// What if there's bad data?
// We need validation!
// do the save...
// maybe with fetch()?
}
}
class Resource {
constructor() { }
save() {
this.validate();
// do the save...
// maybe with fetch()?
}
}
class Resource {
constructor() { }
save() {
/* ... */
}
validate() {
if (this.id && typeof(this.id) !== "number") {
throw new Error("Invalid resource ID");
}
}
}
class Todo extends Resource {
constructor() { /* ... */ }
render() { /* ... */ }
validate() {
if (typeof(this.text) !== "string") {
throw new Error("Invalid Todo text");
}
// more data checks...
}
}
We have a problem... the ID is never checked!
Don't duplicate, reuse!
class Todo extends Resource {
constructor() { /* ... */ }
render() { /* ... */ }
validate() {
super.validate();
if (typeof(this.text) !== "string") {
throw new Error("Invalid Todo text");
}
// more data checks...
}
}
This is still just prototypes!
(Be careful, it's really just a new keyword that behaves differently in different conditions.)
(You still need to know about prototypes.)
class App {
// ...
static createTodo() {
const text = document.querySelector('.todo-text').value;
const item = new Todo(text);
try {
item.save();
const list = document.querySelector('.todo-list');
list.innerHTML += item.render();
} catch(err) {
// Handle the error!
// (Maybe tell the user something's wrong?)
}
}
}
... but can I use these today?
| Feature | Modern Browsers? | IE 11? |
|---|---|---|
| Classes | ◕‿◕ | ಠ_ಠ |
| Default Fn args | ◕‿◕ | ಠ_ಠ |
| let and const | ◕‿◕ | o_o |
| Template Literals | ◕‿◕ | ಠ_ಠ |
| Object Deconstruction | ◕‿◕ | ಠ_ಠ |
| Promises | ◕‿◕ | ಠ_ಠ |
| Arrow Functions | ◕‿◕ | ಠ_ಠ |
| fetch | ◕‿◕ | ಠ_ಠ |
Jordan Kasper | @jakerella