ES6+ 笔记

140 阅读5分钟

有的内容是三四年前写的。

Module

chrome 已经支持 module,需要注意 script 标签需要加上 type="module"

<script type="module" src="2.js"></script>

export

使用示例:

export var firstName = 'Michael';

export function multiply(x, y) {
  return x * y;
};

export { last, year };

错误写法:

var m = 1;
export m;

import

示例代码:
import {firstName, lastName, year} from './profile.js';
import 'lodash';
只是将其内部的代码执行一遍,上面的例子也会执行module内部的代码。
module 内部定义的变量不会暴露出来。

模块整体加载:import * as circle from './circle';

export default

使用:

export default function() {
  console.log('lala')
}

import fn from './1.js'

一个module中只能有一个 export default。
如果将module整体加载,export default 的内容也是其中一项,key 是 default

和直接export不同之处:

正确:
var a = 1
export default a

错误:
export default var a = 1;

总结:export 后面需要声明性质的东西,export default 后面需要具体的值

export 与 import 的复合写法

import { foo, bar } from 'my_module';
export { foo, bar };
可以写成:
export { foo, bar } from 'my_module';

注意,这样写以后,foo 和 bar 不能在当前的 module 使用

其它例子:

export { foo as myFoo } from 'my_module';
export * from 'my_module';
export { default } from 'foo';

import()

待补充

一些提示

import 引入的变量都是常量。 如果一个HTML文件引用了两个JS文件,这两个JS文件都引用了同一个module,前者对引用到的变量的修改会影响到后者读取到的内容(如果引用的变量是对象,可以修改其属性)。

函数

函数参数的默认值是『惰性求值』的


看下面这个例子:

function foo({x, y = 5} = {}) {
  console.log(x, y);
}

foo() // undefined 5

函数参数的默认值是{ },如果函数调用提供了参数,则变量x和y通过解构赋值生成,解构赋值中y是有默认值的


错误的写法:

// 报错
function f(a, ...b, c) {
  // ...
}

因为rest参数只能是最后一个参数


箭头函数

特点:
没有 this,所以也不能作为构造函数,不能用call()、bind()等 没有 arguments 不能使用 yield 命令

箭头函数中的this对象,是定义时所在的对象,而不是使用时所在的对象,所以也是固定不变的
看下面的例子:

function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

数组

扩展运算符

将数组转为逗号分隔的参数序列
可以将有 iterator 接口的数据结构转为数组,如 Arguments、NodeList 等
主要用于函数调用,可以替代函数的 apply 方法

运用:

求一个数组的最大元素:
// ES5 的写法
Math.max.apply(null, [14, 3, 77])

// ES6 的写法
Math.max(...[14, 3, 77])


把一个数组的元素 push 进另一个数组
// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);

// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
复制数组:
// ES5
const a2 = a1.concat();
// ES6
const a2 = [...a1];

合并数组:
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// ES6 的合并数组
[...arr1, ...arr2, ...arr3]

把字符串转为数组:
[...'hello']

扩展运算内部调用的是Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符转为数组,如 Set / Map / Generator函数返回的结果等

Array.from()

可以将两类对象转为数组:array-like 和 iterable
可以接受第二个参数,作用类似 map:

Array.from([1, 2, 3], x => x * x)
// [1, 4, 9]
// 等同于
Array.from(arrayLike).map(x => x * x);

其它

fill() 使用给定值,填充一个数组

new Array(3).fill(7)
// [7, 7, 7]

entries(),keys() 和 values() 返回的是 Iterator 对象,可以使用 for...of 遍历 flat() 可以将嵌套的数组拉平,参数指定想要拉平的层数

对象

对象合并:Object.assign()
克隆对象:Object.assign({}, origin)

属性的遍历

for...in 返回自身和继承的 enumerable 属性
Object.keys() 返回自身的 enumerable 属性
Object.getOwnPropertyNames() 返回自身的 enumerable 和 innumerable 属性
Object.getOwnPropertySymbols() 返回自身的所有 Symbol 属性
Reflect.ownKeys() 返回自身的所有键名

Object.create(),Object.getPrototypeOf(),Object.setPrototypeOf()

尽量不要直接操作__proto__,而是使用 Object.setPrototypeOf() Object.getPrototypeOf() Object.create()代替
使用示例:

// es5 的写法
obj.__proto__ = someOtherObj;

// es6 的写法
var obj = Object.create(someOtherObj);
// 格式
Object.setPrototypeOf(object, prototype)

// 用法
const o = Object.setPrototypeOf({}, null);
Object.getPrototypeOf(obj);

super关键字

指向当前对象(定义时所在对象)的__proto__,只能用在对象的方法之中(只能用简写的对象方法)

const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

Object.keys(),Object.values(),Object.entries()

可以和 for...of 配合使用,用于遍历对象

let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };

for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}
for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

Object.keys(obj) 返回由键名组成的数组
Object.values(obj) 返回由键值组成的数组
Object.entries() 返回由键值对组成的数组
使用 Object.entries() 将对象转为Map结构:

const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

对象的扩展运算符

const [a, ...b] = [1, 2, 3];
a // 1
b // [2, 3]
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

合并两个对象:

let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);

Symbol

是一种新的原始数据类型
可以作为对象的 key,保证属性名不会发生冲突

创建 Symbol:

let s = Symbol();

Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

let s1 = Symbol('foo');
s1 // Symbol(foo)
s1.toString() // "Symbol(foo)"

Symbol 类型还可以用于定义一组常量,保证这组常量的值都是不相等的

实例:消除魔术字符串

const shapeType = {
  triangle: Symbol()
};

function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });

属性名的遍历

Symbol 作为属性名,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys() 等方法返回 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名

Symbol.for(),Symbol.keyFor()

Symbol.for() 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');

s1 === s2 // true

Symbol.keyFor() 方法返回一个已登记的 Symbol 类型值的key。

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

内置的 Symbol 值

Symbol.hasInstance 
指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法
Symbol.isConcatSpreadable
Symbol.species 
Symbol.match 
Symbol.replace 
Symbol.search
Symbol.split 
Symbol.iterator 
Symbol.toPrimitive
Symbol.toStringTag
Symbol.unscopables