学习总结常用的ES6语法(二)

学习总结常用的ES6语法(一)

七、 module

ES6 中模块化语法有两种形式

  1. 如果是输出一个唯一的对象,使用export default
// 创建 util1.js 文件,内容如下
export default {// 导出
	a:100
}
// 创建 index.js 文件,内容如下
import obj from './util1.js'// 引入
console.log(obj)
  1. 如果想要输出许多个对象,就不能使用 default,且import时候加 { … },
// 创建util2.js 文件,内容如下
export function fn1() {// 导出
	alert('fn1')
}
export function fn2() {
	alert('fn2')
}
// 创建 index.js 文件,内容如下
import {fn1,fn2} from './util2.js'// 引入
fn1()
fn2()

模块化 :主要是用来抽离公共代码,隔离作用域,避免变量冲突等
1.自执行函数;在一个单独的函数作用域中执行代码,避免变量冲突
2.闭包的写法;函数a里面的函数b ,可以访问函数a声明的变量
3.各种过度的规范:AMD CMD UMD
*AMD 使用 require js 来编写模块化,特点; 依赖必须提前声明好
*CMD 使用 seaJs commonJs:nodejs 中自带的模块化来编写模块化,特点; 支持动态引入依赖文件
*UMD 兼容了AMD commonJs 模块化语法
4. webpack(require.ensure):wabpack 2.x 版本中的代码分割。
5. import from: ES6 引入的模块化,支持import 来引入另一个js

八、Proxy

如果你平时有关注 Vue 的进展的话,可能已经知道了在 Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。
Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。

let p = new Proxy(target, handler)

target 代表需要添加代理的对象,handler 用来自定义对象中的操作,比如可以用来自定义 set 或者 get 函数。

//接下来我们通过 `Proxy` 来实现一个数据响应式
let onWatch = (obj, setBind, getLogger) => {
  let handler = {
    get(target, property, receiver) {
      getLogger(target, property)
      return Reflect.get(target, property, receiver)
    },
    set(target, property, value, receiver) {
      setBind(value, property)
      return Reflect.set(target, property, value)
    }
  }
  return new Proxy(obj, handler)
}

let obj = { a: 1 }
let p = onWatch(
  obj,
  (v, property) => {
    console.log(`监听到属性${property}改变为${v}`)
  },
  (target, property) => {
    console.log(`'${property}' = ${target[property]}`)
  }
)
p.a = 2 // 监听到属性a改变
p.a // 'a' = 2

在上述代码中,我们通过自定义 setget 函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。
当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要我们在 get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy 可以完美监听到任何方式的数据改变,唯一缺陷可能就是浏览器的兼容性不好了。

九、Class 类定义

class 其实一直是 JS 的关键字(保留字),但是一直没有正式使用,直到 ES6 。 ES6 的 class 就是取代之前构造函数初始化对象的形式,从语法上更加符合面向对象的写法。

//JS 构造函数的写法
function MathHandle(x, y) {
  this.x = x;
  this.y = y;
}
MathHandle.prototype.add = function () {
  return this.x + this.y;
};
var m = new MathHandle(1, 2);
console.log(m.add())

//用 ES6 class 的写法
class MathHandle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  add() {
    return this.x + this.y;
  }
}
const m = new MathHandle(1, 2);
console.log(m.add())

注意以下几点,全都是关于 class 语法的:

  1. class 是一种新的语法形式,是class Name {...}这种形式,和函数的写法完全不一样
  2. 两者对比,构造函数函数体的内容要放在 class 中的constructor函数中,constructor即构造器,初始化实例时默认执行
  3. class 中函数的写法是add() {...}这种形式,并没有function关键字

使用 class 来实现继承就更加简单了,至少比构造函数实现继承简单很多。

//JS 构造函数实现继承
// 动物
function Animal() {
    this.eat = function () {
        console.log('animal eat')
    }
}
// 狗
function Dog() {
    this.bark = function () {
        console.log('dog bark')
    }
}
Dog.prototype = new Animal()
// 哈士奇
var hashiqi = new Dog()


//ES6 class 实现继承
class Animal {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(`${this.name} eat`)
    }
}
class Dog extends Animal {
    constructor(name) {
        super(name)
        this.name = name
    }
    say() {
        console.log(`${this.name} say`)
    }
}
const dog = new Dog('哈士奇')
dog.say()
dog.eat()

