Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【ES6/7/8】学习整理 #11

Open
Marinerer opened this issue Mar 26, 2018 · 9 comments
Open

【ES6/7/8】学习整理 #11

Marinerer opened this issue Mar 26, 2018 · 9 comments

Comments

@Marinerer
Copy link
Owner

Marinerer commented Mar 26, 2018

@Marinerer Marinerer changed the title ES6/7/8 知识点 【ES6/7/8】知识点 Mar 26, 2018
@Marinerer
Copy link
Owner Author

Marinerer commented Apr 23, 2018

源自:ES6 完全使用手册:mqyqingfeng/Blog#111

前言

  1. 这里的 "ES6" 泛指 ES5 之后的新语法
  2. 这里的 "完全" 是指本文会不断更新
  3. 这里的 "使用" 是指本文会展示很多 ES6 的使用场景
  4. 这里的 "手册" 是指你可以参照本文将项目更多的重构为 ES6 语法

此外还要注意这里不一定就是正式进入规范的语法。

1. let 和 const

在我们开发的时候,可能认为应该默认使用 let 而不是 var,这种情况下,对于需要写保护的变量要使用 const。

然而另一种做法日益普及:默认使用 const,只有当确实需要改变变量的值的时候才使用 let。这是因为大部分的变量的值在初始化后不应再改变,而预料之外的变量的修改是很多 bug 的源头。

// 例子 1-1

// bad
var foo = 'bar';

// good
let foo = 'bar';

// better
const foo = 'bar';

2. 模板字符串

1. 模板字符串

需要拼接字符串的时候尽量改成使用模板字符串:

// 例子 2-1

// bad
const foo = 'this is a' + example;

// good
const foo = `this is a ${example}`;

2. 标签模板

可以借助标签模板优化书写方式:

// 例子 2-2

let url = oneLine `
    www.taobao.com/example/index.html
    ?foo=${foo}
    &bar=${bar}
`;

console.log(url); // www.taobao.com/example/index.html?foo=foo&bar=bar

oneLine 的源码可以参考 《ES6 系列之模板字符串》

3. 箭头函数

优先使用箭头函数,不过以下几种情况避免使用:

1. 使用箭头函数定义对象的方法

// 例子 3-1

// bad
let foo = {
  value: 1,
  getValue: () => console.log(this.value)
}

foo.getValue();  // undefined

2. 定义原型方法

// 例子 3-2

// bad
function Foo() {
  this.value = 1
}

Foo.prototype.getValue = () => console.log(this.value)

let foo = new Foo()
foo.getValue();  // undefined

3. 作为事件的回调函数

// 例子 3-3

// bad
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
    console.log(this === window); // => true
    this.innerHTML = 'Clicked button';
});

4. Symbol

1. 唯一值

// 例子 4-1


// bad
// 1. 创建的属性会被 for-in 或 Object.keys() 枚举出来
// 2. 一些库可能在将来会使用同样的方式,这会与你的代码发生冲突
if (element.isMoving) {
  smoothAnimations(element);
}
element.isMoving = true;

// good
if (element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__) {
  smoothAnimations(element);
}
element.__$jorendorff_animation_library$PLEASE_DO_NOT_USE_THIS_PROPERTY$isMoving__ = true;

// better
var isMoving = Symbol("isMoving");

...

if (element[isMoving]) {
  smoothAnimations(element);
}
element[isMoving] = true;

2. 魔术字符串

魔术字符串指的是在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。

魔术字符串不利于修改和维护,风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。

// 例子 4-1

// bad
const TYPE_AUDIO = 'AUDIO'
const TYPE_VIDEO = 'VIDEO'
const TYPE_IMAGE = 'IMAGE'

// good
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()

function handleFileResource(resource) {
  switch(resource.type) {
    case TYPE_AUDIO:
      playAudio(resource)
      break
    case TYPE_VIDEO:
      playVideo(resource)
      break
    case TYPE_IMAGE:
      previewImage(resource)
      break
    default:
      throw new Error('Unknown type of resource')
  }
}

3. 私有变量

Symbol 也可以用于私有变量的实现。

// 例子 4-3

const Example = (function() {
    var _private = Symbol('private');

    class Example {
        constructor() {
          this[_private] = 'private';
        }
        getName() {
          return this[_private];
        }
    }

    return Example;
})();

var ex = new Example();

console.log(ex.getName()); // private
console.log(ex.name); // undefined

5. Set 和 Map

1. 数组去重

// 例子 5-1

[...new Set(array)]

2. 条件语句的优化

// 例子 5-2
// 根据颜色找出对应的水果

// bad
function test(color) {
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}

test('yellow'); // ['banana', 'pineapple']
// good
const fruitColor = {
  red: ['apple', 'strawberry'],
  yellow: ['banana', 'pineapple'],
  purple: ['grape', 'plum']
};

function test(color) {
  return fruitColor[color] || [];
}
// better
const fruitColor = new Map()
  .set('red', ['apple', 'strawberry'])
  .set('yellow', ['banana', 'pineapple'])
  .set('purple', ['grape', 'plum']);

function test(color) {
  return fruitColor.get(color) || [];
}

6. for of

1. 遍历范围

for...of 循环可以使用的范围包括:

  1. 数组
  2. Set
  3. Map
  4. 类数组对象,如 arguments 对象、DOM NodeList 对象
  5. Generator 对象
  6. 字符串

2. 优势

ES2015 引入了 for..of 循环,它结合了 forEach 的简洁性和中断循环的能力:

// 例子 6-1

for (const v of ['a', 'b', 'c']) {
  console.log(v);
}
// a b c

for (const [i, v] of ['a', 'b', 'c'].entries()) {
  console.log(i, v);
}
// 0 "a"
// 1 "b"
// 2 "c"

3. 遍历 Map

// 例子 6-2

let map = new Map(arr);

// 遍历 key 值
for (let key of map.keys()) {
  console.log(key);
}

// 遍历 value 值
for (let value of map.values()) {
  console.log(value);
}

// 遍历 key 和 value 值(一)
for (let item of map.entries()) {
  console.log(item[0], item[1]);
}

// 遍历 key 和 value 值(二)
for (let [key, value] of data) {
  console.log(key)
}

7. Promise

1. 基本示例

// 例子 7-1

// bad
request(url, function(err, res, body) {
    if (err) handleError(err);
    fs.writeFile('1.txt', body, function(err) {
        request(url2, function(err, res, body) {
            if (err) handleError(err)
        })
    })
});

