ES6 详解

es 总结。

let const

  • 定义常量使用 const
  • 块级作用域定义变量使用 let

变量的解构赋值

数组、对象解构

const [first, second] = arr;

// 定义的变量名要与属性名一致
const {first, ...second} = obj;

使用扩展运算符(…)拷贝数组。(对象也是如此)

const arrCopy = [...arr];
const objCopy = {...obj};

// similar
Object.assign(obj,{a,1})

解构赋值也可以设置默认值。

计算属性

const name = 'tom'; 
const obj = {[name]: 'hello'} // {tom:'hello'}

字符串的扩展

待整理…

正则的扩展

待整理…

数值的扩展

之前一些全局方法(parseInt、isFinite)等,放到了Number对象上。

新增了一些方法,比如判断数字是否是整型、正数、处于安全数值范围、立方根、三角函数、对数运算。

// 二进制 八进制表示
console.log('B',0B111110111);
console.log(0o767);

// 是否是正常数值
console.log('15',Number.isFinite(15));
console.log('NaN',Number.isFinite(NaN));
console.log('1/0',Number.isFinite('true'/0));
console.log('NaN',Number.isNaN(NaN));
console.log('0',Number.isNaN(0));


// 是否整形
console.log('25',Number.isInteger(25));
console.log('25.0',Number.isInteger(25.0));
console.log('25.1',Number.isInteger(25.1));
console.log('25.1',Number.isInteger('25'));

// 安全值
console.log(Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER);
console.log('10',Number.isSafeInteger(10));
console.log('a',Number.isSafeInteger('a'));

// 取整数位
console.log(4.1,Math.trunc(4.1));
console.log(4.9,Math.trunc(4.9));

// 判断正负数 零
console.log('-5',Math.sign(-5));
console.log('0',Math.sign(0));
console.log('5',Math.sign(5));
console.log('50',Math.sign('50'));
console.log('foo',Math.sign('foo'));

// 立方根
console.log('-1',Math.cbrt(-1));
console.log('8',Math.cbrt(8));

函数的扩展

参数默认值

function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}

const p = new Point();
p // { x: 0, y: 0 }

rest参数

ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
let sum = 0;

for (var val of values) {
sum += val;
}

return sum;
}

add(2, 5, 3) // 10

严格模式

从 ES5 开始,函数内部可以设定为严格模式。

function doSomething(a, b) {
'use strict';
// code
}

ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

// 报错
function doSomething(a, b = a) {
'use strict';
// code
}

// 报错
const doSomething = function ({a, b}) {
'use strict';
// code
};

// 报错
const doSomething = (...a) => {
'use strict';
// code
};

const obj = {
// 报错
doSomething({a, b}) {
'use strict';
// code
}
};

name属性

函数的name属性,返回该函数的函数名。
这个属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准。

function foo() {}
foo.name // "foo"

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

箭头函数

var f = v => v;

// 等同于
var f = function (v) {
return v;
};

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

双冒号运算符

用来取代call、apply、bind调用。

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

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

const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}

尾部调用优化

尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。

function f(x){
return g(x);
}

优化

function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();

// 等同于
function f() {
return g(3);
}
f();

// 等同于
g(3);

上面代码中,如果函数g不是尾调用,函数f就需要保存内部变量m和n的值、g的调用位置等信息。但由于调用g之后,函数f就结束了,所以执行到最后一步,完全可以删除f(x)的调用帧,只保留g(3)的调用帧。

尾递归

函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}

factorial(5) // 120

上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。

如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。

function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

// or

function tailFactorial(n, total) {
if (n === 1) return total;
return tailFactorial(n - 1, n * total);
}

function factorial(n) {
return tailFactorial(n, 1);
}

factorial(5) // 120

数组的扩展

扩展运算符

rest逆运算。

Array.from

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

下面是一个类似数组的对象,Array.from将它转为真正的数组。

let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).filter(p => {
return p.textContent.length > 100;
});

// arguments对象
function foo() {
var args = Array.from(arguments);
// ...
}

