ECMAScript 6-11 概述
1. ECMA 介绍
ECMA(European Computer Manufacturers Association)是欧洲计算机制造商协会,目标是评估、开发和认可电信和计算机标准。1994年后改名为Ecma国际。
2. ECMAScript 是什么
ECMAScript 是由Ecma国际通过ECMA-262标准化的脚本程序设计语言。
3. ECMA-262 介绍
ECMA-262 是Ecma国际制定的一个标准,专门描述了ECMAScript语言的行为。
4. ECMA-262 历史
- 第1版:1997年,制定了语言的基本语法。
- 第2版:1998年,进行了较小改动。
- 第3版:1999年,引入正则、异常处理、格式化输出等。
- 第4版:2007年,过于激进,未发布。
- 第5版:2009年,引入严格模式、JSON,扩展对象、数组、原型、字符串、日期方法。
- 第6版:2015年,模块化、面向对象语法、Promise、箭头函数、let、const、数组解构赋值等。
- 第7版:2016年,幂运算符、数组扩展、Async/await关键字。
- 第8版:2017年,Async/await、字符串扩展。
- 第9版:2018年,对象解构赋值、正则扩展。
- 第10版:2019年,扩展对象、数组方法。
5. ES.next
从ES6开始,每年发布一个版本,版本号比年份最后一位大1。
6. 谁在维护ECMA-262
TC39(Technical Committee 39)是推进ECMAScript 发展的委员会,其会员包括苹果、谷歌、微软、因特尔等公司。
7. 为什么要学习ES6
- ES6版本变动内容最多,具有里程碑意义。
- ES6加入许多新的语法特性,编程实现更简单、高效。
- ES6是前端发展趋势,就业必备技能。
8. ES6 兼容性
可以通过 http://kangax.github.io/compat-table/es6/ 查看兼容性。
ECMAScript 6 新特性
1. let 关键字
- 不允许重复声明。
- 块级作用域。
- 不存在变量提升。
- 不影响作用域链。
2. const 关键字
- 声明必须赋初始值。
- 标识符一般为大写。
- 不允许重复声明。
- 值不允许修改。
- 块级作用域。
3. 变量的解构赋值
- 可以从数组和对象中提取值,对变量进行赋值。
示例
const arr = ['张学友', '刘德华', '黎明', '郭富城'];
let [zhang, liu, li, guo] = arr;
const lin = {
name: '林志颖',
tags: ['车手', '歌手', '小旋风', '演员']
};
let {name, tags} = lin;
// 复杂解构
let wangfei = {
name: '王菲',
age: 18,
songs: ['红豆', '流年', '暧昧', '传奇'],
history: [
{name: '窦唯'},
{name: '李亚鹏'},
{name: '谢霆锋'}
]
};
let {songs: [one, two, three], history: [first, second, third]} = wangfei;
4. 模板字符串
- 用反引号(`)标识。
- 字符串中可以出现换行符。
- 可以使用
${xxx}
形式输出变量。
示例
let str = `<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>`;
let star = '王宁';
let result = `${star}在前几年离开了开心麻花`;
5. 简化对象写法
- 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
示例
let name = '尚硅谷';
let slogon = '永远追求行业更高标准';
let improve = function () {
console.log('可以提高你的技能');
}
let atguigu = {
name,
slogon,
improve,
change() {
console.log('可以改变你')
}
};
6. 箭头函数
ES6 允许使用「箭头」(=>)定义函数。箭头函数与传统函数有许多不同之处,特别是在this
的绑定上。
箭头函数的语法
// 通用写法
let fn = (arg1, arg2, arg3) => {
return arg1 + arg2 + arg3;
}
// 省略小括号(仅当有一个参数时)
let fn2 = num => {
return num * 10;
};
// 省略花括号(函数体只有一条语句)
let fn3 = score => score * 20;
// this指向声明时所在作用域中this的值
let fn4 = () => {
console.log(this);
}
箭头函数的注意点
- 形参只有一个时,小括号可以省略。
- 函数体只有一条语句时,花括号可以省略,此时函数返回该条语句的执行结果。
- 箭头函数不会创建自己的
this
,而是继承外围(函数或全局)执行上下文中的this
值。 - 箭头函数不能作为构造函数使用,即不能使用
new
关键字调用。 - 箭头函数没有
arguments
对象,可以使用剩余参数代替。
应用场景
箭头函数非常适合用作回调函数,因为它们不会更改this
的指向,这让在回调函数中操作外围对象的属性变得非常简单和直接。
let school = {
name: '尚硅谷',
getName() {
let fn5 = () => {
console.log(this); // 此处的this指向school对象
}
fn5();
}
};
school.getName(); // 输出: 学校对象
7. Rest 参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
对象。
语法
function add(...args) {
console.log(args); // args是一个数组
}
add(1, 2, 3, 4, 5); // 输出: [1, 2, 3, 4, 5]
// rest 参数必须是最后一个形参
function minus(a, b, ...args) {
console.log(a, b, args);
}
minus(100, 1, 2, 3, 4, 5, 19); // 输出: 100 1 [2, 3, 4, 5, 19]
应用场景
Rest 参数非常适合不定个数参数函数的场景,能够很方便地收集所有的剩余参数。
8. Spread 扩展运算符
扩展运算符(spread)也是三个点(…)。它可以将一个数组或对象转换成用逗号分隔的参数序列,用于解包。
数组解包
let tfboys = ['德玛西亚之力', '德玛西亚之翼', '德玛西亚皇子'];
function fn() {
console.log(arguments);
}
fn(...tfboys); // 输出: ['德玛西亚之力', '德玛西亚之翼', '德玛西亚皇子']
对象解包
let skillOne = { q: '致命打击' };
let skillTwo = { w: '勇气' };
let skillThree = { e: '审判' };
let skillFour = { r: '德玛西亚正义' };
let gailun = { ...skillOne, ...skillTwo, ...skillThree, ...skillFour };
console.log(gailun); // 输出: { q: '致命打击', w: '勇气', e: '审判', r: '德玛西亚正义' }
9. Symbol
9.1 Symbol基本使用
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,类似于字符串。
Symbol特点
- Symbol的值是唯一的,用来解决命名冲突的问题。
- Symbol值不能与其他数据进行运算。
- Symbol定义的对象属性不能使用for…in循环遍历,但可以使用
Reflect.ownKeys
来获取对象的所有键名。
示例
// 创建 Symbol
let s1 = Symbol();
console.log(s1, typeof s1); // Symbol() "symbol"
// 添加标识的 Symbol
let s2 = Symbol('尚硅谷');
let s2_2 = Symbol('尚硅谷');
console.log(s2 === s2_2); // false
// 使用 Symbol.for 定义
let s3 = Symbol.for('尚硅谷');
let s3_2 = Symbol.for('尚硅谷');
console.log(s3 === s3_2); // true
9.2 Symbol内置值
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法,称为“魔术方法”,因为它们会在特定的场景下自动执行。
- Symbol.hasInstance:当其他对象使用
instanceof
运算符,判断是否为该对象的实例时,会调用这个方法。 - Symbol.isConcatSpreadable:对象的
Symbol.isConcatSpreadable
属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()
时,是否可以展开。 - Symbol.species:创建衍生对象时,会使用该属性。
- Symbol.match:当执行
str.match(myObject)
时,如果该属性存在,会调用它,返回该方法的返回值。 - Symbol.replace:当该对象被
str.replace(myObject)
方法调用时,会返回该方法的返回值。 - Symbol.search:当该对象被
str.search(myObject)
方法调用时,会返回该方法的返回值。 - Symbol.split:当该对象被
str.split(myObject)
方法调用时,会返回该方法的返回值。 - Symbol.iterator:对象进行
for...of
循环时,会调用Symbol.iterator
方法,返回该对象的默认遍历器。 - Symbol.toPrimitive:该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
- Symbol.toStringTag:在该对象上面调用
toString
方法时,返回该方法的返回值。 - Symbol.unscopables:该对象指定了使用
with
关键字时,哪些属性会被with
环境排除。
10 迭代器
遍历器(Iterator)是一种机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置。
- 第一次调用对象的
next
方法,指针自动指向数据结构的第一个成员。 - 接下来不断调用
next
方法,指针一直往后移动,直到指向最后一个成员。 - 每调用
next
方法返回一个包含value
和done
属性的对象。
原生具备iterator接口的数据
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
示例
let arr = ['a', 'b', 'c'];
for (let value of arr) {
console.log(value); // 输出 'a', 'b', 'c'
}
11 生成器
生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
function* gen() {
yield '一只没有耳朵';
yield '一只没有尾巴';
return '真奇怪';
}
let iterator = gen();
console.log(iterator.next()); // { value: '一只没有耳朵', done: false }
console.log(iterator.next()); // { value: '一只没有尾巴', done: false }
console.log(iterator.next()); // { value: '真奇怪', done: true }
12 Promise
Promise基础
Promise 是 ES6 引入的异步编程的新解决方案。它是一个构造函数,用来封装一个异步操作并可以获取其成功或失败的结果。
基本用法
let promise = new Promise(function(resolve, reject) {
// 异步操作
if (/* 异步操作成功 */) {
resolve(value); // 将Promise对象的状态从“pending”变为“fulfilled”,同时将异步操作的结果,作为回调函数的参数
} else {
reject(error); // 将Promise对象的状态从“pending”变为“rejected”,同时将异步操作报出的错误,作为回调函数的参数
}
});
promise.then(function(value) {
// 当Promise的状态变为fulfilled时调用
console.log(value);
}, function(error) {
// 当Promise的状态变为rejected时调用
console.log(error);
});
Promise特点
- 状态:Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
- 链式调用:then()和catch()方法返回的是Promise对象,支持链式调用。
- 改变Promise状态:Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。状态一旦改变,就不会再变,也就是说,Promise对象的状态改变是单向的。
Promise链式调用
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
Promise.all
Promise.all
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// 输出: [3, 42, "foo"]
Promise.race
Promise.race
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例,并以数组中,第一个完成的 Promise 实例的结果值,作为返回结果。
let promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// "two" —— 因为它比 promise1 完成得快
});
Promise应用场景
Promise 在处理异步操作、并发请求、定时器设置等方面有着广泛的应用,是现代前端开发中不可或缺的一部分。它让异步编程变得更加清晰和易于管理。
13. Set
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
集合的属性和方法:
- size:返回集合的元素个数。
- add(value):增加一个新元素,返回当前集合。
- delete(value):删除元素,返回boolean值。
- has(value):检测集合中是否包含某个元素,返回boolean值。
- clear():清空集合,返回undefined。
示例代码:
// 创建一个空集合
let s = new Set();
// 创建一个非空集合
let s1 = new Set([1,2,3,1,2,3]);
// 集合属性与方法
// 返回集合的元素个数
console.log(s1.size); // 3
// 添加新元素
console.log(s1.add(4)); // Set(4) {1, 2, 3, 4}
// 删除元素
console.log(s1.delete(1)); // true
// 检测是否存在某个值
console.log(s1.has(2)); // true
// 清空集合
console.log(s1.clear()); // undefined
console.log(s1.size); // 0
14. Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map也实现了iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
Map的属性和方法:
- size:返回Map的元素个数。
- set(key, value):增加一个新元素,返回当前Map。
- get(key):返回键名对象的键值。
- has(key):检测Map中是否包含某个元素,返回boolean值。
- clear():清空集合,返回undefined。
示例代码:
// 创建一个空 map
let m = new Map();
// 创建一个非空 map
let m2 = new Map([
['name','尚硅谷'],
['slogon','不断提高行业标准']
]);
// 属性和方法
// 获取映射元素的个数
console.log(m2.size); // 2
// 添加映射值
console.log(m2.set('age', 6)); // Map(3) {"name" => "尚硅谷", "slogon" => "不断提高行业标准", "age" => 6}
// 获取映射值
console.log(m2.get('age')); // 6
// 检测是否有该映射
console.log(m2.has('age')); // true
// 清除
console.log(m2.clear()); // undefined
console.log(m2.size); // 0
15. Class 类
ES6 提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
- class 声明类
- constructor 定义构造函数初始化
- extends 继承父类
- super 调用父级构造方法
- static 定义静态方法和属性
- 父类方法可以重写
示例代码
// 父类
class Phone {
// 构造方法
constructor(brand, color, price) {
this.brand = brand;
this.color = color;
this.price = price;
}
// 对象方法
call() {
console.log('我可以打电话!!!')
}
}
// 子类
class SmartPhone extends Phone {
constructor(brand, color, price, screen, pixel) {
super(brand, color, price);
this.screen = screen;
this.pixel = pixel;
}
// 子类方法
photo(){
console.log('我可以拍照!!');
}
playGame(){
console.log('我可以玩游戏!!');
}
// 方法重写
call(){
console.log('我可以进行视频通话!!');
}
// 静态方法
static run(){
console.log('我可以运行程序')
}
static connect(){
console.log('我可以建立连接')
}
}
// 实例化对象
const Nokia = new Phone('诺基亚', '灰色', 230);
const iPhone6s = new SmartPhone('苹果', '白色', 6088, '4.7inch','500w');
// 调用子类方法
iPhone6s.playGame();
// 调用重写方法
iPhone6s.call();
// 调用静态方法
SmartPhone.run();
16. 数值扩展
16.1. 二进制和八进制
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b
和0o
表示。
16.2. Number.isFinite() 与 Number.isNaN()
Number.isFinite()
用来检查一个数值是否为有限的。Number.isNaN()
用来检查一个值是否为NaN。
16.3. Number.parseInt() 与 Number.parseFloat()
ES6 将全局方法parseInt
和parseFloat
,移植到Number
对象上面,使用不变。
16.4. Math.trunc
用于去除一个数的小数部分,返回整数部分。
16.5. Number.isInteger
Number.isInteger()
用来判断一个数值是否为整数。
17. 对象扩展
ES6 新增了一些Object
对象的方法。
- Object.is:比较两个值是否严格相等,与
===
行为基本一致(+0 与NaN)。 - Object.assign:对象的合并,将源对象的所有可枚举属性,复制到目标对象。
- proto、setPrototypeOf、Object.setPrototypeOf:可以直接设置对象的原型。
18. 模块化
18.1. 模块化的好处
模块化的优势有以下几点:
- 防止命名冲突
- 代码复用
- 高维护性
18.2. 模块化规范产品
ES6 之前的模块化规范有:
- CommonJS => NodeJS、Browserify
- AMD => requireJS
- CMD => seaJS
18.3. ES6 模块化语法
模块功能主要由两个命令构成:export
和import
。
export
命令用于规定模块的对外接口。import
命令用于输入其他模块提供的功能。
ECMASript 7 新特性
1. Array.prototype.includes
Array.prototype.includes
方法用来检测数组中是否包含某个元素,返回布尔类型值。
let arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true
console.log(arr.includes(6)); // false
2. 指数操作符
在ES7中引入指数运算符 **
,用来实现幂运算,功能与 Math.pow
结果相同。
console.log(2 ** 3); // 8
console.log(Math.pow(2, 3)); // 8
ECMASript 8 新特性
1. async和await
async
和 await
两种语法结合可以让异步代码像同步代码一样。
1.1. async函数
async
函数的返回值为Promise
对象。Promise
对象的结果由async
函数执行的返回值决定。
async function fetchData() {
return 'Data';
}
fetchData().then(data => console.log(data)); // 输出:Data
1.2. await表达式
await
必须写在async
函数中。await
右侧的表达式一般为Promise
对象。await
返回的是Promise
成功的值。- 如果
await
的Promise
失败了,就会抛出异常,需要通过try...catch
捕获处理。
async function fetchData() {
try {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch error:', error);
}
}
fetchData();
2. Object.values和Object.entries
2.1. Object.values()
Object.values()
方法返回一个给定对象的所有可枚举属性值的数组。
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.values(obj)); // [1, 2, 3]
2.2. Object.entries()
Object.entries()
方法返回一个给定对象自身可遍历属性 [key, value]
的数组。
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]
2.3. Object.getOwnPropertyDescriptors
Object.getOwnPropertyDescriptors()
方法返回指定对象所有自身属性的描述对象。
const obj = {
a: 1,
get b() { return 2; },
};
console.log(Object.getOwnPropertyDescriptors(obj));
// 输出对象属性的描述符
ECMASript 9 新特性
1.Rest/Spread 属性
Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,
在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符
function connect({host, port, ...user}) {
console.log(host);
console.log(port);
console.log(user);
}
connect({
host: '127.0.0.1',
port: 3306,
username: 'root',
password: 'root',
type: 'master'
});
2. 正则表达式命名捕获组
ES9允许命名捕获组使用符号『?<name>
』,这样获取捕获结果可读性更强。
let str = '<a href="http://www.atguigu.com">尚硅谷</a>';
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result = reg.exec(str);
console.log(result.groups.url);
console.log(result.groups.text);
3. 正则表达式反向断言
ES9支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。
// 声明字符串
let str = 'JS5211314你知道么555啦啦啦';
// 正向断言
const reg1 = /\d+(?=啦)/;
const result1 = reg1.exec(str);
// 反向断言
const reg2 = /(?<=么)\d+/;
const result2 = reg2.exec(str);
console.log(result2);
4. 正则表达式dotAll模式
正则表达式中点.
匹配除回车外的任何单字符,标记『s
』改变这种行为,允许行终止符出现。
let str = `
<ul>
<li>
<a>肖生克的救赎</a>
<p>上映日期: 1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期: 1994-07-06</p>
</li>
</ul>`;
// 声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
// 执行匹配
let data = [];
let result;
while (result = reg.exec(str)) {
data.push({title: result[1], time: result[2]});
}
// 输出结果
console.log(data);
ECMAScript 10 新特性
- Object.fromEntries
- trimStart和trimEnd
- Array.prototype.flat 与 flatMap
- Symbol.prototype.description
ECMAScript 11 新特性
- String.prototype.matchAll
- 类的私有属性
- Promise.allSettled
- 可选链操作符
- 动态import导入
- globalThis对象