// good
request(url)
.then(function(result) {
    return writeFileAsynv('1.txt', result)
})
.then(function(result) {
    return request(url2)
})
.catch(function(e){
    handleError(e)
});

2. finally

// 例子 7-2

fetch('file.json')
.then(data => data.json())
.catch(error => console.error(error))
.finally(() => console.log('finished'));

8. Async

1. 代码更加简洁

// 例子 8-1

// good
function fetch() {
  return (
    fetchData()
    .then(() => {
      return "done"
    });
  )
}

// better
async function fetch() {
  await fetchData()
  return "done"
};
// 例子 8-2

// good
function fetch() {
  return fetchData()
  .then(data => {
    if (data.moreData) {
        return fetchAnotherData(data)
        .then(moreData => {
          return moreData
        })
    } else {
      return data
    }
  });
}

// better
async function fetch() {
  const data = await fetchData()
  if (data.moreData) {
    const moreData = await fetchAnotherData(data);
    return moreData
  } else {
    return data
  }
};
// 例子 8-3

// good
function fetch() {
  return (
    fetchData()
    .then(value1 => {
      return fetchMoreData(value1)
    })
    .then(value2 => {
      return fetchMoreData2(value2)
    })
  )
}

// better
async function fetch() {
  const value1 = await fetchData()
  const value2 = await fetchMoreData(value1)
  return fetchMoreData2(value2)
};

2. 错误处理

// 例子 8-4

// good
function fetch() {
  try {
    fetchData()
      .then(result => {
        const data = JSON.parse(result)
      })
      .catch((err) => {
        console.log(err)
      })
  } catch (err) {
    console.log(err)
  }
}

// better
async function fetch() {
  try {
    const data = JSON.parse(await fetchData())
  } catch (err) {
    console.log(err)
  }
};

3. "async 地狱"

// 例子 8-5

// bad
(async () => {
  const getList = await getList();
  const getAnotherList = await getAnotherList();
})();

// good
(async () => {
  const listPromise = getList();
  const anotherListPromise = getAnotherList();
  await listPromise;
  await anotherListPromise;
})();

// good
(async () => {
  Promise.all([getList(), getAnotherList()]).then(...);
})();

9. Class

构造函数尽可能使用 Class 的形式

// 例子 9-1

class Foo {
  static bar () {
    this.baz();
  }
  static baz () {
    console.log('hello');
  }
  baz () {
    console.log('world');
  }
}

Foo.bar(); // hello
// 例子 9-2

class Shape {
  constructor(width, height) {
    this._width = width;
    this._height = height;
  }
  get area() {
    return this._width * this._height;
  }
}

const square = new Shape(10, 10);
console.log(square.area);    // 100
console.log(square._width);  // 10

10.Decorator

1. log

// 例子 10-1

class Math {
  @log
  add(a, b) {
    return a + b;
  }
}

log 的实现可以参考 《ES6 系列之我们来聊聊装饰器》

2. autobind

// 例子 10-2

class Toggle extends React.Component {

  @autobind
  handleClick() {
    console.log(this)
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        button
      </button>
    );
  }
}

autobind 的实现可以参考 《ES6 系列之我们来聊聊装饰器》

3. debounce

// 例子 10-3

class Toggle extends React.Component {

  @debounce(500, true)
  handleClick() {
    console.log('toggle')
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        button
      </button>
    );
  }
}

debounce 的实现可以参考 《ES6 系列之我们来聊聊装饰器》

4. React 与 Redux

// 例子 10-4

// good
class MyReactComponent extends React.Component {}

export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

// better
@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {};

11. 函数

1. 默认值

// 例子 11-1

// bad
function test(quantity) {
  const q = quantity || 1;
}

// good
function test(quantity = 1) {
  ...
}
// 例子 11-2

doSomething({ foo: 'Hello', bar: 'Hey!', baz: 42 });

// bad
function doSomething(config) {
  const foo = config.foo !== undefined ? config.foo : 'Hi';
  const bar = config.bar !== undefined ? config.bar : 'Yo!';
  const baz = config.baz !== undefined ? config.baz : 13;
}

// good
function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 }) {
  ...
}

// better
function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 } = {}) {
  ...
}
// 例子 11-3

// bad
const Button = ({className}) => {
	const classname = className || 'default-size';
	return <span className={classname}></span>
};

// good
const Button = ({className = 'default-size'}) => (
	<span className={classname}></span>
);

// better
const Button = ({className}) =>
	<span className={className}></span>
}

Button.defaultProps = {
	className: 'default-size'
}
// 例子 11-4

const required = () => {throw new Error('Missing parameter')};

const add = (a = required(), b = required()) => a + b;

add(1, 2) // 3
add(1); // Error: Missing parameter.

12. 拓展运算符

1. arguments 转数组

// 例子 12-1

// bad
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// good
const sortNumbers = (...numbers) => numbers.sort();

2. 调用参数

// 例子 12-2

// bad
Math.max.apply(null, [14, 3, 77])

// good
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);

3. 构建对象

剔除部分属性,将剩下的属性构建一个新的对象

// 例子 12-3
let [a, b, ...arr] = [1, 2, 3, 4, 5];

const { a, b, ...others } = { a: 1, b: 2, c: 3, d: 4, e: 5 };

有条件的构建对象

// 例子 12-4

// bad
function pick(data) {
  const { id, name, age} = data

  const res = { guid: id }

  if (name) {
    res.name = name
  }
  else if (age) {
    res.age = age
  }

  return res
}

// good
function pick({id, name, age}) {
  return {
    guid: id,
    ...(name && {name}),
    ...(age && {age})
  }
}

合并对象

// 例子 12-5

let obj1 = { a: 1, b: 2,c: 3 }
let obj2 = { b: 4, c: 5, d: 6}
let merged = {...obj1, ...obj2};

4. React

将对象全部传入组件

// 例子 12-6

const parmas =  {value1: 1, value2: 2, value3: 3}

<Test {...parmas} />

13. 双冒号运算符

// 例子 13-1

foo::bar;
// 等同于
bar.bind(foo);

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。

// 例子 13-2

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

let log = ::console.log;
// 等同于
var log = console.log.bind(console);

14. 解构赋值

1. 对象的基本解构

// 例子 14-1

componentWillReceiveProps(newProps) {
	this.setState({
		active: newProps.active
	})
}