Array.of

Array.of方法用于将一组值,转换为数组。

let arr = Array.of(3, 4, 7, 9, 11);
console.log('arr=', arr);

let empty = Array.of();
console.log('empty', empty);

copyWithin

数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)
  • target(必需):从该位置开始替换数据。如果为负值,表示倒数。
  • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]

// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}

// 将2号位到数组结束,复制到0号位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]

// 对于没有部署 TypedArray 的 copyWithin 方法的平台
// 需要采用下面的写法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]

find\findIndex

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10

[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2

fill

fill方法使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7)
// [7, 7, 7]

fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

entries\keys\values

for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"

inludes

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true

对象的扩展

Object.is

用来代替相等与严格相等运算符。

Object.is('foo', 'foo')
// true
Object.is({}, {})
// false

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

symbol

ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

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

let s = Symbol();

typeof s
// "symbol"

// -----

let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

// --------

// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();

s1 === s2 // false

// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');

s1 === s2 // false

// --------

let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

Symbol 作为属性名,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。

const obj = {};
let a = Symbol('a');
let b = Symbol('b');

obj[a] = 'Hello';
obj[b] = 'World';

const objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols
// [Symbol(a), Symbol(b)]


let obj = {
[Symbol('my_key')]: 1,
enum: 2,
nonEnum: 3
};

Reflect.ownKeys(obj)
// ["enum", "nonEnum", Symbol(my_key)]

Symbol.for(),Symbol.keyFor()

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

s1 === s2 // true

// -----

Symbol.for("bar") === Symbol.for("bar")
// true

Symbol("bar") === Symbol("bar")
// false

// ----
// Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

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

set && map

set

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

for (let i of s) {
console.log(i);
}
// 2 3 5 4

Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]

// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5

// 例三
const set = new Set(document.querySelectorAll('div'));
set.size // 56

// 类似于
const set = new Set();
document
.querySelectorAll('div')
.forEach(div => set.add(div));
set.size // 56

// 去除数组的重复成员
[...new Set(array)]

Set 实例的属性和方法

  • size
  • add()
  • delete()
  • has()
  • clear()
s.add(1).add(2).add(2);
// 注意2被加入了两次

s.size // 2

s.has(1) // true
s.has(2) // true
s.has(3) // false

s.delete(2);
s.has(2) // false

Array.from方法可以将 Set 结构转为数组。

const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);

function dedupe(array) {
return Array.from(new Set(array));
}

dedupe([1, 1, 2, 3]) // [1, 2, 3]

遍历

let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

for (let x of set) {
console.log(x);
}
// red
// green
// blue


set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9

使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值。

const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set

其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

三个方法:

const ws = new WeakSet();
const obj = {};
const foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo); // false

ws.delete(window);
ws.has(window); // false

WeakSet 没有size属性,没有办法遍历它的成员。

map

类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

// ---