注意以下两点:

  1. 使用extends即可实现继承,更加符合经典面向对象语言的写法,如 Java
  2. 子类的constructor一定要执行super(),以调用父类的constructor

十、Set与Map

Set 和 Map 都是 ES6 中新增的数据结构,是对当前 JS 数组和对象这两种重要数据结构的扩展。
由于是新增的数据结构,目前尚未被大规模使用,但是作为前端程序员,提前了解是必须做到的。
先总结一下两者最关键的地方:

  1. Set 类似于数组,但数组可以允许元素重复,Set 不允许元素重复;
  2. Map 类似于对象,但普通对象的 key 必须是字符串或者数字,而 Map 的 key 可以是任何数据类型.
Set

Set 实例不允许元素有重复,可以通过以下示例证明。可以通过一个数组初始化一个 Set 实例,或者通过add添加元素,元素不能重复,重复的会被忽略。

// 例1
const set = new Set([1, 2, 3, 4, 4]);
console.log(set) // Set(4) {1, 2, 3, 4}

// 例2
const set = new Set();
[2, 3, 5, 4, 5, 8, 8].forEach(item => set.add(item));
for (let item of set) {
  console.log(item);
}
// 2 3 5 4 8

Set 实例的属性和方法有:

  1. size:获取元素数量。
  2. add(value):添加元素,返回 Set 实例本身。
  3. delete(value):删除元素,返回一个布尔值,表示删除是否成功。
  4. has(value):返回一个布尔值,表示该值是否是 Set 实例的元素。
  5. clear():清除所有元素,没有返回值。
const s = new Set();
s.add(1).add(2).add(2); // 添加元素

s.size // 2

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

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

s.clear();
console.log(s);  // Set(0) {}

Set 实例的遍历,可使用如下方法:

  1. keys():返回键名的遍历器。
  2. values():返回键值的遍历器。不过由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys()values()返回结果一致。
  3. entries():返回键值对的遍历器。
  4. forEach():使用回调函数遍历每个成员。
let set = new Set(['aaa', 'bbb', 'ccc']);

for (let item of set.keys()) {
  console.log(item);
}
// aaa
// bbb
// ccc

for (let item of set.values()) {
  console.log(item);
}
// aaa
// bbb
// ccc

for (let item of set.entries()) {
  console.log(item);
}
// ["aaa", "aaa"]
// ["bbb", "bbb"]
// ["ccc", "ccc"]

set.forEach((value, key) => console.log(key + ' : ' + value))
// aaa : aaa
// bbb : bbb
// ccc : ccc
Map

Map 的用法和普通对象基本一致,先看一下它能用非字符串或者数字作为 key 的特性。

const map = new Map();
const obj = {p: 'Hello World'};

map.set(obj, 'OK')
map.get(obj) // "OK"

map.has(obj) // true
map.delete(obj) // true
map.has(obj) // false

需要使用new Map()初始化一个实例,上面代码中set get has delete顾名即可思义。
其中,map.set(obj, 'OK')就是用对象作为的 key (不光可以是对象,任何数据类型都可以),并且后面通过map.get(obj)正确获取了。
Map 实例的属性和方法如下:

  1. size:获取成员的数量
  2. set:设置成员 key 和 value
  3. get:获取成员属性值
  4. has:判断成员是否存在
  5. delete:删除成员
  6. clear:清空所有
const map = new Map();
map.set('aaa', 100);
map.set('bbb', 200);

map.size // 2

map.get('aaa') // 100

map.has('aaa') // true

map.delete('aaa')
map.has('aaa') // false

map.clear()

Map 实例的遍历方法有:

  1. keys():返回键名的遍历器。
  2. values():返回键值的遍历器。
  3. entries():返回所有成员的遍历器。
  4. forEach():遍历 Map 的所有成员。
set('aaa', 100);
map.set('bbb', 200);

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

for (let value of map.values()) {
  console.log(value);
}
// 100
// 200

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// aaa 100
// bbb 200

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// aaa 100
// bbb 200const map = new Map();
map.set('aaa', 100);
map.set('bbb', 200);

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

