从ES6到ES10的新特性万字大总结
介绍
ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)在标准ECMA-262中定义的脚本语言规范。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际上后两者是ECMA-262标准的实现和扩展。历史版本
至发稿日为止有九个ECMA-262版本发表。其历史版本如下:1997年6月:第一版
1998年6月:修改格式,使其与ISO/IEC16262国际标准一样
1999年12月:强大的正则表达式,更好的词法作用域链处理,新的控制指令,异常处理,错误定义更加明确,数据输出的格式化及其它改变
2009年12月:添加严格模式("use strict")。修改了前面版本模糊不清的概念。增加了getters,setters,JSON以及在对象属性上更完整的反射。
2011年6月:ECMAScript标5.1版形式上完全一致于国际标准ISO/IEC 16262:2011。
2015年6月:ECMAScript 2015(ES2015),第 6 版,最早被称作是 ECMAScript 6(ES6),添加了类和模块的语法,其他特性包括迭代器,Python风格的生成器和生成器表达式,箭头函数,二进制数据,静态类型数组,集合(maps,sets 和 weak maps),promise,reflection 和 proxies。作为最早的 ECMAScript Harmony 版本,也被叫做ES6 Harmony。
2016年6月:ECMAScript 2016(ES2016),第 7 版,多个新的概念和语言特性。
2017年6月:ECMAScript 2017(ES2017),第 8 版,多个新的概念和语言特性。
2018年6月:ECMAScript 2018 (ES2018),第 9 版,包含了异步循环,生成器,新的正则表达式特性和 rest/spread 语法。
2019年6月:ECMAScript 2019 (ES2019),第 10 版。
发展标准
TC39(Technical Committee 39)是一个推动JavaScript发展的委员会,它的成语来自各个主流浏览器的代表成语。会议实行多数决,每一项决策只有大部分人同意且没有强烈反对才能去实现。TC39成员制定着ECMAScript的未来。
每一项新特性最终要进入到ECMAScript规范里,需要经历5个阶段,这5个阶段如下:
Stage 0: Strawperson
只要是TC39成员或者贡献者,都可以提交想法
Stage 1: Proposal
这个阶段确定一个正式的提案
Stage 2: draft
规范的第一个版本,进入此阶段的提案大概率会成为标准
Stage 3: Candidate
进一步完善提案细则
Stage 4: Finished
表示已准备好将其添加到正式的ECMAScript标准中
由于ES6以前的属性诞生年底久远,我们使用也比较普遍,遂不进行说明,ES6之后的语言风格跟ES5以前的差异比较大,所以单独拎出来做个记录。
ES6(ES2015)
ES6是一次重大的革新,比起过去的版本,改动比较大,本文仅对常用的API以及语法糖进行讲解。Let 和 Const
在ES6以前,JS只有var一种声明方式,但是在ES6之后,就多了let跟const这两种方式。用var定义的变量没有块级作用域的概念,而let跟const则会有,因为这三个关键字创建是不一样的。区别如下:
{
var a = 10
let b = 20
const c = 30
}
a // 10
b // Uncaught ReferenceError: b is not defined
c // c is not defined
let d = 40
const e = 50
d = 60
d // 60
e = 70 // VM231:1 Uncaught TypeError: Assignment to constant variable.var let const
var let const 变量提升 √ × × 全局变量 √ × × 重复声明 √ × × 重新赋值 √ √ × 暂时死区 × √ √ 块作用域 × √ √ 只声明不初始化 √ √ × 类(Class)
在ES6之前,如果我们要生成一个实例对象,传统的方法就是写一个构造函数,例子如下:1 function Person(name, age) {
2 this.name = name
3 this.age = age
4 }
5 Person.prototype.information = function () {
6 return 'My name is ' + this.name + ', I am ' + this.age
7 }但是在ES6之后,我们只需要写成以下形式:
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
information() {
return 'My name is ' + this.name + ', I am ' + this.age
}
}箭头函数(Arrow function)
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或 new.target。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。在ES6以前,我们写函数一般是:
var list = [1, 2, 3, 4, 5, 6, 7]
var newList = list.map(function (item) {
return item * item
})但是在ES6里,我们可以:
const list = [1, 2, 3, 4, 5, 6, 7]
const newList = list.map(item => item * item)看,是不是简洁了不少
函数参数默认值(Function parameter defaults)
在ES6之前,如果我们写函数需要定义初始值的时候,需要这么写:function config (data) {
var data = data || 'data is empty'
}这样看起来也没有问题,但是如果参数的布尔值为falsy时就会出问题,例如我们这样调用config:
config(0)
config('')那么结果就永远是后面的值
如果我们用函数参数默认值就没有这个问题,写法如下:
const config = (data = 'data is empty') => {}
模板字符串(Template string)
在ES6之前,如果我们要拼接字符串,则需要像这样:var name = 'kris'
var age = 24
var info = 'My name is ' + this.name + ', I am ' + this.age但是在ES6之后,我们只需要写成以下形式:
const name = 'kris'
const age = 24
const info = `My name is ${name}, I am ${age}`解构赋值(Destructuring assignment)
我们通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。比如我们需要交换两个变量的值,在ES6之前我们可能需要:
var a = 10
var b = 20
var temp = a
a = b
b = temp但是在ES6里,我们有:
let a = 10
let b = 20
[a, b] = [b, a]是不是方便很多
模块化(Module)
在ES6之前,JS并没有模块化的概念,有的也只是社区定制的类似CommonJS和AMD之类的规则。例如基于CommonJS的NodeJS:// circle.js
// 输出
const { PI } = Math
exports.area = (r) => PI * r ** 2
exports.circumference = (r) => 2 * PI * r// index.js
// 输入
const circle = require('./circle.js')
console.log(`半径为 4 的圆的面积是 ${circle.area(4)}`)在ES6之后我们则可以写成以下形式:
// circle.js
// 输出
const { PI } = Math
export const area = (r) => PI * r ** 2
export const circumference = (r) => 2 * PI * r// index.js
// 输入
import {
area
} = './circle.js'
console.log(`半径为 4 的圆的面积是: ${area(4)}`)扩展操作符(Spread operator)
扩展操作符可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。比如在ES5的时候,我们要对一个数组的元素进行相加,在不使用reduce或者reduceRight的场合,我们需要:
function sum(x, y, z) {
return x + y + z;
}
var list = [5, 6, 7]
var total = sum.apply(null, list)但是如果我们使用扩展操作符,只需要如下:
const sum = (x, y, z) => x + y + z
const list = [5, 6, 7]
const total = sum(...list)非常的简单,但是要注意的是扩展操作符只能用于可迭代对象
如果是下面的情况,是会报错的:
var obj = {'key1': 'value1'}
var array = [...obj] // TypeError: obj is not iterable对象属性简写(Object attribute shorthand)
在ES6之前,如果我们要将某个变量赋值为同样名称的对象元素,则需要:var cat = 'Miaow'
var dog = 'Woof'
var bird = 'Peet peet'var someObject = {
cat: cat,
dog: dog,
bird: bird
}但是在ES6里我们就方便很多:
let cat = 'Miaow'
let dog = 'Woof'
let bird = 'Peet peet'let someObject = {
cat,
dog,
bird
}console.log(someObject)
//{
// cat: "Miaow",
// dog: "Woof",
// bird: "Peet peet"
//}非常方便
Promise
Promise 是ES6提供的一种异步解决方案,比回调函数更加清晰明了。Promise 翻译过来就是承诺的意思,这个承诺会在未来有一个确切的答复,并且该承诺有三种状态,分别是:
等待中(pending)
完成了 (resolved)
拒绝了(rejected)
这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了,也就是说一旦状态变为 resolved 后,就不能再次改变new Promise((resolve, reject) => {
resolve('success')
// 无效
reject('reject')
})当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('finifsh')
// new Promise -> finifshPromise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
Promise.resolve(1)
.then(res => {
console.log(res) // => 1
return 2 // 包装成 Promise.resolve(2)
})
.then(res => {
console.log(res) // => 2
})当然了,Promise 也很好地解决了回调地狱的问题,例如:
ajax(url, () => {
// 处理逻辑
ajax(url1, () => {
// 处理逻辑
ajax(url2, () => {
// 处理逻辑
})
})
})
可以改写成:ajax(url)
.then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => console.log(res))
for...of
for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。例子如下:
const array1 = ['a', 'b', 'c'];
for (const element of array1) {
console.log(element)
}// "a"
// "b"
// "c"
Symbol
symbol 是一种基本数据类型,Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。
例子如下:
const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');console.log(typeof symbol1); // "symbol"
console.log(symbol3.toString()); // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')); // false
迭代器(Iterator)/ 生成器(Generator)
迭代器(Iterator)是一种迭代的机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要内部有 Iterator 接口,就可以完成依次迭代操作。一旦创建,迭代器对象可以通过重复调用next()显式地迭代,从而获取该对象每一级的值,直到迭代完,返回{ value: undefined, done: true }
虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。生成器函数提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用 function*语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator的迭代器。 通过调用生成器的下一个方法消耗值时,Generator函数将执行,直到遇到yield关键字。
可以根据需要多次调用该函数,并且每次都返回一个新的Generator,但每个Generator只能迭代一次。
所以我们可以有以下例子:
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
for (let i = start; i < end; i += step) {
yield i;
}
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}
Set/WeakSet
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。所以我们可以通过Set实现数组去重
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)])
// [2, 3, 4, 5, 6, 7, 32]
WeakSet 结构与 Set 类似,但区别有以下两点:WeakSet 对象中只能存放对象引用, 不能存放值, 而 Set 对象都可以。
WeakSet 对象中存储的对象值都是被弱引用的, 如果没有其他的变量或属性引用这个对象值, 则这个对象值会被当成垃圾回收掉. 正因为这样, WeakSet 对象是无法被枚举的, 没有办法拿到它包含的所有元素。
所以代码如下:var ws = new WeakSet()
var obj = {}
var foo = {}ws.add(window)
ws.add(obj)ws.has(window) // true
ws.has(foo) // false, 对象 foo 并没有被添加进 ws 中ws.delete(window) // 从集合中删除 window 对象
ws.has(window) // false, window 对象已经被删除了ws.clear() // 清空整个 WeakSet 对象
Map/WeakMap
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。例子如下,我们甚至可以使用NaN来作为键值:
var myMap = new Map();
myMap.set(NaN, "not a number");myMap.get(NaN); // "not a number"
var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。跟Map的区别与Set跟WeakSet的区别相似,具体代码如下:
var wm1 = new WeakMap(),
wm2 = new WeakMap(),
wm3 = new WeakMap();
var o1 = {},
o2 = function(){},
o3 = window;wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value可以是任意值,包括一个对象
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个WeakMap对象
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2中没有o2这个键
wm2.get(o3); // undefined,值就是undefinedwm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是undefined)wm3.set(o1, 37);
wm3.get(o1); // 37
wm3.clear();
wm3.get(o1); // undefined,wm3已被清空
wm1.has(o1); // true
wm1.delete(o1);
wm1.has(o1); // false
Proxy/Reflect
Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 Proxy 的方法相同。Reflect不是一个函数对象,因此它是不可构造的。
Proxy跟Reflect是非常完美的配合,例子如下:
const observe = (data, callback) => {
return new Proxy(data, {
get(target, key) {
return Reflect.get(target, key)
},
set(target, key, value, proxy) {
callback(key, value);
target[key] = value;
return Reflect.set(target, key, value, proxy)
}
})
}const FooBar = { open: false };
const FooBarObserver = observe(FooBar, (property, value) => {
property === 'open' && value
? console.log('FooBar is open!!!')
: console.log('keep waiting');
});
console.log(FooBarObserver.open) // false
FooBarObserver.open = true // FooBar is open!!!
当然也不是什么都可以被代理的,如果对象带有configurable: false 跟writable: false 属性,则代理失效。Regex对象的扩展
正则新增符号
i 修饰符// i 修饰符
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
y修饰符// y修饰符
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]r1.exec(s) // ["aa"]
r2.exec(s) // null
String.prototype.flags// 查看RegExp构造函数的修饰符
var regex = new RegExp('xyz', 'i')
regex.flags // 'i'
unicode模式var s = '
从ES6到ES10的新特性万字大总结的更多相关文章
- 从 ES6 到 ES10 的新特性万字大总结
以下文章来源于鱼头的Web海洋 ,作者陈大鱼头 鱼头的Web海洋 一个名为Web的海洋世界 (给前端大全加星标,提升前端技能) 作者:鱼头的Web海洋 公号 / 陈大鱼头 (本文来自作者投稿) 介 ...
- Atitit mac os 版本 新特性 attilax大总结
Atitit mac os 版本 新特性 attilax大总结 1. Macos概述1 2. 早期2 2.1. Macintosh OS (系统 1.0) 1984年2 2.2. Mac OS 7. ...
- atitit.atiLinq v2新特性attilax大总结 q326
atitit.atiLinq v2新特性attilax大总结 q326 1. V3规划 (分开sql2obj sql2sql sql2xml)1 2. V2新特性 Url linq的定义1 3. V1 ...
- es6/es7/es8常用新特性总结(超实用)
本文标题有误导性,因为我其实想写node8的新特性,说实话一下子从node v1.x跳跃到node 8.x+ 真有点受宠若惊的感觉.一直觉得node 数组. 对象.序列等的处理没有python方便,因 ...
- Redis4.0新特性之-大KEY删除
接上一篇,我们得知了redis中存在大KEY,那么这个大KEY如何删除呢?本文将从源码角度分析Redis4.0带来的新特性. 在Redis中,对于大KEY的删除一直是个比较头疼的问题,为了不影响服务, ...
- ES6中的一些新特性
这两个命令是ES6的新语法知识.这两个新的特性解决了ES6中的一些小的"bug"问题.其中包含一些知识:块级作用域.let命令.const命令.全局对象的属性.Google V8引 ...
- es6中的部分新特性
1.es6中变量声明可以使用let声明变量,用const声明常量.例: test:function(){ { var num=10; let num1=11; const num2=12; } con ...
- ES6语法:函数新特性(一)
ES6 函数 引言: 函数在任何语言中偶读很重要,java里面的函数通常叫做方法,其实是一个东西,使用函数可以简化更多的代码,代码结构看着更加清晰.今天我们来学学ES6语法中,函数有什么变化. 虽然现 ...
- ES6那些事半功倍的新特性(一)
数组方面 Array.from(xxx):把json格式字符串转换成数组: Array.of(x,x,x):负责把一堆文本或者变量转换成数组 find( ):该方法为实例方法,就是以Array对象开头 ...
随机推荐
- Git的基本使用 -- 分支管理
查看分支 git branch 前面带 * 的为当前所在分支 创建分支 git branch 分支名 切换分支 git checkout 分支名 创建并切换到此分支 git checkout -b 分 ...
- 配置本地https
参考 https://juejin.im/post/5a6db896518825732d7fd8e0 https://juejin.im/post/590ec765a22b9d0058fcfaa5 比 ...
- HTML的背景
HTML HTML(超文本标记语言),超文本包括:文字.图片.音频.视频.动画等. W3C(万维网联盟)标准包括: 结构化标准语言(HTML.XML) 1.1. HTML(超文本标记语言):用来显示数 ...
- 路飞-Redis的使用,登录注册接口
复习 """ 1.git项目开发 提供公钥成为开发者.copy项目.开发项目 先commit.再pull(可能出现冲突).最后push 特殊功能可以新建dev的子分支进行 ...
- 2.springboot------微服务
什么是微服务? 微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合:可以通过http的方式进行互通.要说微服务架构,先得说说过去我们的单体应用架构. 单体应用架 ...
- Poj1328Radar Installation雷达安装
原题链接 经典贪心,转化为问题为,对于所有的区间,求最小的点数能使每个区间都至少有一个点. #include<iostream> #include<cstdio> #inclu ...
- 【Markdown】新手快速入门基础教程
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档.当前许多网站都广泛使用 Markdown 来撰写帮助文档或是用于论坛上发表消息.例如:GitHub.简书.reddi ...
- AJAX-状态属性
XMLHttpRequest对象的readyState属性 作用:表示xhr对象的请求状态 值:由0到4表示5个状态 0:请求尚未初始化 1:已经打开到WEB服务器的连接,正在向服务器发送请求 2:请 ...
- 浅谈ABB机器人(工具坐标,工件坐标,有效载荷)
工具坐标(tool): 使tcl坐标偏移到工具上,例如焊接工作,使机器人工作点切入焊枪点上 mass:工具的重量 xyz:偏移距离的大小 验证:通过手动模式,切换至自定义工具,重定向 工件坐标(wob ...
- C语言程序设计100例之(28):直线蛇形阵
例28 直线蛇形阵 问题描述 编写程序,将自然数1.2.….N2按蛇形方式逐个顺序存入N阶方阵.例如,当N=3和N=4时的直线蛇形阵如下图1所示. 图1 直线蛇形阵 输入格式 一个正整 ...
- 从 ES6 到 ES10 的新特性万字大总结