const map = new Map([
['name', '张三'],
['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"

遍历方法

const map = new Map([
['F', 'no'],
['T', 'yes'],
]);

for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"

Map 结构转为数组结构,比较快速的方法是使用扩展运算符(…)。

const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);

[...map.keys()]
// [1, 2, 3]

[...map.values()]
// ['one', 'two', 'three']

[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]

[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]

WeakMap

WeakMap与Map的区别有两点。

首先,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。

其次,WeakMap的键名所指向的对象,不计入垃圾回收机制。

proxy && reflect

{
let obj = { // 供应商
time: '2017-03-11',
name: 'net',
_r: 123
};

let monitor = new Proxy(obj, { // 代理商
// 拦截对象属性的读取
get(target, key) {
return target[key].replace('2017', '2018')
},
// 拦截对象设置属性
set(target, key, value) {
if (key === 'name') {
return target[key] = value;
} else {
return target[key];
}
},
// 拦截key in object操作
has(target, key) {
if (key === 'name') {
return target[key]
} else {
return false;
}
},
// 拦截delete
deleteProperty(target, key) {
if (key.indexOf('_') > -1) {
delete target[key];
return true;
} else {
return target[key]
}
},
// 拦截Object.keys,Object.getOwnPropertySymbols,Object.getOwnPropertyNames
ownKeys(target) {
return Object.keys(target).filter(item => item != 'time')
}
});

console.log('get', monitor.time);

monitor.time = '2018';
monitor.name = 'mukewang';
console.log('set', monitor.time, monitor);

console.log('has', 'name' in monitor, 'time' in monitor);

// delete monitor.time;
// console.log('delete',monitor);
//
// delete monitor._r;
// console.log('delete',monitor);
console.log('ownKeys', Object.keys(monitor));

}

{
let obj = {
time: '2017-03-11',
name: 'net',
_r: 123
};

console.log('Reflect get', Reflect.get(obj, 'time'));
Reflect.set(obj, 'name', 'mukewang');
console.log(obj);
console.log('has', Reflect.has(obj, 'name'));
}


// proxy 应用 数据校验
{
function validator(target, validator) {
return new Proxy(target, {
_validator: validator,
set(target, key, value, proxy) {
if (target.hasOwnProperty(key)) {
let va = this._validator[key];
if (!!va(value)) {
return Reflect.set(target, key, value, proxy)
} else {
throw Error(`不能设置${key}${value}`)
}
} else {
throw Error(`${key} 不存在`)
}
}
})
}

const personValidators = {
name(val) {
return typeof val === 'string'
},
age(val) {
return typeof val === 'number' && val > 18
},
mobile(val) {

}
}

class Person {
constructor(name, age) {
this.name = name;
this.age = age;
this.mobile = '1111';
return validator(this, personValidators)
}
}

const person = new Person('lilei', 30);

console.info(person);

person.name = 'Han mei mei';

console.info(person);
}

类与对象

待整理…

promise

{
// 基本定义
let ajax = function (callback) {
console.log('执行');
setTimeout(function () {
callback && callback.call()
}, 1000);
};
ajax(function () {
console.log('timeout1');
})
}

{
let ajax = function () {
console.log('执行2');
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve()
}, 1000);
})
};

ajax().then(function () {
console.log('promise', 'timeout2');
})
}

{
let ajax = function () {
console.log('执行3');
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve()
}, 1000);
})
};

ajax()
.then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve()
}, 2000);
});
})
.then(function () {
console.log('timeout3');
})
}

{
let ajax = function (num) {
console.log('执行4');
return new Promise(function (resolve, reject) {
if (num > 5) {
resolve()
} else {
throw new Error('出错了')
}
})
}

ajax(6).then(function () {
console.log('log', 6);
}).catch(function (err) {
console.log('catch', err);
});

ajax(3).then(function () {
console.log('log', 3);
}).catch(function (err) {
console.log('catch', err);
});
}

{
// 所有图片加载完再添加到页面
function loadImg(src) {
return new Promise((resolve, reject) => {
let img = document.createElement('img');
img.src = src;
img.onload = function () {
resolve(img);
}
img.onerror = function (err) {
reject(err);
}
})
}

function showImgs(imgs) {
imgs.forEach(function (img) {
document.body.appendChild(img);
})
}

Promise.all([
loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
loadImg('http://i4.buimg.com/567751/2b07ee25b08930ba.png'),
loadImg('http://i2.muimg.com/567751/5eb8190d6b2a1c9c.png')
]).then(showImgs)

}

{
// 有一个图片加载完就添加到页面
function loadImg(src) {
return new Promise((resolve, reject) => {
let img = document.createElement('img');
img.src = src;
img.onload = function () {
resolve(img);
}
img.onerror = function (err) {
reject(err);
}
})
}

function showImgs(img) {
let p = document.createElement('p');
p.appendChild(img);
document.body.appendChild(p)
}

Promise.race([
loadImg('http://i4.buimg.com/567571/df1ef0720bea6832.png'),
loadImg('http://i4.buimg.com/567751/2b07ee25b08930ba.png'),
loadImg('http://i2.muimg.com/567751/5eb8190d6b2a1c9c.png')
]).then(showImgs)

}


