ECMAScript 6.x 学习笔记

整理归纳的 ES6 中新增的一些新特性。每个知识点有简单的例子以便快速了解。内容较多推荐查看目录选择性食用。

let

定义变量,相当于之前的 var,但块级区分得更明显。

和 var 的主要区别:

  • 没有预编译,let 在编译时才初始化,不存在变量提升。在代码块内,只要用 let 定义变量,在之前使用,都是报错。先定义完再使用。
  • 同一个作用域里,不能重复定义变量。
  • 类 for 循环(for…in、for…of),for() 里是一个块级作用域,{} 里又是一个块。每次循环 for(let i) 里用 let 定义的 i 值都是独立的,每次循环浏览器引擎会记住上一个 let i 的值。
for (let i = 0; i < 1; i++) {
  let i = 5; // 不会报错
}
  • 在程序和方法的最顶端,let 不像 var 一样,let 不会在全局对象里新建一个属性
var x = "global";
let y = "global";
console.log(this.x); // "global"
console.log(this.y); // undefined

const

定义常量,定义好不能改变(理论上)。特性与 let 基本相同。

和 let 的主要区别:

  • 定义的时候必须要赋值。值不能变,一改就报错。
  • 对象引用值,例如数组,可以增删改不能置空。可以使用 Object.freeze([ ])让他完全不能更改。

解构赋值

常用于数据交互 ajax,等号左右两边结构要保持一致。

let [a, b, c] = [12, 5, 6];
let json = { name:"xdx", age="18" };
let { name, age } =json;

主要的用法

let { name, age:newName } = { name:"xdx", age="18" }; // 可以通过冒号取别名

let { name, age = "default" } = { name: "xdx" };  // 可以给默认数据避免 undefined(无法避免 null,null 是空指针)

[a, b] = [b, a]; // 交换两值的位置

字符串模板

`` 左上角数字 1 旁边那个键,用来拼接字符串。

  • 可以以随意换行
  • 代替字符串连接+
let name = "XDX";
let age = 18;
let str = `这个人叫${name},年龄是${age}`;

字符串新增方法

基本类型在调用方法时实际是自动转成了 String 的实例并调用对应的方法,故是构造函数 String 原型上新增的方法。

字符串查找

str.indexOf(sth); // 字符串查找,返回索引(位置) ,没找到返回-1

str.includes(sth); // 字符串查找,返回值 true/false

str.startsWith(sth); // 字符串是否以谁开头(检测网址)

str.endsWith(sth); // 字符串是否以谁结尾(检测文件类型)

str.repeat(times); // 返回重复 times 次的字符串

填充字符串

str.padStart("填充后整个字符串长度", "填充东西"); // 往前填充 "aaa".padStart(10, "12345") -> "1234512aaa"
str.padEnd("填充后整个字符串长度", "填充东西"); // 往后填充 "aaa".padEnd(10, "12345") -> "aaa1234512"

str.padStart(str.length + padStr.length, padStr); // 完整填充

函数变化与新增

函数默认参数

function show(x = 666) {
  console.log(x);
}
show(); // 666
// 配合解构使用
function show({ x = 0, y = 0 } = {}) {
  console.log(x, y);
}
show(); // 0, 0

函数参数存在变量提升,不能再使用 let,const 声明

function show(a = 18) {
  let a = 101; //错误
  console.log(a);
}
show();

扩展运算符

… 三个点

主要的三个作用

  • 处理形式参数
  • 浅拷贝对象
  • 对对象进行解构
// 1、当参数个数不定时,可以使用该方法将所有参数合并为一个数组
function show(...a) {
  console.log(a);
}
show(1, 2, 3, 4); //[1,2,3,4]

// 2、将不定的参数放到最后,聚合为一个数组
function show2(a, b, ...c) {
  consoel.log(c);
}
show2(1, 2, 3, 4, 5, 6, 7); // [3, 4, 5, 6, 7]

// 3、复制对象 浅拷贝
let obj = {
  a: 3,
  b: function () {
    console.log("这是一个方法");
  },
};

let obj2 = { ...obj1 };

