从 ES6 到 ES10 的新特性万字大总结
介绍
ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)在标准ECMA-262中定义的脚本语言规范。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际上后两者是ECMA-262标准的实现和扩展。
历史版本
至发稿日为止有九个ECMA-262版本发表。其历史版本如下:
1.1997年6月:第一版2.1998年6月:修改格式,使其与ISO/IEC16262国际标准一样3.1999年12月:强大的正则表达式,更好的词法作用域链处理,新的控制指令,异常处理,错误定义更加明确,数据输出的格式化及其它改变4.2009年12月:添加严格模式("use strict"
)。修改了前面版本模糊不清的概念。增加了getters,setters,JSON以及在对象属性上更完整的反射。5.2011年6月:ECMAScript标5.1版形式上完全一致于国际标准ISO/IEC 16262:2011。6.2015年6月:ECMAScript 2015(ES2015),第 6 版,最早被称作是 ECMAScript 6(ES6),添加了类和模块的语法,其他特性包括迭代器,Python风格的生成器和生成器表达式,箭头函数,二进制数据,静态类型数组,集合(maps,sets 和 weak maps),promise,reflection 和 proxies。作为最早的 ECMAScript Harmony 版本,也被叫做ES6 Harmony。7.2016年6月:ECMAScript 2016(ES2016),第 7 版,多个新的概念和语言特性。8.2017年6月:ECMAScript 2017(ES2017),第 8 版,多个新的概念和语言特性。9.2018年6月:ECMAScript 2018 (ES2018),第 9 版,包含了异步循环,生成器,新的正则表达式特性和 rest/spread 语法。10.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 | |
变量提升 | √ | × | × |
全局变量 | √ | × | × |
重复声明 | √ | × | × |
重新赋值 | √ | √ | × |
暂时死区 | × | √ | √ |
块作用域 | × | √ | √ |
只声明不初始化 | √ | √ | × |
类(Class)
在ES6之前,如果我们要生成一个实例对象,传统的方法就是写一个构造函数,例子如下:
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.information = function () {
return 'My name is ' + this.name + ', I am ' + this.age
}
但是在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'
}
这样看起来也没有问题,但是如果参数的布尔值为false时就会出问题,例如我们这样调用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
翻译过来就是承诺的意思,这个承诺会在未来有一个确切的答复,并且该承诺有三种状态,分别是:
1.等待中(pending)2.完成了 (resolved)3.拒绝了(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 -> finifsh
Promise
实现了链式调用,也就是说每次调用 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*
[2]语法编写。最初调用时,生成器函数不执行任何代码,而是返回一种称为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,值就是undefined
wm1.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的新特性万字大总结
本文转自https://cloud.tencent.com/developer/article/1615505[作者:陈大鱼头•github: KRISACHAN[1]] 介绍 ECMAScript是 ...
- ECMAScript 2019(ES10) 的新特性总结
快速通道: ES6.ES7.ES8.ES9.ES10.ES11.ES12.ES13新特性大全 老规矩,先纵览下 ES2019 的新功能: Array.flat()和Array.flatMap():数组 ...
- ES10的新特性 —— bigint
前面的话 虽然es10现在还是草案,但Chrome大多数功能都已经实现.下面介绍新特性:bigint基本数据类型,es6中的基本数据类型包括:null,undefined,number,string, ...
- 说说ES10的新特性!
ES10 虽然没有像 ES6 那么多新特性,但 ES10 仍然有一些有用的特性.文本通过简单示例来介绍了 ES10 新出来的特性.通过这种方式,咱们就可以快速理解,而不需要看太多的官方解释. ES10 ...
- ES6基础-字符串的新特性
字符串的新特性 1. es6新增遍历接口:for...of循环遍历 2. 模版字符串: 使用``号对字符串进行原格式输出 可以使用trim()方法进行取消换行 模版字符串潜入变量,需要将变量名写在${ ...
- ES6进阶 字符串处理新特性
文章目录 字符串遍历 模板字符串 认识 JavaScript 表达式 字符串新增的方法 includes(), startsWith(), endsWith() repeat() padStart() ...
- ES2019(ES10)新特性
ECMAScript2019 对象新增方法:Object.fromEntries() 字符串新增方法:trimStart() 与 trimEnd() 数组新增方法:flat() 与 flatMap() ...
- ES6、ES7、ES8、ES9、ES10、ES11、ES12、ES13新特性大全
本文是对 ES6 至 ES13 常用到的特性的总结,关于每个特性的详细内容,都有单独的文章进行详细讲述,可以跳转到具体文章进行学习查看.学习永无止境大家一起努力 . 文章为从新到旧的顺序. ECMAS ...
- ECMAScript 2019(ES10)新特性简介
简介 ES10是ECMA协会在2019年6月发行的一个版本,因为是ECMAScript的第十个版本,所以也称为ES10. 今天我们讲解一下ES10的新特性. ES10引入了2大特性和4个小的特性,我们 ...
最新文章
- HTML5学习之二:HTML5中的表单2
- Windows 8部署系列PART2:部署先决条件准备
- CVPR 2020 论文大盘点-全景分割与视频目标分割篇
- mstsc连接远程桌面如何挂载本地磁盘
- AVFrame中data与linesize关系
- Linux 命令之 curl -- 文件传输工具/下载工具/网络接口调试
- OpenGL ES入门(使用指南)
- hdu4540---DP入门
- deepnode软件下载地址_office2010 软件下载地址
- Geforce Experience无法登录的解决方法
- 心知天气使用签名验证方式
- 树莓派4B WIFI 物理网口设置固定IP方法
- 脑洞大开的思维工具:六顶思考帽
- 关于线上支付的实现思想方法与例子
- 2019年数学建模A题 高压油管的压力控制
- 2018.8.4T3(大容斥)
- 活体检测 根据TPR、FPR绘制图像计算最优模型阈值
- 宏文件下载_SolidWorks模型英文特征改中文名字方法分享SolidWorks宏文件 [
- MFC CPropertySheet 多页面切换 事例
- 北师大 外国教育史-7(进步主义教育运动)
热门文章
- 浏览器便携化操作方法
- 广东工业大学计算机研究生学硕就业,广州大学与广东工业大学2018年MPAcc就业平均薪资解析!...
- vue中单选框设置默认选中值
- SCT71403Q,LDO参数
- Tridium niagara N4---报警告警信息点设置
- 消息中间件合集:MQ(ActiveMQ/RabbitMQ/RocketMQ)+Kafka+笔记
- 谷歌公开了内部管理Infra层的两个工具的Paper
- Latex 自用中文模板
- python计算圆周率代码_使用 Python 计算 π 值
- JSP中include的两种方法