ES6
ES6(ECMAScript2015)即ECMAScript的2015年颁布的标准,新标准内容主要是js的语法糖,方便开发者更好的编写js,使开发更加工程性
阮一峰ES6教程:https://es6.ruanyifeng.com/
变量
变量声明
ES5使用var的声明变量,具有3个特点
- 可以重复声明
- 没有块级作用域
- 不能限制修改
因此,ES6为让变量更可控制,完善了声明变量
let
- 声明的变量名不允许重复声明(同一作用域下)
- let声明变量支持块级作用域
const
- 声明常量,不可被修改
- 常量声明时必须初始化
- 具备let所有特性
注:
块级作用域:即语法块(花括号内),每次执行括号内,变量都在独立的作用域中
预解析:let,const声明的变量不会被预解析
解构赋值
数组的解构赋值
let [a,b,c] = [1,2,3]; //1,2,3
一个变量数组 = 一个值数组,以逗号分隔后相互对应项赋值;支持设置默认值;
对象的解构赋值
let {a,b,c} = {a:1,b:2,c:3}; //1,2,3
变量对象 = 变量值对象,不存在声明顺序问题;支持变量新别名对值接收
允许使用默认值
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
var {x, y = 5} = {x: 1};
其他用法
let { cos,sin,random } = Math; //func,func,func;将对象值赋予环境变量
let [a,b,c,d,e,f] = "hello"; //h,e,l,l,o,undefined解构字符串
import { data, request } from 'request' //解构引入模块的成员
函数
参数默认值
function getNum(outNum = 0){//支持对函数形参赋予默认值
return outNum;
};
箭头函数
写法简洁
let getSum = (a,b) => console.log(a + b);
// 一个参数可省去括号,一行代码可省去花括号
this指向
var obj = {show: () => console.log(this);}
doucment.onclick = obj.show;
箭头函数特点:
- this指向:是定义时所在的对象,而不是使用时所在的对象。
- 由于其this特殊性,不允许使用 new 操作符使其提升为构造函数
- 没有默认的 arguments 对象
字符串
方法
includes(str,index)
判断字符串是否包含参数指定字符串,index为起始检索位
startsWith(str)
判断字符串是否以参数指定字符串开头
endsWith(str)
判断字符串是否以参数指定字符串结束
模板字符串
ES5
var data = {name: 'page',age: '20'};
var html = '<p>姓名:'+ data.name +'</p><p>年龄'+ data.age +'</p>';
ES6
let html = `
<p>姓名:${data.name}</p>
<p>年龄:${data.age}</p>`;
拓展运算符
使用 ...
rest参数(剩余参数转成数组)
function test(multiNum,...numbers){//独立参转为数组
return multiNum * numbers.reduce(function(prev,next,index,arr){
return prev * next
})
};
延伸
let arr = [2,2,3];
console.log(test(...arr));//将数组拓展,把数组项转为多个独立参数
let arr2 = [4,4,5];
console.log([...arr,...arr2]); //将两个数组拆分后并至一个数组
// 箭头函数内不支持访问arguments参数对象,可通过拓展运算符实现
let getOPtions = (...arguments) => console.log(arguments);
对象
let data = {person: {name: 'page',age: 20, gender: '男'...}};
let res = {...data}; // 快速浅拷贝
let info = {...data.person}; // 拷贝部分
对象
增强写法
const app = {
height,
width,
render(options){ ... }
}
属性名表达式:对象声明的属性是环境下变量值
es5
var str = 'collected';
var type = {};
type[str] = true;
es6
var str = 'collected';
var type = {[str]: true};
Class类
ES5:创建类使用构造函数,给构造函数prototype添加方法
ES6:class关键字声明类,constructor声明构造函数,内部声明原型方法
class dropdown{
static defaults = { options : true } // 静态属性/方法
loaded = "loaded" // 声明实例默认属性(公开字段声明)
constructor ($ele,options){ // 构造函数
this.$ele = $ele;
this.options = options;
}
// 原型方法
show(){
this.$ele.showHide("show");
}
hide(){
this.$ele.showHide("hide");
}
get value(){
return "value"
}
set value(val){
this._value = val;
}
// 私有属性/方法
#privateAttr = "value";
#getPrivateAttr(){
return this.#privateAttr;
}
};
class继承
class Dog extends Animals {
constructor(name,type){
super(name); //调用父类构造函数
}
// 原型方法自动继承父类原型
};
class类特点
- 内部定义的所有方法不可枚举,如constructor,getName,getColor
- 类必须使用new调用,不能如es5中构造函数可单独调用
- 类声明不会预解析提升,原因是类的继承必须保证子类在父类之后定义
super关键字
类初始化(constructor)内,调用父类构造函数以实现继承
类中调用,访问类的属性/静态方法
实例方法中调用,访问实例原型的属性/方法
Promise
Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。可以理解为承诺将异步返回结果传递给关联处理程序,实现编写类似同步的代码进行异步操作;
状态
-
pending 待定的初始状态
-
fullfilled 操作成功
-
rejected 操作失败
resolve与reject
const preloadImage = function (path) {
const image = new Image();
image.onload = resolve;
return new Promise(function (resolve, reject) {
image.onerror = reject;
image.src = path;
});
};
preloadImage('/images/eg.png').then(
() => console.log('success'), // 作为参数resolve回调函数
() => console.log('failed') // 作为参数reject回调函数
)
Primise.all
注意
- Promise内部会在创建时立马执行,resolve/reject并不会阻塞代码执行
- resolve/reject回调仅会在循环队列,即不会马上执行
手写Promise
异步操作回调 => Promise实例记录 => 执行对应then回调
class Ipromise {
constructor(asycback){
this.status = 'pending'; // 初始状态
this.value = undefined; // fullfilled结果
this.result = undefined; // rejected结果
this.fullfilledCallbacks = []; // fullfilled状态下回调集合
this.rejectedCallbacks = []; // rejected状态下回调集合
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
try{
asycback(this.resolve,this.reject); // 代理执行,完成后更新状态
}catch(e){this.reject(e)}
}
reject(result){
if(this.status === 'pending'){
this.status = 'rejected';
this.result = result;
this.rejectedCallbacks.forEach(function(callback){
callback(result);
})
}
}
resolve(store){
if(this.status === 'pending'){
this.status = 'fullfilled';
this.value = store;
this.fullfilledCallbacks.forEach(function(callback){
callback(store);
})
}
}
then(fullfilled,rejected){
switch(this.status){
case 'pending' :
this.fullfilledCallbacks.push(fullfilled);
this.rejectedCallbacks.push(rejected);
break;
case 'fullfilled': fullfilled(this.store);
break;
case 'rejected': rejected(this.result);
break;
}
}
}
new Ipromise(function(resolve,reject){
setTimeout(function(){
resolve(2)
},2000);
}).then(
function(data){console.log(data)},
function(error){console.log(error)},
)
数组
Array.from(list,mapFn)
- 将类数组(Nodelist等)与可遍历对象转为Array类型
- mapFn为转数组前对数组项的处理
Array.of(item...)
- 将一组值转化为数组
- es5我们常用[].slice.call(arguments)
Array.find/findIndex(func,func的this指向对象)
- 返回满足查找条件的第一个索引项/索引
- 未找到返回undefined/-1
for快速遍历
循环索引值
for(let i in this.list){
console.log(i + ': ' + this.list[i]);
};
循环数组项
for(let item of this.list){
console.log('数组项: ' + item)
};
兼容ES6
es6大部分新特性需要IE10+,兼容低版本浏览器时可使用babel编译为es5代码
Babel
初始化npm
npm init
安装babel编译器,bebel cli命令工具,babel编译相关参数
npm i @babel/core @babel/cli @babel/preset-env
新建.babelrcl配置文件
{
"presets": [
"@babel/env",
"@babel/preset-react"
],
"plugins": []
}
执行编译输出命令
babel src -d dist // 将src目录的js编译至dist目录下
polyfill
问题
Babel默认只转换新的JavaScript语法(语法糖),并不会转换API(如:Symbol、Promise),以及全局对象上的方法,如Object.assign
解决:自动补丁
安装
npm install --save @babel/polyfill
项目根目录创建babel.config.json
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
"useBuiltIns": "usage",
"corejs": "3.6.5",
}
]
]
}
Symbol类型
symbol是一种基本数据类型。Symbol()函数会返回symbol类型的值,一个symbol值能作为对象属性的标识符。这意味着对象属性类型包括字符串属性和Symbol标识的属性两种;
创建
let id = Symbol();
let info = Symbol('info'); //参数仅是对该symbol的描述
使用
let obj = {
[id]: 2020, //使用symbol标识唯一属性,声明需以方括号包裹
id: '2020', //普通的字符串属性'id'
};
访问symbol属性,必须通过symbol访问
console.log(obj[id],obj.id); //2020,'2020'
Object.getOwnPropertySymbols(obj)
- 返回symbol属性组成的数组,用于遍历symbol属性
Set对象
与Array相同,Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。
let s = new Set();
let sets = new Set([0,{},[1,2],'a str']);
sets.add(0);
sets.has(-1); //flase
sets.size(); //length
set.delete(0);
数组去重
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]);
支持foreach遍历
Map对象
Map对象保存键值对,允许任何值(对象或者原始值) 都可以作为一个键或一个值(映射),并且能够记住键的原始插入顺序。
let myMap = new Map([[2,'two'],[3,'three']]);
myMap.set(0, "zero");
myMap.set(1, "one");
for (let [key, value] of myMap) {
console.log(key + " = " + value);
}
async/await函数
以同步写法编写异步处理,比Promise更简洁,与Promise密切相关
返回Promsie
async function foo() {
return 1; // 等同于return Promise.resolve(1)
}.then(res => console.log(res));
await
async function foo() { // await终止函数内执行,后续代码被放在then回调
await 1; // Promise.resolve(1).then(
await 2; // reuturn Promise.resolve(2)
} // )
Proxy代理器
Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
创建
const p = new Proxy(target, handler) //返回一个代理器
// target为代理的对象实例,handle对象即所有代理操作
// 对代理器的操作自动handler
Generator函数
遍历器生成函数,返回遍历器对象;ES6 提供的一种异步编程解决方案;语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,定义不同的内部状态。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
// 该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false } // done即完成状态
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
next方法
function* f() {
for(var i = 0; true; i++) {
var reset = yield i; // i 将被作为next()返回值,next参数将会作为yield返回值
if(reset) { i = -1; }
}
}
var g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }
for…of循环,无需next
function* fibonacci() {
let [prev, curr] = [0, 1];
for (;;) {
yield curr;
[prev, curr] = [curr, prev + curr];
}
}
for (let n of fibonacci()) {
if (n > 1000) break; // 执行次数由外部决定
console.log(n);
}
function* objectEntries(obj) {
let propKeys = Reflect.ownKeys(obj);
for (let propKey of propKeys) { // 执行次数由内部决定
yield [propKey, obj[propKey]];
}
}
let jane = { first: 'Jane', last: 'Doe' };
for (let [key, value] of objectEntries(jane)) {
console.log(`${key}: ${value}`);
}
// 另一个写法
jane[Symbol.iterator] = objectEntries; // Generator遍历器生成函数生成对象性遍历器
for (let [key, value] of jane) {
console.log(`${key}: ${value}`);
}
Generator.prototype.throw
var g = function* () {
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
};
var i = g();
i.next();
try {
i.throw('a'); // 向g的构造器函数内抛出错误,传递值为a
i.throw('b'); // 继续抛出错误,此时g内部执行完成,向外抛出
} catch (e) {
console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b
Generator.prototype.return
返回给定的值,并提前终结遍历器函数
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
Generator应用
状态机:避免声明外部变量去记忆状态就能显示多状态切换
var clock = function* () {
while (true) {
console.log('Tick!');
yield;
console.log('Tock!');
yield;
}
};
回调函数:
// 请求处理逻辑
function* main() {
var result = yield request("http://some.url");
var resp = JSON.parse(result);
console.log(resp.value);
}
// 请求方法
function request(url) {
makeAjaxCall(url, function(response){
it.next(response);
});
}
var it = main();
it.next();
let steps = [step1Func, step2Func, step3Func];
function* iterateSteps(steps){
for (var i=0; i< steps.length; i++){
var step = steps[i];
yield step();
}
}
Module模块
相对已有的CommonJS、AMD模块化规范,新增ES6 Module(按引用传递)
export
基本使用
export var num = 0;
export const Temp = 'xx';
export function func() {};
// 等同于
export { num, Temp, func }
// 继承
export * from './module1.js';
export * from './module2.js';
export default
// func模块
// export default唯一性, 但支持与具名导出并存
export default function func() {}
// 引用
import func from './func.js';
as关键字
const value = 'xxx';
export {
value as outName
...
}
export { default as AppMain } from './AppMain';
// 或
export { default } from './xxx.js';
import
基本使用
import './script.js';
import { num } from './xxx.js';
// 注意:num应该作为只读,由于按引用传值,修改是危险操作
num++; // warn dangerous
as关键字
import { vaule as A } from './xxx.js';
import * as api from './api.js';
import default
import module1, { childModule1 } from "./main.js";
运行时加载
// 与require相像
import('./xx.js');
if(true) import('./xx.js');
import(getModulePath(params));
// promise
import('./xx.js').then(({export1, export2}) => console.log('imported!'));
new Promise.all([import('./module1.js'), import('./module2.js')]);
Decorator装饰器
装饰器(Decorator)用来增强 JavaScript 类(class)功能的函数,支持装饰4种类型值:
-
类(class)
-
类的属性
-
类的方法
-
属性存储器(accessor)
声明装饰器
type Decorator = (value: Input, context: {
kind: string; // 装饰类型:class method getter setter ...
name: string | symbol; // 装饰值名称
access: { // 访问值的存取器
get?(): unknown;
set?(value: unknown): void;
};
private?: boolean; // 是否为私有
static?: boolean; // 是否为静态
addInitializer?(initializer: () => void): void; // 初始化值时执行一些处理
}) => Output | void;
装饰器参数
value
:所装饰的值,可能是 undefined
(装饰一个未定义值属性时)
context
:上下文对象
类装饰器
function testable(isTestable) {
return function(target) {
target.isTestable = isTestable;
}
}
@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true
@testable(false)
class MyClass {}
MyClass.isTestable // false
方法装饰器
function logged(value, { kind, name }) {
if (kind === "method") {
return function (...args) {
console.log(`starting ${name} with arguments ${args.join(", ")}`);
const ret = value.call(this, ...args);
console.log(`ending ${name}`);
return ret;
};
}
}
class C {
@logged
m(arg) {}
}
new C().m(1);
存储器装饰器
同方法装饰器,本质对getter/setter方法装饰
属性的装饰
属性装饰器的第一个参数是undefined
,即不输入值。用户可以选择让装饰器返回一个初始化函数,当该属性被赋值时,这个初始化函数会自动运行,它会收到属性的初始值,然后返回一个新的初始值
function logged(value, { kind, name }) {
if (kind === "field") {
return function (initialValue) {
console.log(`initializing ${name} with value ${initialValue}`);
return initialValue;
};
}
// ...
}
class C {
@logged x = 1;
}
new C();
core-decoraors.js
一个提供常见装饰器的第三方模块,如 readonly
等