// 异步加载图片
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();

image.onload = function() {
resolve(image);
};

image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};

image.src = url;
});
}

// ajax promise封装
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();

});

return promise;
};

getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});

iterator

各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for…of循环,Iterator 接口主要供for…of消费。

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }

function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}

原生具备 Iterator 接口的数据结构如下。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

下面的例子是数组的Symbol.iterator属性。

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

一个对象如果要具备可被for…of循环调用的 Iterator 接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)。

class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}

[Symbol.iterator]() { return this; }

next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}

function range(start, stop) {
return new RangeIterator(start, stop);
}

for (var value of range(0, 3)) {
console.log(value); // 0, 1, 2
}
let obj={
start:[1,3,2],
end:[7,9,8],
[Symbol.iterator](){
let self=this;
let index=0;
let arr=self.start.concat(self.end);
let len=arr.length;
return {
next(){
if(index<len){
return {
value:arr[index++],
done:false
}
}else{
return {
value:arr[index++],
done:true
}
}
}
}
}
}
for(let key of obj){
console.log(key);
}

generator

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}

var hw = helloWorldGenerator();
{
// genertaor基本定义
let tell=function* (){
yield 'a';
yield 'b';
return 'c'
};

let k=tell();

console.log(k.next());
console.log(k.next());
console.log(k.next());
console.log(k.next());
}

{
let obj={};
obj[Symbol.iterator]=function* (){
yield 1;
yield 2;
yield 3;
}

for(let value of obj){
console.log('value',value);
}
}

{
let state=function* (){
while(1){
yield 'A';
yield 'B';
yield 'C';
}
}
let status=state();
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
console.log(status.next());
}

// {
// let state=async function (){
// while(1){
// await 'A';
// await 'B';
// await 'C';
// }
// }
// let status=state();
// console.log(status.next());
// console.log(status.next());
// console.log(status.next());
// console.log(status.next());
// console.log(status.next());
// }
//

{
let draw=function(count){
//具体抽奖逻辑
console.info(`剩余${count}次`)
}

let residue=function* (count){
while (count>0) {
count--;
yield draw(count);
}
}

let star=residue(5);
let btn=document.createElement('button');
btn.id='start';
btn.textContent='抽奖';
document.body.appendChild(btn);
document.getElementById('start').addEventListener('click',function(){
star.next();
},false)
}

{
// 长轮询
let ajax=function* (){
yield new Promise(function(resolve,reject){
setTimeout(function () {
resolve({code:0})
}, 200);
})
}

let pull=function(){
let genertaor=ajax();
let step=genertaor.next();
step.value.then(function(d){
if(d.code!=0){
setTimeout(function () {
console.info('wait');
pull()
}, 1000);
}else{
console.info(d);
}
})
}

pull();
}

decorator

修饰器是个函数、修改类的行为(扩展类的功能)

module

导入导出语法

import .... from ...
export
export default
// 如果模块默认输出一个函数,函数名的首字母应该小写。
// 如果模块默认输出一个对象,对象名的首字母应该大写。

export 与 import 的复合写法

// foo和bar实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo和bar
export { foo, bar } from 'my_module';

// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };

// ---
// 具名接口改为默认接口的写法如下
export { es6 as default } from './someModule';

// 等同于
import { es6 } from './someModule';
export default es6;

// ---
// 默认接口也可以改名为具名接口。
export { default as es6 } from './someModule';

使用Eslint

# 安装
$ npm i -g eslint
# 然后,安装 Airbnb 语法规则,以及 import、a11y、react 插件。
$ npm i -g eslint-config-airbnb
$ npm i -g eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react
# 最后,在项目的根目录下新建一个.eslintrc文件,配置ESLint。
{
"extends": "eslint-config-airbnb"
}

检验文件

eslint index.js
amenzai wechat
扫一扫上面二维码,获取更多内容。
欢迎各位老板打赏