ES6学习总结

71 阅读3分钟

一、let和const

1、let

let用来声明局部变量,用法类似于var,但是没有变量提升。

2、const

const声明一个只读的常量。一旦声明,常量的值就不能改变。

3、块级作用域

块状作用域解决了内层变量可能会覆盖外层变量用来计数的循环变量泄露为全局变量的问题

二、解构赋值

解构不成功,变量的值就等于undefined

var [a, b, c] = [1, 2, 3];

let [ , , third] = ["foo", "bar", "baz"];

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

var { foo, bar } = { foo: "aaa", bar: "bbb" };

// 重命名,前面的键名为匹配模式不是变量
var { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // foo is not defined

// 指定默认值
var {x, y = 5} = {x: 1};
x // 1
y // 5

// 默认值生效的条件是,对象的属性值严格等于`undefined`。
var {x = 3} = {x: undefined};
x // 3

var {x = 3} = {x: null};
x // null

等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。

Set结构也可以解构赋值,事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。

function* fibs() {
    var a = 0;
    var b = 1;
    while (true) {
        yield a;
        [a, b] = [b, a + b];
    }
}

var [first, second, third, fourth, fifth, sixth] = fibs();

解构赋值指定默认值必须值为undefined

三、字符串扩展

codePointAt()

codePointAt方法会正确返回32位的UTF-16字符的码点。

// 𠮷为4个字节储存的字符
var s = "𠮷";
// p为2个字节储存的字符
let p = "a";

console.log(
    s.codePointAt(0),
    s.codePointAt(1),
    s.charCodeAt(0),
    p.codePointAt(0),
    p.charCodeAt(0)
);
// 134071 57271 55362 97 97

字符串的遍历器接口

字符串可以被for...of循环遍历。

for (let codePoint of 'foo') {
  console.log(codePoint)
}
// "f"
// "o"
// "o"

at()

返回字符串给定位置的字符,同es5charAt方法,charAt不能识别码点大于0xFFFF的字符。

// 识别码点大于`0xFFFF`的字符。
'abc'.at(0) // "a"

includes(), startsWith(), endsWith()

  • includes() :返回布尔值,表示是否找到了参数字符串。
  • startsWith() :返回布尔值,表示参数字符串是否在源字符串的头部。
  • endsWith() :返回布尔值,表示参数字符串是否在源字符串的尾部。
var s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

参数如果是小数,会被取整。参数是负数或者Infinity,会报错

'na'.repeat(2.9) 
'na'.repeat(Infinity)
// RangeError
'na'.repeat(-1)
// RangeError

padStart(),padEnd()

串补全长度的功能,padStart用于头部补全,padEnd用于尾部补全。

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

模板字符串

$('#result').append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

标签模板

模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。

alert`123`
// 等同于
alert(123)

Symbol

Symbol为新的原始数据类型Symbol,表示独一无二的值。用于防止属性名的冲突,导致属性被覆盖删除等。

Set和Map结构

Set

Set基础

Set无重复数组,类似与数组结构,Set中NaN唯一,及两个NaN只能添加为一个。Set可用for of遍历循环。

var s = new Set();

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

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

Set实例的属性和方法

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set实例的成员总数。

Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

  • add(value):添加某个值,返回Set结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。

遍历操作

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • entries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员 Set没有键名,故keys()和values()行为一致

WeakSet

WeakSet基础

WeakSet结构与Set类似,WeakSet的成员只能是对象。而且WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的。

WeakSet实例的属性和方法

WeakSet结构有以下三个方法。

  • WeakSet.prototype.add(value) :向WeakSet实例添加一个新成员。
  • WeakSet.prototype.delete(value) :清除WeakSet实例的指定成员。
  • WeakSet.prototype.has(value) :返回一个布尔值,表示某个值是否在WeakSet实例之中。
var ws = new WeakSet();
var obj = {};
var foo = {};

ws.add(window);
ws.has(window); // true
ws.delete(window);

Map

Map基础

var items = [
  ['name', '张三'],
  ['title', 'Author']
];
var map = new Map();
items.forEach(([key, value]) => map.set(key, value));

如果对同一个键多次赋值,后面的值将覆盖前面的值。

let map = new Map();

map
.set(1, 'aaa')
.set(1, 'bbb');

map.get(1) // "bbb"

Map实例的属性和方法

  • Map.prototype.size:返回Set实例的成员总数。

Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

  • set(key, value)set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。
  • get(key)get方法读取key对应的键值,如果找不到key,返回undefined
  • delete(key)delete方法删除某个键,返回true。如果删除失败,返回false。
  • has(key)has方法返回一个布尔值,表示某个键是否在Map数据结构中。
  • clear():清除所有成员,没有返回值。
const a = { a: 1 };
let map = new Map().set(a, "a111").set(2, "b");
a.a = 2;
map.get(a); // a111
map.has(a); // true
map.delete(a);
map.has(a); // false
map.clear();
map.has(2); // false

遍历方法

Map原生提供三个遍历器生成函数和一个遍历方法。

  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。
  • forEach():遍历Map的所有成员。 forEach接受第二个参数用来绑定this
var reporter = {
  report: function(key, value) {
    console.log("Key: %s, Value: %s", key, value);
  }
};

map.forEach(function(value, key, map) {
  this.report(key, value);
}, reporter);

与其他数据结构的互相转换

  1. Map和互转数组
let arr2 = [    [{ a: 2 }, "a111"],
    [2, "b"]
];
let map = new Map(arr2);
map; // Map(2) { { a: 2 } => 'a111', 2 => 'b' }
[...map]; // [ [ { a: 2 }, 'a111' ], [ 2, 'b' ] ]
  1. Map和对象互转 前提是map的键名都是字符串
const strObjToMap = (obj) => {
    const map = new Map();
    for (let l in obj) {
        console.log(l);
        map.set(l, obj[l]);
    }
    return map;
};
const strMapToObj = (map) => {
    let result = {};
    for (let l of map.entries()) {
        console.log(l);
        result[l[0]] = l[1];
    }
    return result;
};
let obj1 = { 2: "b", 12: "a111" };
strObjToMap(obj1); // Map(2) { '2' => 'b', '12' => 'a111' }
strMapToObj(map); // { '2': 'b', '12': 'a111' }
  1. Map和Json互转 Map和Json互转,需要注意json是不是二维数组,map的键名是不是字符串
let json1 = {
    a: 1,
    b: 2
};
let json2 = [    [1, 2],
    [2, 3]
];
let json3 = [1, 2, 3, 4];
const strJsonToMap = (json) => {
    if (Array.isArray(json) && json.some((item) => Array.isArray(item))) {
        return new Map(json);
    }
    return strObjToMap(json);
};
const strMapToJson = (map) => {
    if ([...map.keys()].every((item) => typeof item === "string")) {
        return strMapToObj(map);
    }
    return [...map];
};
strJsonToMap(json1); // Map(2) { 'a' => 1, 'b' => 2 }
strJsonToMap(json2); // Map(2) { 1 => 2, 2 => 3 }
strJsonToMap(json3); // Map(4) { '0' => 1, '1' => 2, '2' => 3, '3' => 4 }
strMapToJson(new Map().set(json1, "a")); //  [ [ { a: 1, b: 2 }, 'a' ] ]
strMapToJson(strJsonToMap(json1)); // { a: 1, b: 2 }

WeakMap

WeakMap结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受其他类型的值作为键名,而且键名所指向的对象不计入垃圾回收机制WeakMap的设计目的在于,键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收。WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。

var wm = new WeakMap();
var element = document.querySelector(".element");

wm.set(element, "Original");
wm.get(element) // "Original"

element.parentNode.removeChild(element);
element = null;
wm.get(element) // undefined

Proxy 和 Reflect

Proxy 概述

Proxy拦截器用于修改某些操作的默认行为

var obj2 = new Proxy(
    {},
    {
        get: function (target, key, receiver) {
            console.log(`getting ${key}!`);
            return Reflect.get(target, key, receiver);
        },
        set: function (target, key, value, receiver) {
            console.log(`setting ${key}!`);
            return Reflect.set(target, key, value, receiver);
        }
    }
);
obj2.key = 1; // setting key!
obj2.key; // getting key!

Proxy 支持的拦截操作一览。

  • get(target, propKey, receiver) 读取拦截,拦截对象属性的读取,比如proxy.fooproxy['foo']。最后一个参数receiver是一个对象。
  • set(target, propKey, value, receiver) 赋值拦截,拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey) 判断是否拥有拦截,拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。
  • deleteProperty(target, propKey) 删除拦截,拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target) 读取属性数组拦截,拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy),返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。
  • getOwnPropertyDescriptor(target, propKey) 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc) 拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target) 拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target) 拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target) 拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto) 拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args) 拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args) 拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