componentWillReceiveProps({active}) {
	this.setState({active})
}
// 例子 14-2

// bad
handleEvent = () => {
  this.setState({
    data: this.state.data.set("key", "value")
  })
};

// good
handleEvent = () => {
  this.setState(({data}) => ({
    data: data.set("key", "value")
  }))
};
// 例子 14-3

Promise.all([Promise.resolve(1), Promise.resolve(2)])
.then(([x, y]) => {
    console.log(x, y);
});

2. 对象深度解构

// 例子 14-4

// bad
function test(fruit) {
  if (fruit && fruit.name)  {
    console.log (fruit.name);
  } else {
    console.log('unknown');
  }
}

// good
function test({name} = {}) {
  console.log (name || 'unknown');
}
// 例子 14-5

let obj = {
    a: {
      b: {
        c: 1
      }
    }
};

const {a: {b: {c = ''} = ''} = ''} = obj;

3. 数组解构

// 例子 14-6

// bad
const splitLocale = locale.split("-");
const language = splitLocale[0];
const country = splitLocale[1];

// good
const [language, country] = locale.split('-');

4. 变量重命名

// 例子 14-8

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); // "aaa"

5. 仅获取部分属性

// 例子 14-9

function test(input) {
  return [left, right, top, bottom];
}
const [left, __, top] = test(input);

function test(input) {
  return { left, right, top, bottom };
}
const { left, right } = test(input);

15. 增强的对象字面量

// 例子 15-1

// bad
const something = 'y'
const x = {
  something: something
}

// good
const something = 'y'
const x = {
  something
};

动态属性

// 例子 15-2

const x = {
  ['a' + '_' + 'b']: 'z'
}

console.log(x.a_b); // z

16. 数组的拓展方法

1. keys

// 例子 16-1

var arr = ["a", , "c"];

var sparseKeys = Object.keys(arr);
console.log(sparseKeys); // ['0', '2']

var denseKeys = [...arr.keys()];
console.log(denseKeys);  // [0, 1, 2]

2. entries

// 例子 16-2

var arr = ["a", "b", "c"];
var iterator = arr.entries();

for (let e of iterator) {
    console.log(e);
}

3. values

// 例子 16-3

let arr = ['w', 'y', 'k', 'o', 'p'];
let eArr = arr.values();

for (let letter of eArr) {
  console.log(letter);
}

4. includes

// 例子 16-4

// bad
function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red');
  }
}

// good
function test(fruit) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}

5. find

// 例子 16-5

var inventory = [
    {name: 'apples', quantity: 2},
    {name: 'bananas', quantity: 0},
    {name: 'cherries', quantity: 5}
];

function findCherries(fruit) {
    return fruit.name === 'cherries';
}

console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }

6. findIndex

// 例子 16-6

function isPrime(element, index, array) {
  var start = 2;
  while (start <= Math.sqrt(element)) {
    if (element % start++ < 1) {
      return false;
    }
  }
  return element > 1;
}

console.log([4, 6, 8, 12].findIndex(isPrime)); // -1, not found
console.log([4, 6, 7, 12].findIndex(isPrime)); // 2

更多的就不列举了。

17. optional-chaining

举个例子:

// 例子 17-1

const obj = {
  foo: {
    bar: {
      baz: 42,
    },
  },
};

const baz = obj?.foo?.bar?.baz; // 42

同样支持函数:

// 例子 17-2

function test() {
  return 42;
}
test?.(); // 42

exists?.(); // undefined

需要添加 @babel/plugin-proposal-optional-chaining 插件支持

18. logical-assignment-operators

// 例子 18-1

a ||= b;

obj.a.b ||= c;

a &&= b;

obj.a.b &&= c;

Babel 编译为:

var _obj$a, _obj$a2;

a || (a = b);

(_obj$a = obj.a).b || (_obj$a.b = c);

a && (a = b);

(_obj$a2 = obj.a).b && (_obj$a2.b = c);

出现的原因:

// 例子 18-2

function example(a = b) {
  // a 必须是 undefined
  if (!a) {
    a = b;
  }
}

function numeric(a = b) {
  // a 必须是 null 或者 undefined
  if (a == null) {
    a = b;
  }
}

// a 可以是任何 falsy 的值
function example(a = b) {
  // 可以,但是一定会触发 setter
  a = a || b;

  // 不会触发 setter,但可能会导致 lint error
  a || (a = b);

  // 就有人提出了这种写法:
  a ||= b;
}

需要 @babel/plugin-proposal-logical-assignment-operators 插件支持

19. nullish-coalescing-operator

a ?? b

// 相当于

(a !== null && a !== void 0) ? a : b

举个例子:

var foo = object.foo ?? "default";

// 相当于

var foo = (object.foo != null) ? object.foo : "default";

需要 @babel/plugin-proposal-nullish-coalescing-operator 插件支持

20. pipeline-operator

const double = (n) => n * 2;
const increment = (n) => n + 1;

// 没有用管道操作符
double(increment(double(5))); // 22

// 用上管道操作符之后
5 |> double |> increment |> double; // 22

21. Module 模块(import/export)

Imports

import 'helpers'
// aka: require('···')

import Express from 'express'
// aka: const Express = require('···').default || require('···')

import { indent } from 'helpers'
// aka: const indent = require('···').indent

import * as Helpers from 'helpers'
// aka: const Helpers = require('···')

import { indentSpaces as indent } from 'helpers'
// aka: const indent = require('···').indentSpaces

Exports

export default function () { ··· }
// aka: module.exports.default = ···

export function mymethod () { ··· }
// aka: module.exports.mymethod = ···

export const pi = 3.14159
// aka: module.exports.pi = ···

export { default as MyComponent } from './my-component'
//

Demo

// A.js
export default 20
// use
import Num from './A'


// A.js
export const A = 20
// use
import { A } from './A'


// A.js
export default 20
export const name = 'Jhon'
export const gender = 'male'
// use
import A, { name, gender } from './A'
import A, { name as MyName, gender as MyGender } from './A'
import * as User from './A'


// A.js, B.js, C.js
// index.js
export { default as A } from './A'
export { default as B } from './B'
export { default as C } from './C'

其他

新开了 知乎专栏,大家可以在更多的平台上看到我的文章,欢迎关注哦~

