<info 340/>
ES6+ Syntax
Joel Ross
Autumn 2022
Project Draft Iteration
If there were problems in Draft 1, you can iterate and fix those problem for Draft 2.
When you submit Draft 2, if you've fixed the problems we will increase your score on Draft 1 by up to 20% (e.g., from a 4/5 to a 5/5).
Do not resubmit Draft 1! We will adjust scores when you submit Draft 2.
Now Hiring TAs!
TAing positions added regularly to:
https://ischool.uw.edu/about/jobs/students
For more information: ihrhelp@uw.edu
View of the Day
-
Review Q&A
- We have time to go over past things!
-
JavaScript Versions (lecture + demo)
-
Arrow Functions (lecture + pollev)
-
Destructure & Spread (lecture + pollev)
-
ES6 Modules (demo)
Review Q&A
JavaScript History
The official name for JavaScript is ECMAScript, often abbreviated "ES"
- 1995: developed by Brendan Eich (co-founder of Mozilla) for the Netscape browser--took 10 days.
- 2011: version 5; first major revision since 1999.
- 2015: version 6 ("ES 6") released
- 2016: "ES 2016" released
- Annual releases since then, named for the year
As with CSS, actually providing specified features depends on the browser.
ES6+ Features
ES 6 is now supported by most major browsers, and introduces some key language features that are commonly used in React. In particular:
- Arrow Functions (ES 6)
- Destructuring and Spreading (ES 6; ES 2018)
- Modules (ES 6, only recently supported in browser)
Syntactic Sugar causes cancer of the semicolon
Arrow Functions
A
shortcut syntax for declaring functions using
=>
//normal function declaration
const foo = function(params) {
return 'foo '+params;
}
//arrow function declaration (block body)
const foo = (params) => {
return 'foo '+params;
}
//arrow function declaration (concise body)
const foo = (params) => 'foo '+params;
single expression implies return
//arrow function declaration (concise, no params)
const sayHello = () => console.log('Hello world!');
anonymous function assigned to a variable
Arrow Functions
Arrow functions are particularly nice for anonymous callback functions (literals), and in fact should always be used.
const array = [1,2,3]; //an array to work with
//normal function
array.map(function(num) {
return num*2; //multiply each item by 2
});
//arrow syntax
array.map((num) => {
return num*2; //multiply each item by 2
});
//concise body - ONLY FOR TINY EXPRESSIONS
//(better to avoid entirely)
array.map((num) => num*2);
const doggy = {
name: "Fido",
bark: () => console.log("woof")
}
Code Style
Recommendation: don't use concise body syntax. Always include the explicit block.
- It's easier to read (more clearly a function)
- It's easier to modify (can add additional statements)
- It's easier to debug (can add console logs)
/* Do this */
const exclaimed = stringArray.map((aString) => {
return aString + "!!" //add exclamation points
})
/* Do not do this */
const exclaimed = stringArray.map((aString) => aString + "!!")
Destructuring
Destructuring assignment lets you assign (unpack) each value of an array or object into into separate variables in a single operation.
//destructuring arrays
const myArray = [1, 2, 3];
const [x, y, z] = myArray; //myArray elements to `x`, `y`, `z`
console.log(x); //=> 1;
console.log(y); //=> 2;
console.log(z); //=> 3;
//destructuring objects
const myObject = {a: 1, b: 2, c: 3};
const {a, b, c} = myObject; //myObject.a to a, etc.
console.log(a); //=> 1
console.log(b); //=> 2;
console.log(c); //=> 3;
Destructuring Params
It's possible to destructure function parameters. This is commonly used in React (with object params)... but can make code maintenance harder.
//an example person objbect
const person = {name: 'Ada', height: 64, weight: 135}
//a function that expects a person object
function getBMI(personObj) {
const height = person.height;
const weight = person.weight;
return 703*weight/(height*height);
}
const adaBMI = getBMI(person);
//implicit
const personObj = {
name: 'Ada', height: 64, weight: 135
}
//implicit - destructured
const {height, weight} = {
name: 'Ada', height: 64, weight: 135
}
//an example person objbect
const person = {name: 'Ada', height: 64, weight: 135}
//a function that expects a person object
function getBMI({height, weight}) {
return 703*weight/(height*height);
}
const adaBMI = getBMI(person);
Spread Operator
const dimensions = [10, 20, 30, 40];
//extra values are "spread" into destructuring slots
const [width, height, ...rest] = dimensions
console.log(width); //=> 10
console.log(height); //=> 20
console.log(rest); //=> [30, 40]; the rest of the values!
The spread operator (sometimes rest operator) lets you apply the elements of an array or object into multiple "slots" (e.g., in an assignment or argument). This is particularly useful for duplicating arrays/objects
const person = {name: 'Ada', height: 64, weight: 135}
const copyOfPerson = {...person}; //clone an object!
console.log(person === copyOfPerson); //false! different objects!
//all off the properties are "spread" into the new object
const personWithHat = {hat: 'bowler', ...person}
console.log(person); //has name, height, weight
console.log(personWithHat); //has name, height, weight, hat
Spread Operator
//a function that adds up all the arguments (no matter how many!)
function sum(...numbers) {
//all arguments are gathered in the `numbers` array
//numbers is an array, so we can `reduce()` it!
let total = numbers.reduce((runningTotal, num) => {
return runningTotal + num; //new total
}, 0); //start at 0
return total;
//or as one line with a concise arrow function:
return numbers.reduce((total, n) => total+n);
}
sum(3, 4, 3); // => 10
sum(10, 20, 30, 40); // => 100
When used in a function argument, a spread operator will gather all of the arguments into an array.
ES 6 Modules
Modules are self-contained, isolated scripts that are able to run in their own namespace (without polluting the global environment). Modules can export values that may be imported and used by other modules.
Web pages load modules by including a script tag with the type="module" attribute.
<!-- load a script as a module -->
<script type="module" src="path/to/module.js"></script>
Live-Server
cd path/to/project
npx live-server .
where index.html is!
dot to serve entire folder
When an HTML page is opened with the file:// protocol, JavaScript cannot access system files. You need to run a local web server instead to use the http:// protocol.
You can do this with the live-server package
Access the webpage at http://localhost:8080/index.html
Stop the server by using ctrl+c in the command shell.
Importing
It is "possible" to import external libraries and make them available in your script from JavaScript (not HTML)
CommonJS (used by Node.js)
ES 6 Modules (used by browsers)
const util = require('util');
node version of "import"
global to refer
to the library
module library name
import { ArrayList } from 'util'; //named import
module library name
Java
import java.util.ArrayList;
module name
variable to import
variable to import
Exporting Values
export variables, functions, and classes to be available for other modules to
import.
/*** my-module.js ***/
export function foo() { return 'foo'; } //named export
export const bar = "bar"; //export a variable
//will not be available (a "private" function)
function baz() { return 'baz'; }
/*** index.js ***/
//example of named imports:
import {foo} from './my-module.js'; //single named import
import {foo, bar} from './my-module.js'; //multiple named import
foo() //=> 'foo'
console.log(bar) //=> 'bar'
relative path to file (start with ./)
.js extension is implied by default in Node
Importing Options
Use the as keyword to "alias" a value.
Use import * syntax to import all values from a module.
/*** my-module.js ***/
export function foo() { return 'foo'; } //"named" export
//provide an "alias" (consumer name) for value
export { bar as yourBar };
/*** index.js ***/
import { yourBar } from './my-module.js'; //import value by name
yourBar() //=> 'bar'
import { bar } from './my-module.js'; //error, no value `bar` exported
//provide "alias" for value when importing!
import {foo as myFoo} from './my-module.js';
myFoo(); //=> 'foo'
//import everything that was exported
//loads as a single object with values as properties
import * as theModule from './my-module.js';
theModule.foo(); //=> 'foo'
theModule.yourBar(); //=> 'bar'
Default Exports
Each module can have a single default export, which provides a shortcut when importing.
/*** my-module.js ***/
export default function sayHello() {
return 'Hello world!';
}
/*** index.js ***/
//default import can assign own alias without needing name!
//think: "import {default as greet} from './mymodule.js'"
import greet from './my-module.js';
greet(); //=> "Hello world!"
Be careful about whether an export is named or default!
External libraries especially are not consistent.
When to export?
- Only export a value if it must be used by another module. When in doubt, keep values private.
- In general, prefer named exports. This makes it easier to export additional values later if needed.
- If a module is designed around a single value or function (e.g., a single React Component), make it a default export. A single exported value isn't necessarily a default export!
Action Items!
Action Items!
-
Read Ch 15: React
-
Problem Set 06 due next Tuesday (1 week)
Next time: React! Finally!
Working with
this
In JavaScript, functions are called on
objects. In a function,
this is a local variable that
implicitly
assigned that object as its value.
//An object representing a Dog
let doggy = {
name: "Fido",
bark: function() { console.log(this.name + " woofs"); }
}
// An object representing another Dog
let doggo = {
name: "Spot",
bark: function() { console.log(this.name + " yips")}
}
//This is Fido barking
doggy.bark( /*this = doggy*/ ); //=> "Fido woofs"
//This is Spot barking
doggo.bark( /*this = doggo*/ ); //=> "Spot yips"
//This is Fido using Spot's bark!
doggy.bark = doggo.bark; //assign the function value to `doggy`
doggy.bark( /*this = doggy*/) //=> "Fido yips"
Working with
this
class Person {
constructor(name) {}
//greet each person in the list
greetAll(peopleArray) {
//loop through each person using a callback
peopleArray.forEach(function(person) {
console.log("Hi " + person.name + ", I'm " + this.name);
});
}
}
Because
this is assigned to whatever object the function is called on, bugs can occur...
what is this called on?
undefined!
this & Arrow Functions
class Person {
constructor(name) {}
//greet each person in the list
greetAll(peopleArray) {
//loop through each person using a callback
peopleArray.forEach((person) => {
console.log("Hi " + person.name + ", I'm " + this.name);
});
}
}
Arrow functions do not create a new context (and bind a different value to
this), making it work as expected!
the object!
Always use arrow functions for anonymous callbacks!
arrow function is "called" on the `this` when it was created!
/*** module.js ***/
export function foo() { console.log('foo') }
export function bar() { console.log('bar') }
export default function baz() { console.log('baz') }
/*** index.js ***/
/* A */
import bar from './module.js';
/* B */
import { bar } from './module.js';
/* C */
import * as bar from './module.js';
/* D */
import { * as bar } from './module.js';
//call bar to print "bar"
bar();
info340au22-es6
By Joel Ross
info340au22-es6
- 477