for (let value of map.values()) {
  console.log(value);
}
// 100
// 200

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// aaa 100
// bbb 200

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// aaa 100
// bbb 200

十一、Promise对象

Promise是 CommonJS 提出来的这一种规范,有多个版本,在 ES6 当中已经纳入规范,原生支持 Promise 对象,非 ES6 环境可以用类似 Bluebird、Q 这类库来支持。
Promise 可以将回调变成链式调用写法,流程更加清晰,代码更加优雅。

简单归纳下 Promise:三个状态、两个过程、一个方法,快速记忆方法:3-2-1
三个状态:pendingfulfilledrejected
两个过程:1. pending→fulfilled(resolve); 2. pending→rejected(reject)
一个方法:then

// Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。
console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

异步编程中的结果怎么拿 —>promise

    // 使用promise的步骤:
    // a.构造函数
    // b.return 一个对象
    // c.对象有个 .then()
    // d. .then(()=>{})有传一个回调函数
    // e.promise((resolve))
    // f.resolve() 一调用就会触发外部的 .then的回调
    // 技巧: 一个异步就是一个Promise对象,调用resolve传递结果
     var p = new Promise((resolve,reject)=>{
     // e.promise((resolve))
     setTimeout(() => {
       var sum = 100
       resolve(sum)
     }, 0);
     })
     // c.对象有个 .then()
     // d. .then(()=>{})有传一个回调函数
     p.then((res)=>{
       // f.resolve() 一调用就会触发外部的 .then的回调
        console.log('.then 执行了--- ',res); 
     })
     
     //优点:解决了回调地狱的问题
     ajax('XXX1')
 	 .then(res => {
      // 操作逻辑
      return ajax('XXX2')
 	 }).then(res => {
      // 操作逻辑
      return ajax('XXX3')
 	 }).then(res => {
      // 操作逻辑
 	 })
    // promise问题:如果异步很多就会有很多个promise对象,无法取消Promise
    // 使用.then 获取结果就会很麻烦 ,错误需要通过回调函数来捕获

十二、async函数

async、await 是异步的终极解决方案
async 表示函数里有异步操作, await必须在async函数的上下文中,紧跟在后面的表达式需要等待结果(主要的意图是用来等待 Promise 对象的状态被 resolved,也就是为了异步操作同步化);
async 函数的返回值是 Promise 对象,可以使用then方法指定下一步操作(只有 async 函数内部的异步操作全部执行完,除非遇到 return 语句或者抛出错误,才会执行 then 方法指定的回调函数)

async function f() {
 await Promise.reject('出错了');
 await Promise.resolve('hello world'); // 不会执行
}

//解决办法:把 await 命令放在 try…catch 代码块中
async function f() {
 try {
 await Promise.reject('出错了');
 } catch(e) {
 }
 return await Promise.resolve('hello world');
}
f()
.then(v => console.log(v))
// hello world

//多个 await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发
// 写法一(推荐)
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

// 优秀写法
async function dbFuc(db) {
 let docs = [{}, {}, {}];
 let promises = docs.map((doc) => db.post(doc)); // 发送post请求
 let results = [];
 for (let promise of promises) {
 results.push(await promise);
 }
 console.log(results);
}

优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题

缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。

async function test() {
  // 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
  // 如果有依赖性的话,其实就是解决回调地狱的例子了
  await fetch('XXX1')
  await fetch('XXX2')
  await fetch('XXX3')
}
//下面来看一个使用 await 的例子:

let a = 0
let b = async () => {
  a = a + await 10
  console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1
//首先函数 b 先执行,在执行到 await 10 之前变量 a 还是 0,因为 await 内部实现了 generator ,generator 会保留堆栈中东西,所以这时候 a = 0 被保存了下来
//因为 await 是异步操作,后来的表达式不返回 Promise 的话,就会包装成 Promise.reslove(返回值),然后会去执行函数外的同步代码
//同步代码执行完毕后开始执行异步代码,将保存下来的值拿出来使用,这时候 a = 0 + 10
//上述解释中提到了 await 内部实现了 generator,其实 await 就是 generator 加上 Promise的语法糖,且内部实现了自动执行 generator。如果你熟悉 co 的话,其实自己就可以实现这样的语法糖。

一些ES6的API看这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Friday--星期五

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值