参考

  1. ES6 实践规范
  2. babel 7 简单升级指南
  3. 不得不知的 ES6 小技巧
  4. 深入解析 ES6:Symbol
  5. 什么时候你不能使用箭头函数?
  6. 一些使 JavaScript 更加简洁的小技巧
  7. 几分钟内提升技能的 8 个 JavaScript 方法
  8. [译] 如何使用 JavaScript ES6 有条件地构造对象
  9. 5 个技巧让你更好的编写 JavaScript(ES6) 中条件语句
  10. ES6 带来的重大特性 – JavaScript 完全手册(2018版)

ES6 系列

ES6 系列目录地址:https://github.com/mqyqingfeng/Blog

@Marinerer Marinerer changed the title 【ES6/7/8】知识点 【ES6/7/8】知识 May 29, 2018
@Marinerer
Copy link
Owner Author

《深入理解ES6》教程学习笔记

转自:https://github.com/hyy1115/ES6-learning


发现问题欢迎pull commit

使用ES6开发已经有1年多了,以前看的是阮一峰老师的ES6教程,也看过MDN文档的ES6语法介绍。

最近买了《深入理解ES6》的书籍来看,为什么学习ES6这么久还要买这本书呢?主要是看到Daniel Abramove(react核心团队成员及redux的创造者)为本书做了序,作为一个react粉丝,还是挺看好这本书能给我带来一个新的升华,而且本书的作者也非常厉害。感谢 @lenville 的翻译贡献。

本章作为导读,用来汇总所有章节,每一章的内容也是我的学习笔记,也会加上自己对ES6新功能的理解分析,希望对前端爱好者也有帮助。

全剧终

@Marinerer Marinerer changed the title 【ES6/7/8】知识 【ES6/7/8】学习整理 May 29, 2018
@Marinerer
Copy link
Owner Author

.

2 similar comments
@Marinerer
Copy link
Owner Author

.

@Marinerer
Copy link
Owner Author

.

@Marinerer
Copy link
Owner Author

@Marinerer
Copy link
Owner Author

@Marinerer
Copy link
Owner Author

Marinerer commented Jul 6, 2018

@Marinerer
Copy link
Owner Author

ES6、ES7、ES8、ES9、ES10新特性一览

源自:https://juejin.im/post/5ca2e1935188254416288eb2

ES全称ECMAScript,ECMAScript是ECMA制定的标准化脚本语言。目前JavaScript使用的ECMAScript版本为ECMA-417。关于ECMA的最新资讯可以浏览 ECMA news查看。

ECMA规范最终由TC39敲定。TC39由包括浏览器厂商在内的各方组成,他们开会推动JavaScript提案沿着一条严格的发展道路前进。 从提案到入选ECMA规范主要有以下几个阶段:

  • Stage 0: strawman——最初想法的提交。
  • Stage 1: proposal(提案)——由TC39至少一名成员倡导的正式提案文件,该文件包括API事例。
  • Stage 2: draft(草案)——功能规范的初始版本,该版本包含功能规范的两个实验实现。
  • Stage 3: candidate(候选)——提案规范通过审查并从厂商那里收集反馈
  • Stage 4: finished(完成)——提案准备加入ECMAScript,但是到浏览器或者Nodejs中可能需要更长的时间。

ES6新特性(2015)

ES6的特性比较多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度很大,所以ES6中的特性比较多。 在这里列举几个常用的:

  • 模块化
  • 箭头函数
  • 函数参数默认值
  • 模板字符串
  • 解构赋值
  • 延展操作符
  • 对象属性简写
  • Promise
  • Let与Const

1.类(class)

对熟悉Java,object-c,c#等纯面向对象语言的开发者来说,都会对class有一种特殊的情怀。ES6 引入了class(类),让JavaScript的面向对象编程变得更加简单和易于理解。

  class Animal {
    // 构造函数,实例化的时候将会被调用,如果不指定,那么会有一个不带参数的默认构造函数.
    constructor(name,color) {
      this.name = name;
      this.color = color;
    }
    // toString 是原型对象上的属性
    toString() {
      console.log('name:' + this.name + ',color:' + this.color);

    }
  }

 var animal = new Animal('dog','white');//实例化Animal
 animal.toString();

 console.log(animal.hasOwnProperty('name')); //true
 console.log(animal.hasOwnProperty('toString')); // false
 console.log(animal.__proto__.hasOwnProperty('toString')); // true

 class Cat extends Animal {
  constructor(action) {
    // 子类必须要在constructor中指定super 函数,否则在新建实例的时候会报错.
    // 如果没有置顶consructor,默认带super函数的constructor将会被添加、
    super('cat','white');
    this.action = action;
  }
  toString() {
    console.log(super.toString());
  }
 }

 var cat = new Cat('catch')
 cat.toString();

 // 实例cat 是 Cat 和 Animal 的实例,和Es5完全一致。
 console.log(cat instanceof Cat); // true
 console.log(cat instanceof Animal); // true

复制代码

2.模块化(Module)

ES5不支持原生的模块化,在ES6中模块作为重要的组成部分被添加进来。模块的功能主要由 export 和 import 组成。每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过import来引用其它模块提供的接口。同时还为模块创造了命名空间,防止函数的命名冲突。

导出(export)

ES6允许在一个模块中使用export来导出多个变量或函数。

导出变量

//test.js
export var name = 'Rainbow'

复制代码

心得:ES6不仅支持变量的导出,也支持常量的导出。 export const sqrt = Math.sqrt;//导出常量

ES6将一个文件视为一个模块,上面的模块通过 export 向外输出了一个变量。一个模块也可以同时往外面输出多个变量。

 //test.js
 var name = 'Rainbow';
 var age = '24';
 export {name, age};

复制代码

导出函数

// myModule.js
export function myModule(someArg) {
  return someArg;
}  

复制代码

导入(import)

定义好模块的输出以后就可以在另外一个模块通过import引用。

import {myModule} from 'myModule';// main.js
import {name,age} from 'test';// test.js

复制代码

心得:一条import 语句可以同时导入默认函数和其它变量。import defaultMethod, { otherMethod } from 'xxx.js';

3.箭头(Arrow)函数

这是ES6中最令人激动的特性之一。=>不只是关键字function的简写,它还带来了其它好处。箭头函数与包围它的代码共享同一个this,能帮你很好的解决this的指向问题。有经验的JavaScript开发者都熟悉诸如var self = this;var that = this这种引用外围this的模式。但借助=>,就不需要这种模式了。

箭头函数的结构