Iterator和for...of循环

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

Generator 函数

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

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

var hw = helloWorldGenerator();

yield语句

遍历器对象的next方法的运行逻辑如下。 (1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。 (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。 (3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。 (4)如果该函数没有return语句,则返回的对象的value属性值为undefinedyield语句如果用在一个表达式之中,必须放在圆括号里面。

es6转码es5

1、安装脚手架

npm install --global babel-cli

2、配置配置文件.babelrc

.babelrc基本格式为

{
  "presets": [],
  "plugins": []
}

添加相关规则如

  {
    "presets": [
      "es2015",
      "react",
      "stage-2"
    ],
    "plugins": []
  }

预设规则集:

// ES2015转码规则
$ npm install --save-dev babel-preset-es2015

// react转码规则
$ npm install --save-dev babel-preset-react

// ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3

3、使用命令行转码babel-cli

基本用法

// 转码结果输出到标准输出
$ babel example.js

// 转码结果写入一个文件
// --out-file 或 -o 参数指定输出文件
$ babel example.js --out-file compiled.js
// 或者
$ babel example.js -o compiled.js

// 整个目录转码
// --out-dir 或 -d 参数指定输出目录
$ babel src --out-dir lib
// 或者
$ babel src -d lib

// -s 参数生成source map文件
$ babel src -d lib -s