// 4、对属性或方法解构时使用
let { x, y, ...i } = { x: 1, y: 2, z: 4, j: 6 }; // 属于扩展运算符部分的定义(z、j)可以随意
i; // {z: 4, j: 6}

箭头函数

主要形式() => {},与 function(){}类似,主要时块级作用域中 this 指向不同

// 主要的一些用法
let show = () => 1;

() => return "东西";

() =>{
  "语句"
  return "东西";
}

注意

  • this 问题, 定义函数所在的对象,不在是运行时所在的对象
  • 箭头函数不能当构造函数
  • 箭头函数里面没有 arguments,用 …实现类似功能
let show = (...args) => {
  console.log(args);
};

数组新增内容

循环

// arr.forEach() 代替普通 for,没有返回值
arr.forEach(function (val, index, arr) {
  console.log(val, index, arr);
  //里面的 this 默认指向 windows
});

arr.filter(); // 过滤,过滤一些不合格“元素”,如果回调函数返回 true,就留下来

arr.some(); // 类似查找,数组里面某一个元素符合条件,只要有返回 true

arr.every(); // 数组里面所有的元素都要符合条件,才返回 true

arr.map(); // 用于重新整理数据结构(处理后台传过来的数据),正常情况下需要配合 return,一般返回一个新的数组。若是没有 return,相当于 forEach。

arr.map

以上方法都可以接收两个参数,一个回调 + 一个 this。使用箭头函数则第二个参数无效

还有一个比较特殊的 reduce,用来求数组的和、阶乘等

arr.reduce(); //从左往右
arr.reduceRight(); //从右往左

arr.reduce((prec, cur, index, arr) => {}); // 接收的参数:前面处理的结果,后一个元素,索引,数组本身

reduce

遍历数组/对象的新方法

for….of….

默认遍历的就是 value,arr.value 有可能会报错

for (let val of arr) {
  console.log(val);
}

of 后面数组可以使用的方法

arr.keys(); // 数组下标 index
arr.entries(); // 数组某一项,索引和内容,以数组展示(可用解构)

复制数组

arr2 = arr1.slice();
arr2 = [...arr1];
arr2 = Array.from(arr1);
arr2 = Object.assign([], arr1):

其余补充

// 1、把类数组(获取一组元素、arguments...)对象转成数组,具备 length 属性一般为类数组
Array.from();

// 2、把一组值,转成数组
Array.of();
let arr = Array.of("apple", "banana", "orange");

// 3、查询元素
arr.find(function () {}); // 查找返回第一个符合条件的数组成员,如果没有找到,返回 undefined

arr.findIndex(function () {}); // 查找对应值的索引,没找到返回-1

arr.indexOf(); // 查找对应值的索引,没找到返回-1

arr.includes(); // 是否包含相应值,返回布尔值

// 4、填充
arr.fill("填充的东西", "开始位置", "结束位置");

对象新增与改进

对象简洁语法

object

新增方法

// 1、Object.is() 用来比较两个值是否相等(==,===)
Object.is("a", "a"); //true
Object.is(NaN, NaN); //true 用==的话返回 false
Object.is(+0, -0); //false 用==的话返回 true

// 2、Object.assign() 用来合并对象
let newArr = Object.assign(目标对象, source1, srouce2, ....) // 后面的覆盖前面的用来复制对象
let newArr2 = Object.assign({}, source1)

// 3、获取key/value
Object.keys(); // 返回属性名
Object.entries(); // 返回整个数据
Object.values(); // 返回属性值

解构以方便遍历

object

Promise

解决异步回调问题,所有的 promise 对象都是 resolve 状态才执行 res,否则执行 err(抓取第一个出现的错误)。

语法:(链式操作)
promise

Promise.all

Promise.all([p1, p2, p3]): 把 promise 对象打包到一个数组里面,打包完还是一个 promise 对象
promiseAll

Promise.race

Promise.race([p1, p2, p3]): 返回第一个成功或失败的结果。

实例

使用 Promise 实现用户登录
promiseUser

模块化

注意: 需要放到 node 环境

定义模块

export "东西"
export const a = 12;
export { aaa, banana };

使用 import

import "./modules/1.js";
import { a as a, banana, c } from "./modules/2.js";
import * as modTwo from "./modules/2.js";
<script type="module"></script>

