赶上ECMAScript的潮流

2018.11

// lib.mjs
export const repeat = (string) => `${string} ${string}`;

// main.mjs
import {repeat} from './lib.mjs';
repeat('#io18');
// usage in HTML
<script type="module" src="/mypath_to_js_module.mjs"></script>
<script nomodule src="fallback.js"></script>
// preload 预加载一些公共库与代码
<link rel="modulepreload" href="lib.mjs" >
// mjs 后缀不是强制,但在 node 实验性新特性中 mjs 是必须的
node --experimental-modules main.mjs

Native Module

Native Module

// 这是多少
1000000000000
   1019436871.42
// 十进制
1_000_000_000_000
1_019_436_871.42
// 十六进制
0b01001001_00101111_01001111
0x23_69_6F_31_38

tc39/proposal-numeric-separator

tc39/proposal-numeric-separator

console.log(Number.MIN_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER); // -9007199254740991
// 例子
BigInt(Number.MAX_SAFE_INTEGER) + 2n;
// → 9_007_199_254_740_993n ✅

1234567890123456789 * 123;
// → 151851850485185200000 ❌

1234567890123456789n * 123n;
// → 151851850485185185047n ✅
42n === BigInt(42);

typeof 123n; // 'bigint'

BigInt

BigInt

// old
const print = (readable) => {
    readable.setEncoding('utf8');
    let data = '';
    readable.on('data', (chunk) => {
        data += chunk;
    });
    readable.on('end', () => {
        console.log(data);
    })
}

const fs = require('fs');
print(fs.createReadStream('./file.txt'));
// new
async function print(readable) {
    readable.setEncoding('utf8');
    let data = '';
    for await (const chunk of readable) {
        data += chunk;
    }
    
    console.log(data);
}

const fs = require('fs');
print(fs.createReadStream('./file.txt'));

Async Iterator/Generator

Async Iterator/Generator

More at http://2ality.com/2016/10/asynchronous-iteration.html

const input = `
Hi, Fliggy. Hello 
world.
`;

/Hello.world/u.test(input); 
/Hello[\s\S]world/u.test(input); // 所有空格和所有非空格匹配 true
/Hello[^]world/u.test(input); // 所有非空 true
/Hello.world/su.test(input); // dotAll mode true

RegExp dotAll mode

RegExp dotAll mode

const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-10');
// result[0] === '2017-07-10'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '10'
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-10');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '10'

Named captures

Named captures

const string = 'Magic hex numbers: DEADBEEF CAFE 8BADF00D';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
    console.log(match);
}
for (const match of string.matchAll(regex)) {
    console.log(match);
}

String Matchall

String Matchall

try {} catch (e) {} // 以前

try {} catch {} // 现在

Catch binding

Catch binding

const string = '      hello        ';
string.trim(); // 'hello';
string.trimStart(); // 'hello        ';
string.trimEnd(); // '      hello';

String trim

String trim

let isLoading = true;

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { 
    /* process your JSON further */
    isLoading = false;
  })
  .catch(function(error) { 
    isLoading = false;
    console.log(error); });

// ?
let isLoading = true;

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { /* process your JSON further */ })
  .catch(function(error) { console.log(error); })
  .finally(function() { isLoading = false; });

Promise.prototype.finally

Promise.prototype.finally

const person = {
    firstName: 'Sebastian',
    lastName: 'Markbåge',
    country: 'USA',
    state: 'CA',
};
const { firstName, lastName, ...rest } = person;
console.log(firstName); // Sebastian
console.log(lastName); // Markbåge
console.log(rest); // { country: 'USA', state: 'CA' }

// Merge two objects:
const defaultSettings = { logWarnings: false, logErrors: false };
const userSettings = { logErrors: true };
// The old way:
const settings1 = Object.assign({}, defaultSettings, userSettings);
// The new way:
const settings2 = { ...defaultSettings, ...userSettings };
// Either results in:
// { logWarnings: false, logErrors: true }

Object rest & spread

Object rest & spread

// 原来
class Counter extends HTMLElement {
  clicked() {
    this.x++;
    window.requestAnimationFrame(this.render.bind(this));
  }

  constructor() {
    super();
    this.onclick = this.clicked.bind(this);
    this.x = 0;
  }

  connectedCallback() { this.render(); }

  render() {
    this.textContent = this.x.toString();
  }
}
window.customElements.define('num-counter', Counter);
class Counter extends HTMLElement {
  x = 0;

  clicked() {
    this.x++;
    window.requestAnimationFrame(this.render.bind(this));
  }

  constructor() {
    super();
    this.onclick = this.clicked.bind(this);
  }

  connectedCallback() { this.render(); }

  render() {
    this.textContent = this.x.toString();
  }
}
window.customElements.define('num-counter', Counter);
class Counter extends HTMLElement {
  #x = 0;

  clicked() {
    this.#x++;
    window.requestAnimationFrame(this.render.bind(this));
  }

  constructor() {
    super();
    this.onclick = this.clicked.bind(this);
  }

  connectedCallback() { this.render(); }

  render() {
    this.textContent = this.#x.toString();
  }
}
window.customElements.define('num-counter', Counter);

Class fileds

Class fileds

黯晓

2018.11