箭头函数的箭头=>之前是一个空括号、单个的参数名、或用括号括起的多个参数名,而箭头之后可以是一个表达式(作为函数的返回值),或者是用花括号括起的函数体(需要自行通过return来返回值,否则返回的是undefined)。

// 箭头函数的例子
()=>1
v=>v+1
(a,b)=>a+b
()=>{
    alert("foo");
}
e=>{
    if (e == 0){
        return 0;
    }
    return 1000/e;
}

复制代码

心得:不论是箭头函数还是bind,每次被执行都返回的是一个新的函数引用,因此如果你还需要函数的引用去做一些别的事情(譬如卸载监听器),那么你必须自己保存这个引用。

卸载监听器时的陷阱

错误的做法

class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));
    }
    onAppPaused(event){
    }
}

复制代码

正确的做法

class PauseMenu extends React.Component{
    constructor(props){
        super(props);
        this._onAppPaused = this.onAppPaused.bind(this);
    }
    componentWillMount(){
        AppStateIOS.addEventListener('change', this._onAppPaused);
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this._onAppPaused);
    }
    onAppPaused(event){
    }
}

复制代码

除上述的做法外,我们还可以这样做:

class PauseMenu extends React.Component{
    componentWillMount(){
        AppStateIOS.addEventListener('change', this.onAppPaused);
    }
    componentWillUnmount(){
        AppStateIOS.removeEventListener('change', this.onAppPaused);
    }
    onAppPaused = (event) => {
        //把函数直接作为一个arrow function的属性来定义,初始化的时候就绑定好了this指针
    }
}

复制代码

需要注意的是:不论是bind还是箭头函数,每次被执行都返回的是一个新的函数引用,因此如果你还需要函数的引用去做一些别的事情(譬如卸载监听器),那么你必须自己保存这个引用。

4.函数参数默认值

ES6支持在定义函数的时候为其设置默认值:

function foo(height = 50, color = 'red')
{
    // ...
}

复制代码

不使用默认值:

function foo(height, color)
{
    var height = height || 50;
    var color = color || 'red';
    //...
}

复制代码

这样写一般没问题,但当参数的布尔值为false时,就会有问题了。比如,我们这样调用foo函数:

foo(0, "")

复制代码

因为0的布尔值为false,这样height的取值将是50。同理color的取值为‘red’。

所以说,函数参数默认值不仅能是代码变得更加简洁而且能规避一些问题。

5.模板字符串

ES6支持模板字符串,使得字符串的拼接更加的简洁、直观。

不使用模板字符串:

var name = 'Your name is ' + first + ' ' + last + '.'

复制代码

使用模板字符串:

var name = `Your name is ${first} ${last}.`

复制代码

在ES6中通过${}就可以完成字符串的拼接,只需要将变量放在大括号之中。

6.解构赋值

解构赋值语法是JavaScript的一种表达式,可以方便的从数组或者对象中快速提取值赋给定义的变量。

获取数组中的值

从数组中获取值并赋值到变量中,变量的顺序与数组中对象顺序对应。

var foo = ["one", "two", "three", "four"];

var [one, two, three] = foo;
console.log(one); // "one"
console.log(two); // "two"
console.log(three); // "three"

//如果你要忽略某些值,你可以按照下面的写法获取你想要的值
var [first, , , last] = foo;
console.log(first); // "one"
console.log(last); // "four"

//你也可以这样写
var a, b; //先声明变量

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

复制代码

如果没有从数组中的获取到值,你可以为变量设置一个默认值。

var a, b;

