目录
- 前言
- Reflect API
- Proxy
- Proxy 与 defineProperty 的区别
- Symbols
- Generators
- generator处理异步代码
- Promise
- Async
- Module
- API介绍
前言
本教程将分为两大部分深入解读ES6的精髓。将带你领略ES6的基础语法,将深入探讨ES6的高级特性,通过浅显易懂的语言和大量精炼的代码示例,为你揭开现代JavaScript的神秘面纱。帮助你深入理解并掌握ES6的强大功能。无论你是初学者还是资深开发者,本教程都将是了解ES6、提升编程技能的宝贵资源,亦可作为你随时查阅的参考手册。
Reflect API
优点:
- 让Object操作都变成函数行为。
- 比如
name in obj
和delete obj[name]
,而Reflect.has(obj, name)
和Reflect.deleteProperty(obj, name)
让它们变成了函数行为。
- 比如
- Reflect对象的方法与Proxy对象的方法一一对应, 只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。
有了Reflect对象以后,很多操作会更易读。
常用语 转换成 Reflect语法:
let Obj = {
name: "jack",
gender: "男",
};
Obj.name; // Reflect.get(Obj,'name')
Obj.name = "proxyName"; // Reflect.set(Obj, 'name', 'proxyName');
"name" in Obj; // Reflect.has(Obj, 'name');
delete Obj.name; // Reflect.deleteProperty(Obj, 'name');
Object.keys(Obj) // Reflect.ownKeys(Obj); // ['name','gender']
Proxy
Proxy 可以理解成,在目标对象之前架设一层
“拦截”
,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。 , 结合上面Reflect的语法,我们可以函数化所有对象的操作
所有可以用的对象实例操作 , 对应的的代理操作
var handler =
{
// target.prop
get: ...,
// target.prop = value
set: ...,
// 'prop' in target
has: ...,
// delete target.prop
deleteProperty: ...,
// target(...args)
apply: ...,
// new target(...args)
construct: ...,
// Object.getOwnPropertyDescriptor(target, 'prop')
getOwnPropertyDescriptor: ...,
// Object.defineProperty(target, 'prop', descriptor)
defineProperty: ...,
// Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
// target.__proto__, object.isPrototypeOf(target), object instanceof target
getPrototypeOf: ...,
// Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
setPrototypeOf: ...,
// Object.keys(target)
ownKeys: ...,
// Object.preventExtensions(target)
preventExtensions: ...,
// Object.isExtensible(target)
isExtensible :...
}
部分代码实例
let target = function (name) {
this.name = name;
return "I am the target";
};
let handler = {
get: function (receiver, key) {
console.log("get:", receiver, key);
return Reflect.get(receiver, key);
},
set: function (receiver, key, value) {
console.log("set:", receiver, key, value);
return Reflect.set(receiver, key, value);
},
has: function (receiver, key) {
console.log("has:", receiver, key);
return Reflect.has(receiver, key);
},
deleteProperty: function (receiver, key) {
console.log("deleteProperty:", receiver, key);
return Reflect.deleteProperty(receiver, key);
},
// 函数调用
apply: function (receiver, ...args) {
console.log("apply:", receiver, args);
return Reflect.apply(receiver, ...args);
},
// 函数new
construct: function (receiver, ...args) {
console.log("construct:", receiver, args);
return Reflect.construct(receiver, ...args);
},
ownKeys: function (receiver) {
console.log("ownKeys:", receiver);
return Reflect.ownKeys(receiver);
},
};
//创建代理对象,后面所有的操作就对代理操作进行操作就行
let p = new Proxy(target, handler);
p.name;
p.name = "proxyName";
"name" in p;
delete p.name;
p("ziyu");
let p1 = new p("ziyu");
console.log(Object.keys(p));
Proxy 与 defineProperty 的区别
可以阅读下我之前写的 Object.defineProperty与Proxy对比【简单易懂】
Symbols
- ES5 的
对象属性名都是字符串,这容易造成属性名的冲突
。所以我们要保证每个属性的名字都是独一无二就成了发展的目标。 - ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol 值通过Symbol函数生成
。对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型
let s = Symbol('foo');
let s1 = Symbol('foo');
console.log(typeof s) // "symbol"
console.log(s === s1);
//有时,我们希望重新使用同一个 Symbol 值,Symbol.for()方法可以做到这一点。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
console.log(s1 === s2) // true
// Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。
Symbol.keyFor(s1) // "foo"
内置的 Symbol 值
- Symbol.hasInstance
- Symbol.isConcatSpreadable
- Symbol.species
- Symbol.match
- Symbol.replace
- Symbol.search
- Symbol.split
- Symbol.iterator
前文提到的 遍历器
- Symbol.toPrimitive
- Symbol.toStringTag
- Symbol.unscopables
Generators
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。,执行 Generator 函数会返回一个遍历器对象, 可以依次遍历 Generator 函数内部的每一个状态。
Generator 函数是一个
普通函数
,但是有两个特征。一是,function关键字与函数名之间有一个星号
;二是,函数体内部使用yield表达式
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
// 这是不是跟前文 遍历器 结构很像
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
Generator 函数可以不用yield表达式,这时就变成了一个单纯的暂缓执行函数。
function* f() {
console.log('执行了!')
}
var generator = f();
setTimeout(function () {
generator.next()
}, 2000);
generator处理异步代码
这里使用到了 Promise 来让代码拥有异步功能,Promise 概念下面会介绍到
function* asyncGenerator() {
yield Promise.resolve(1);
yield Promise.resolve(2);
yield Promise.resolve(3);
}
function useAsyncGenerator() {
for (const value of asyncGenerator()) {
value.then(e=>{
console.log(e);
})
}
}
useAsyncGenerator();
//1
//2
//3
Promise
Promise 是异步编程的一种解决方案
,比传统的解决方案——回调函数和事件
——更合理和更强大。ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
详细介绍见 这篇文章 手写Promise原理全过程【看了就会】
Async
- ES2017 标准引入了
async
函数,使得异步操作变得更加方便。- async 函数是什么?一句话,它就是 Generator 函数的语法糖。
- async函数就是将 Generator 函数的星号
(*)
替换成async,将yield
替换成await,仅此而已。
特点:
- Generator 函数的执行必须靠执行器,所以才有了
co
模块,而async函数
自带执行器 - 返回值是
Promise
。可以使用then方法添加回调函数 - 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
async function f() {
try {
await Promise.reject('出错了'); // 类似同步代码,异常能被捕获
} catch(e) {
}
return await Promise.resolve('hello world');
}
f().then(v => console.log(v)) // hello world
Module
- 在 ES6 之前,最主要的有
CommonJS
和AMD
两种。前者用于服务器,后者用于浏览器。- ES6 模块的设计思想是
尽量的静态化
,使得编译时就能确定模块的依赖关系,以及输入和输出的变量
。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
// CommonJS模块
let { stat } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
// ES6模块
import { stat } from 'fs';
上面代码的实质是从fs模块加载 stat 方法,其他方法不加载。
这种加载称为“编译时加载” 或者 静态加载
,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。由于 ES6 模块是编译时加载,使得静态分析成为可能。
API介绍
模块功能主要由两个命令构成:
export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
特点:
import
和export
命令只能在模块的顶层,不能在代码块之中 ,但是import()函数
可以在代码块中,支持动态加载模块
。
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}
// profile.js
let firstName = 'Michael';
let lastName = 'Jackson';
let year = 1958;
//变量导出
export { firstName, lastName, year };
//默认导出
export default function () {
console.log('foo');
}
//index.js
import defaultFn , { firstName, lastName, year } from './profile.js'
//这些导出的变量都是只读的不能修改
// firtName = "zs" // error