import 特点

  • import 可以是相对路径,也可以是绝对路径
import "https://code.jquery.com/jquery-3.3.1.js";
  • import 模块只会导入一次,无论你引入多少次
  • import ‘./modules/1.js’; 如果这么用,相当于引入文件
  • 有提升效果,import 会自动提升到顶部,首先执行
  • 导出去模块内容,如果里面有定时器更改,外面也会改动

使用 import()

类似 node 里面 require,可以动态引入,默认 import 语法不能写到 if 之类里面,返回值是个 promise 对象

import("./modules/1.js").then((res) => {
  console.log(res.a + res.b);
});

优点:

  • 按需加载
  • 可以写 if 中
  • 路径也可以动态

类(Class)

使用 typeof 检测依旧是 function。

示例

面向对象(人) ,类(Person),属性(name),方法(showName)。
class

注意

  • ES6 里面 class 没有提升功能,在 ES5,用函数模拟可以,默认函数提升
  • ES6 里面 this 一般都会正常指向新建的对象

矫正 class 中的 this

bind 是返回对应函数,便于稍后调用;apply,cal 则是立即调用。

fn.call(this,args1, args2....)
fn.apply(this,[args1, args2....])
fn.bind(this)

class-this

取值函数(getter),存值函数(setter)

class-getter

静态方法

类身上不会继承的方法

继承

ES5 中的继承

extend

ES6 中的继承:extends

class-extends

新数据类型 Symbol

基本数据类型 Number、String、Boolean、Object、undefined、Function、Symbol
typeof 检测为小写的 symbol

let syml = Symbol("aaa");

注意

  • Symbol 不能使用 new 操作符
  • Symbol() 返回是一个唯一值,做一个 key,定义一些唯一或者私有的东西
  • symbol 是一个单独数据类型,就叫 symbol,基本类型
  • 如果 symbol 作为 key,用 for…in 循环遍历不到

generator 函数

生成器,解决异步深度嵌套的问题,基本弃用改用 async。

// 语法 function 和函数名之间添加一个*
function* show() {
  yield;
}
function* show() {}
function* show() {}

定义与调用

run-generator

手动调用比较麻烦可以使用 for .. of 自动遍历 generator,return 的东西会被忽略

gen-for

其他用法

// 解构赋值
let [a, ...b] = gen(); //a = welcome 和 b = to

// 扩展运算符
console.log(...gen()); //直接将左右东西输出

// Array.from()
console.log(Array.from(gen())); //[..,...]

关于异步的一些解决方案:

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise 对象

async

基础用法

捕获错误

async function fn() {
  //表示异步,这个函数里面有异步任务
  letresult = await xxx; //表示后面结果需要等待,得出结果再往下执行
}

特点

  • await 只能放到 async 函数中
  • 相比 genrator 语义化更强
  • await 后面可以是 promise 对象,也可以数字、字符串、布尔
  • async 函数返回是一个 promise 对象
  • 只要 await 语句后面 Promise 状态变成 reject, 那么整个 async 函数会中断执行

解决抛出错误

try…catch
await-catch
promise 本身 catch
await-catch2
建议凡是用到 await 的语句都用 try…catch 包裹

Set 与 WeakSet

类似数组,但是里面不能有重复值。

// Set 用法:
let setArr = new Set(["a", "b", "a"]); //有序

常用属性与方法

setArr.add("a"); // 往 setArr 里面添加一项
setArr.delete("b"); // 删除一项
setArr.has("a"); // 判断 setArr 里面有没有此值 true/false
setArr.size; // 个数 //属性,类似 length
setArr.clear(); // 清空

循环遍历

for (let item of setArr) {
  console.log(item);
} //默认是 values()

for (let item of setArr.keys()) {
  console.log(item);
} //类似 json 的 key,数组的索引,但是值和 value 相同。

for (let item of setArr.values()) {
}

for (let [k, v] of setArr.entries()) {
}
setArr.forEach((value, index) => {
  console.log(value, index);
});

链式操作

let setArr = newSet().add("a").add("b").add("c");

应用示例

// 数组去重:
let arr = [1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 4];
let newArr = [...new Set(arr)];

// set 数据结构变成数组:
[...set];