[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7

复制代码

通过解构赋值可以方便的交换两个变量的值。

var a = 1;
var b = 3;

[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1


复制代码

获取对象中的值

const student = {
  name:'Ming',
  age:'18',
  city:'Shanghai'  
};

const {name,age,city} = student;
console.log(name); // "Ming"
console.log(age); // "18"
console.log(city); // "Shanghai"

复制代码

7.延展操作符(Spread operator)

延展操作符...可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造对象时, 将对象表达式按key-value的方式展开。

语法

函数调用:

myFunction(...iterableObj);

复制代码

数组构造或字符串:

[...iterableObj, '4', ...'hello', 6];

复制代码

构造对象时,进行克隆或者属性拷贝(ECMAScript 2018规范新增特性):

let objClone = { ...obj };

复制代码

应用场景

在函数调用时使用延展操作符

function sum(x, y, z) {
  return x + y + z;
}
const numbers = [1, 2, 3];

//不使用延展操作符
console.log(sum.apply(null, numbers));

//使用延展操作符
console.log(sum(...numbers));// 6

复制代码

构造数组

没有展开语法的时候,只能组合使用 push,splice,concat 等方法,来将已有数组元素变成新数组的一部分。有了展开语法, 构造新数组会变得更简单、更优雅:

const stuendts = ['Jine','Tom']; 
const persons = ['Tony',... stuendts,'Aaron','Anna'];
conslog.log(persions)// ["Tony", "Jine", "Tom", "Aaron", "Anna"]

复制代码

和参数列表的展开类似, ... 在构造字数组时, 可以在任意位置多次使用。

数组拷贝

var arr = [1, 2, 3];
var arr2 = [...arr]; // 等同于 arr.slice()
arr2.push(4); 
console.log(arr2)//[1, 2, 3, 4]

复制代码

展开语法和 Object.assign() 行为一致, 执行的都是浅拷贝(只遍历一层)。

连接多个数组

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
var arr3 = [...arr1, ...arr2];// 将 arr2 中所有元素附加到 arr1 后面并返回
//等同于
var arr4 = arr1.concat(arr2);

复制代码

在ECMAScript 2018中延展操作符增加了对对象的支持

var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };
// 克隆后的对象: { foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }

​```javascript

#### 在React中的应用

通常我们在封装一个组件时,会对外公开一些 props 用于实现功能。大部分情况下在外部使用都应显示的传递 props 。但是当传递大量的props时,会非常繁琐,这时我们可以使用 `...(延展操作符,用于取出参数对象的所有可遍历属性)` 来进行传递。

#### 一般情况下我们应该这样写

​```javascript
<CustomComponent name ='Jine' age ={21} />

复制代码

使用 ... ,等同于上面的写法

const params = {
	name: 'Jine',
	age: 21
}
<CustomComponent {...params} />
复制代码

配合解构赋值避免传入一些不需要的参数

var params = {
	name: '123',
	title: '456',
	type: 'aaa'
}

var { type, ...other } = params;

<CustomComponent type='normal' number={2} {...other} />
//等同于
<CustomComponent type='normal' number={2} name='123' title='456' />

复制代码

8.对象属性简写

在ES6中允许我们在设置一个对象的属性的时候不指定属性名。

不使用ES6

const name='Ming',age='18',city='Shanghai';
   
const student = {
    name:name,
    age:age,
    city:city
};
console.log(student);//{name: "Ming", age: "18", city: "Shanghai"}

复制代码

对象中必须包含属性和值,显得非常冗余。

使用ES6

const name='Ming',age='18',city='Shanghai';
  
const student = {
    name,
    age,
    city
};
console.log(student);//{name: "Ming", age: "18", city: "Shanghai"}

复制代码

对象中直接写变量,非常简洁。

9.Promise

Promise 是异步编程的一种解决方案,比传统的解决方案callback更加的优雅。它最早由社区提出和实现的,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

不使用ES6

嵌套两个setTimeout回调函数:

setTimeout(function()
{
    console.log('Hello'); // 1秒后输出"Hello"
    setTimeout(function()
    {
        console.log('Hi'); // 2秒后输出"Hi"
    }, 1000);
}, 1000);

复制代码

使用ES6

var waitSecond = new Promise(function(resolve, reject)
{
    setTimeout(resolve, 1000);
});

waitSecond
    .then(function()
    {
      console.log("Hello"); // 1秒后输出"Hello"
      return waitSecond;
    })
    .then(function()
    {
        console.log("Hi"); // 2秒后输出"Hi"
    });

复制代码

上面的的代码使用两个then来进行异步编程串行化,避免了回调地狱:

10.支持let与const

在之前JS是没有块级作用域的,const与let填补了这方便的空白,const与let都是块级作用域。

使用var定义的变量为函数级作用域:

{
  var a = 10;
}

console.log(a); // 输出10

复制代码

使用let与const定义的变量为块级作用域:

{
  let a = 10;
}

console.log(a); //-1 or Error“ReferenceError: a is not defined”
复制代码

ES7新特性(2016)

ES2016添加了两个小的特性来说明标准化过程:

  • 数组includes()方法,用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。
  • a ** b指数运算符,它与 Math.pow(a, b)相同。

1.Array.prototype.includes()

includes() 函数用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回false

includes 函数与 indexOf 函数很相似,下面两个表达式是等价的:

arr.includes(x)
arr.indexOf(x) >= 0

复制代码

接下来我们来判断数字中是否包含某个元素:

在ES7之前的做法

使用indexOf()验证数组中是否存在某个元素,这时需要根据返回值是否为-1来判断:

let arr = ['react', 'angular', 'vue'];

if (arr.indexOf('react') !== -1)
{
    console.log('react存在');
}

复制代码

使用ES7的includes()

使用includes()验证数组中是否存在某个元素,这样更加直观简单:

let arr = ['react', 'angular', 'vue'];

if (arr.includes('react'))
{
    console.log('react存在');
}
复制代码

2.指数操作符

在ES7中引入了指数运算符****具有与Math.pow(..)等效的计算结果。

不使用指数操作符

使用自定义的递归函数calculateExponent或者Math.pow()进行指数运算:

function calculateExponent(base, exponent)
{
    if (exponent === 1)
    {
        return base;
    }
    else
    {
        return base * calculateExponent(base, exponent - 1);
    }
}

console.log(calculateExponent(2, 10)); // 输出1024
console.log(Math.pow(2, 10)); // 输出1024
复制代码

使用指数操作符

使用指数运算符**,就像+、-等操作符一样:

console.log(2**10);// 输出1024
复制代码

ES8新特性(2017)

  • async/await
  • Object.values()
  • Object.entries()
  • String padding: padStart()padEnd(),填充字符串达到当前长度
  • 函数参数列表结尾允许逗号
  • Object.getOwnPropertyDescriptors()
  • ShareArrayBufferAtomics对象,用于从共享内存位置读取和写入

1.async/await

ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。例如:

async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}
复制代码

2.Object.values()

Object.values()是一个与Object.keys()类似的新函数,但返回的是Object自身属性的所有值,不包括继承的值。

假设我们要遍历如下对象obj的所有值:

const obj = {a: 1, b: 2, c: 3};
复制代码

不使用Object.values() :ES7

const vals=Object.keys(obj).map(key=>obj[key]);
console.log(vals);//[1, 2, 3]
复制代码

使用Object.values() :ES8

const values=Object.values(obj1);
console.log(values);//[1, 2, 3]
复制代码

从上述代码中可以看出Object.values()为我们省去了遍历key,并根据这些key获取value的步骤。

3.Object.entries()

Object.entries()函数返回一个给定对象自身可枚举属性的键值对的数组。

接下来我们来遍历上文中的obj对象的所有属性的key和value:

不使用Object.entries() :ES7

Object.keys(obj).forEach(key=>{
	console.log('key:'+key+' value:'+obj[key]);
})
//key:a value:1
//key:b value:2
//key:c value:3
复制代码

使用Object.entries() :ES8

for(let [key,value] of Object.entries(obj1)){
	console.log(`key: ${key} value:${value}`)
}
//key:a value:1
//key:b value:2
//key:c value:3

复制代码

4.String padding

在ES8中String新增了两个实例函数String.prototype.padStartString.prototype.padEnd,允许将空字符串或其他字符串添加到原始字符串的开头或结尾。

String.padStart(targetLength,[padString])

  • targetLength:当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
  • padString:(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为 " "。
console.log('0.0'.padStart(4,'10')) //10.0
console.log('0.0'.padStart(20))// 0.00    
复制代码

String.padEnd(targetLength,padString])

  • targetLength:当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
  • padString:(可选) 填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为 " ";
console.log('0.0'.padEnd(4,'0')) //0.00    
console.log('0.0'.padEnd(10,'0'))//0.00000000
复制代码

5.函数参数列表结尾允许逗号

主要作用是方便使用git进行多人协作开发时修改同一个函数减少不必要的行变更。

6.Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors()函数用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

函数原型:

Object.getOwnPropertyDescriptors(obj)
复制代码

返回obj对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

const obj2 = {
	name: 'Jine',
	get age() { return '18' }
};
Object.getOwnPropertyDescriptors(obj2)
// {
//   age: {
//     configurable: true,
//     enumerable: true,
//     get: function age(){}, //the getter function
//     set: undefined
//   },
//   name: {
//     configurable: true,
//     enumerable: true,
//		value:"Jine",
//		writable:true
//   }
// }
复制代码

7.SharedArrayBuffer对象

SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。

/**
 * 
 * @param {*} length 所创建的数组缓冲区的大小,以字节(byte)为单位。  
 * @returns {SharedArrayBuffer} 一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
 */
new SharedArrayBuffer(length)
复制代码

8.Atomics对象

Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。

这些原子操作属于 Atomics 模块。与一般的全局对象不同,Atomics 不是构造函数,因此不能使用 new 操作符调用,也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的(与 Math 对象一样)。

多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的,即下一个原子操作一定会在上一个原子操作结束后才会开始,其操作过程不会中断。

  • Atomics.add()

将指定位置上的数组元素与给定的值相加,并返回相加前该元素的值。

  • Atomics.and()

将指定位置上的数组元素与给定的值相与,并返回与操作前该元素的值。

  • Atomics.compareExchange()

如果数组中指定的元素与给定的值相等,则将其更新为新的值,并返回该元素原先的值。

  • Atomics.exchange()

将数组中指定的元素更新为给定的值,并返回该元素更新前的值。

  • Atomics.load()

返回数组中指定元素的值。

  • Atomics.or()

将指定位置上的数组元素与给定的值相或,并返回或操作前该元素的值。

  • Atomics.store()

将数组中指定的元素设置为给定的值,并返回该值。

  • Atomics.sub()

将指定位置上的数组元素与给定的值相减,并返回相减前该元素的值。

  • Atomics.xor()

将指定位置上的数组元素与给定的值相异或,并返回异或操作前该元素的值。

wait() 和 wake() 方法采用的是 Linux 上的 futexes 模型(fast user-space mutex,快速用户空间互斥量),可以让进程一直等待直到某个特定的条件为真,主要用于实现阻塞。

  • Atomics.wait()

检测数组中某个指定位置上的值是否仍然是给定值,是则保持挂起直到被唤醒或超时。返回值为 "ok"、"not-equal" 或 "time-out"。调用时,如果当前线程不允许阻塞,则会抛出异常(大多数浏览器都不允许在主线程中调用 wait())。

  • Atomics.wake()

唤醒等待队列中正在数组指定位置的元素上等待的线程。返回值为成功唤醒的线程数量。

  • Atomics.isLockFree(size)

可以用来检测当前系统是否支持硬件级的原子操作。对于指定大小的数组,如果当前系统支持硬件级的原子操作,则返回 true;否则就意味着对于该数组,Atomics 对象中的各原子操作都只能用锁来实现。此函数面向的是技术专家。-->

ES9新特性(2018)

1.异步迭代

async/await的某些时刻,你可能尝试在同步循环中调用异步函数。例如:

async function process(array) {
  for (let i of array) {
    await doSomething(i);
  }
}
复制代码

这段代码不会正常运行,下面这段同样也不会:

async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}
复制代码

这段代码中,循环本身依旧保持同步,并在在内部异步函数之前全部调用完成。

ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。例如:

async function process(array) {
  for await (let i of array) {
    doSomething(i);
  }
}
复制代码

2.Promise.finally()

一个Promise调用链要么成功到达最后一个.then(),要么失败触发.catch()。在某些情况下,你想要在无论Promise运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。

.finally()允许你指定最终的逻辑:

function doSomething() {
  doSomething1()
  .then(doSomething2)
  .then(doSomething3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    // finish here!
  });
}
复制代码

3.Rest/Spread 属性

ES2015引入了Rest参数扩展运算符。三个点(...)仅用于数组。Rest参数语法允许我们将一个布丁数量的参数表示为一个数组。

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}
复制代码

展开操作符以相反的方式工作,将数组转换成可传递给函数的单独参数。例如Math.max()返回给定数字中的最大值:

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100
复制代码

ES2018为对象解构提供了和数组一样的Rest参数()和展开操作符,一个简单的例子:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};

const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }
复制代码

或者你可以使用它给函数传递参数:

restParam({
  a: 1,
  b: 2,
  c: 3
});

function restParam({ a, ...x }) {
  // a = 1
  // x = { b: 2, c: 3 }
}
复制代码

跟数组一样,Rest参数只能在声明的结尾处使用。此外,它只适用于每个对象的顶层,如果对象中嵌套对象则无法适用。

扩展运算符可以在其他对象内使用,例如:

const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { ...obj1, z: 26 };
// obj2 is { a: 1, b: 2, c: 3, z: 26 }
复制代码

可以使用扩展运算符拷贝一个对象,像是这样obj2 = {...obj1},但是 这只是一个对象的浅拷贝。另外,如果一个对象A的属性是对象B,那么在克隆后的对象cloneB中,该属性指向对象B。

4.正则表达式命名捕获组

JavaScript正则表达式可以返回一个匹配的对象——一个包含匹配字符串的类数组,例如:以YYYY-MM-DD的格式解析日期:

const
  reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/,
  match  = reDate.exec('2018-04-30'),
  year   = match[1], // 2018
  month  = match[2], // 04
  day    = match[3]; // 30
复制代码

这样的代码很难读懂,并且改变正则表达式的结构有可能改变匹配对象的索引。