使用 map 和 filter 等数据方法

set-map

注意事项

  • new Set([]); 存储数组,可以通过 add 添加对象
  • new WeakSet({}); 存储对象,如果没有其他的变量或属性引用这个对象值,则这个对象值会被回收。因此无法被枚举,没有办法拿到它包含的所有元素。
  • WeakSet 没有 size,clear(),有 add(),has(),delete()

Map 与 WeakMap

类似 object, 但是 object 的键(key)只能是字符串,Map 的 key 可以是任意类型。

Map 是对 json 功能增强,key 可以是任意类型值。

// 使用:
let map = new Map();

newMap

常用方法

map.set(key, value); // 设置一个值
map.get(key); // 获取一个值
map.delete(key); // 删除一项
map.has(key); // 判断有没有
map.clear(); // 清空

循环遍历

for (let [key, value] of map) {
} //默认是 entries
for (let key of map.keys()) {
}
for (let value of map.values()) {
}
for (let [k, v] of map.entries()) {
}
map.forEach((value, key) => {});

注意事项

WeakMap() key 只能是对象。

数字(数值)变化

定义一个不是十进制的数

// 二进制(Binary):
let a = 0b010101; //使用 0b 前缀
// 八进制(Octal):
let a = 0o666; //使用 0o 前缀
// 十六进制(hex):
let a = 0x666; //使用 0x 前缀

常用方法

Number.isNaN(NaN); // 判断是不是不是数字。跟以前 isNaN 一样,只不过是成了 Number 的一个方法
Number.isFinite(a); // 判断是不是数字/有限的

Number.isInteger(a); // 判断数字是不是整数

Number.parseInt();
Number.parseFloat();

安全整数

js 能准确表示的数值范围是-2^53 到 2^53,不包含-2^53 和 2^53

2**53 === 2**53 + 1 //true
Number.isSafeInteger(a); 判断是否是安全整数
Number.MAX_SAFE_INTEGER 最大安全整数
Number.MIN_SAFE_INTEGER 最小安全整数

Math 的变化

新增的一些方法

// Math.trunc() 截取,只保留整数部分
Math.trunc(4.5); //4
Math.trunc(4.9); //4

// Math.sign(-5) 判断一个数到底是正数、负数、0
Math.sign(-5); //-1
Math.sign(5); //1
Math.sign(0); //0
Math.sign(-0); //-0
// 其他值返回 NaN

// Math.cbrt() 计算一个数立方根
Math.cbrt(27); //3

幂运算符

Math.pow(2, 3) == 2 ** 3;

正则新增的内容

命名捕获

语法:(?<名字>)
reg

反向引用命名捕获

反向引用方法\1 \2 $1 $2
语法:\k<名字>
reg1
反向引用\1 等可用
reg2
替换事例
reg3

dotAll 模式

以前 ‘.’ 在正则里表示匹配任意东西,但是不包括 \n,dotAll 模式中的 ‘.’ 将真正的匹配任何东西

let reg = /\w+/gims; //在最后添加 s 以启用 dotAll 模式

dotall

标签函数

function fn() {} //定义函数不变
fn(); //这样调用就是普通函数
fn`一些内容`; //这样调用就是当作标签函数使用

标签函数会将非变量元素和一个空放到一个数组里作为第一个参数传给函数
tagfun

proxy

扩展(增强)对象、方法(函数)一些功能,proxy 是设计模式的一种,代理模式。

作用

预警、上报、扩展功能、统计、增强对象等等

语法

proxy

应用实例

get() 取值时拦截
proxyget
set() 设置值时拦截
proxyset

deleteProperty() 删除时拦截与 has() 检测有没有时拦截;
deleteProperty
apply() 拦截方法
proxyapply

Reflect

组织计划将一些类似 Object.xxx 的语言内部的方法放到了 Reflect 对象身上,然后通过 Reflect 对象直接拿到语言内部的东西。例如:
reflect

日常用法

目前常配合 proxy 中的 apply()使用
使用 Reflect.apply 以保证函数在被拦截之后可以正常运行

// 与 fn.call()和 fn.apply()类似
Reflect.apply(调用的函数,this 指向,参数数组)

一个实例
reflectsample