ES2018允许命名捕获组使用符号?<name>,在打开捕获括号(后立即命名,示例如下:

const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  match  = reDate.exec('2018-04-30'),
  year   = match.groups.year,  // 2018
  month  = match.groups.month, // 04
  day    = match.groups.day;   // 30
复制代码

任何匹配失败的命名组都将返回undefined

命名捕获也可以使用在replace()方法中。例如将日期转换为美国的 MM-DD-YYYY 格式:

const
  reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
  d      = '2018-04-30',
  usDate = d.replace(reDate, '$<month>-$<day>-$<year>');
复制代码

5.正则表达式反向断言

目前JavaScript在正则表达式中支持先行断言(lookahead)。这意味着匹配会发生,但不会有任何捕获,并且断言没有包含在整个匹配字段中。例如从价格中捕获货币符号:

const
  reLookahead = /\D(?=\d+)/,
  match       = reLookahead.exec('$123.89');

console.log( match[0] ); // $
复制代码

ES2018引入以相同方式工作但是匹配前面的反向断言(lookbehind),这样我就可以忽略货币符号,单纯的捕获价格的数字:

const
  reLookbehind = /(?<=\D)\d+/,
  match        = reLookbehind.exec('$123.89');

console.log( match[0] ); // 123.89
复制代码

以上是 肯定反向断言,非数字\D必须存在。同样的,还存在 否定反向断言,表示一个值必须不存在,例如:

const
  reLookbehindNeg = /(?<!\D)\d+/,
  match           = reLookbehind.exec('$123.89');

console.log( match[0] ); // null
复制代码

6.正则表达式dotAll模式

正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现,例如:

/hello.world/.test('hello\nworld');  // false
/hello.world/s.test('hello\nworld'); // true
复制代码

7.正则表达式 Unicode 转义

到目前为止,在正则表达式中本地访问 Unicode 字符属性是不被允许的。ES2018添加了 Unicode 属性转义——形式为\p{...}\P{...},在正则表达式中使用标记 u (unicode) 设置,在\p块儿内,可以以键值对的方式设置需要匹配的属性而非具体内容。例如:

const reGreekSymbol = /\p{Script=Greek}/u;
reGreekSymbol.test('π'); // true
复制代码

此特性可以避免使用特定 Unicode 区间来进行内容类型判断,提升可读性和可维护性。

8.非转义序列的模板字符串

之前,\u开始一个 unicode 转义,\x开始一个十六进制转义,\后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:\uuu\xxx\111。更多细节参考模板字符串

ES10新特性(2019)

  • 行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
  • 更加友好的 JSON.stringify
  • 新增了Array的flat()方法和flatMap()方法
  • 新增了String的trimStart()方法和trimEnd()方法
  • Object.fromEntries()
  • Symbol.prototype.description
  • String.prototype.matchAll
  • Function.prototype.toString()现在返回精确字符,包括空格和注释
  • 简化try {} catch {},修改 catch 绑定
  • 新的基本数据类型BigInt
  • globalThis
  • import()
  • Legacy RegEx
  • 私有的实例方法和访问器

1.行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配

以前,这些符号在字符串文字中被视为行终止符,因此使用它们会导致SyntaxError异常。

2.更加友好的 JSON.stringify

如果输入 Unicode 格式但是超出范围的字符,在原先JSON.stringify返回格式错误的Unicode字符串。现在实现了一个改变JSON.stringify的第3阶段提案,因此它为其输出转义序列,使其成为有效Unicode(并以UTF-8表示)

3.新增了Array的flat()方法和flatMap()方法

flat()flatMap()本质上就是是归纳(reduce) 与 合并(concat)的操作。

Array.prototype.flat()

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

  • flat()方法最基本的作用就是数组降维
var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity 作为深度,展开任意深度的嵌套数组
arr3.flat(Infinity); 
// [1, 2, 3, 4, 5, 6]
复制代码
  • 其次,还可以利用flat()方法的特性来去除数组的空项
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]
复制代码

Array.prototype.flatMap()

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。 这里我们拿map方法与flatMap方法做一个比较。

var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]); 
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]
复制代码

4.新增了String的trimStart()方法和trimEnd()方法

新增的这两个方法很好理解,分别去除字符串首尾空白字符,这里就不用例子说声明了。

5.Object.fromEntries()

Object.entries()方法的作用是返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。

而Object.fromEntries() 则是 Object.entries() 的反转。

Object.fromEntries() 函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现@iterator方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。

  • 通过 Object.fromEntries, 可以将 Map 转化为 Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
复制代码
  • 通过 Object.fromEntries, 可以将 Array 转化为 Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
复制代码

6.Symbol.prototype.description

通过工厂函数Symbol()创建符号时,您可以选择通过参数提供字符串作为描述:

const sym = Symbol('The description');
复制代码

以前,访问描述的唯一方法是将符号转换为字符串:

assert.equal(String(sym), 'Symbol(The description)');
复制代码

现在引入了getter Symbol.prototype.description以直接访问描述:

assert.equal(sym.description, 'The description');
复制代码

7.String.prototype.matchAll

matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。 在 matchAll 出现之前,通过在循环中调用regexp.exec来获取所有匹配项信息(regexp需使用/g标志:

const regexp = RegExp('foo*','g');
const str = 'table football, foosball';

while ((matches = regexp.exec(str)) !== null) {
  console.log(`Found ${matches[0]}. Next starts at ${regexp.lastIndex}.`);
  // expected output: "Found foo. Next starts at 9."
  // expected output: "Found foo. Next starts at 19."
}
复制代码

如果使用matchAll ,就可以不必使用while循环加exec方式(且正则表达式需使用/g标志)。使用matchAll 会得到一个迭代器的返回值,配合 for...of, array spread, or Array.from() 可以更方便实现功能:

const regexp = RegExp('foo*','g'); 
const str = 'table football, foosball';
let matches = str.matchAll(regexp);

for (const match of matches) {
  console.log(match);
}
// Array [ "foo" ]
// Array [ "foo" ]

// matches iterator is exhausted after the for..of iteration
// Call matchAll again to create a new iterator
matches = str.matchAll(regexp);

Array.from(matches, m => m[0]);
// Array [ "foo", "foo" ]
复制代码

matchAll可以更好的用于分组

var regexp = /t(e)(st(\d?))/g;
var str = 'test1test2';

str.match(regexp); 
// Array ['test1', 'test2']
复制代码
let array = [...str.matchAll(regexp)];

array[0];
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
array[1];
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]
复制代码

8.Function.prototype.toString()现在返回精确字符,包括空格和注释

function /* comment */ foo /* another comment */() {}

// 之前不会打印注释部分
console.log(foo.toString()); // function foo(){}

// ES2019 会把注释一同打印
console.log(foo.toString()); // function /* comment */ foo /* another comment */ (){}

// 箭头函数
const bar /* comment */ = /* another comment */ () => {};

console.log(bar.toString()); // () => {}
复制代码

9.修改 catch 绑定

在 ES10 之前,我们必须通过语法为 catch 子句绑定异常变量,无论是否有必要。很多时候 catch 块是多余的。 ES10 提案使我们能够简单的把变量省略掉。

不算大的改动。

之前是

try {} catch(e) {}
复制代码

现在是

try {} catch {}
复制代码

10.新的基本数据类型BigInt

现在的基本数据类型(值类型)不止5种(ES6之后是六种)了哦!加上BigInt一共有七种基本数据类型,分别是: String、Number、Boolean、Null、Undefined、Symbol、BigInt

ES6、ES7、ES8学习指南

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant