ECMAScript 新特性
2021-04-28 本文已影响0人
丽__
- ECMAScript 与 JavaScript
- ECMAScript 的发展过程
- ECMAScript 2015 的新特性
- And more
ECMAScript 通常看做javaScript的标准化规范,实际上JavaScript是ECMAScript的扩展语言,ECMAScript只是提供了最基本的语法,JavaScript在此基础之上升级扩展 JavaScript语言本身指的就是ECMAScript。
2015年开始ES保持每年一个版本的迭代。ES按照年份命名
![](https://img.haomeiwen.com/i24717104/2a709aecd8c93e36.png)
重点介绍在ES5.1基础之上的变化,归为4类
- 解决原有语法上的一些问题或者不足
- 对原有语法进行增强
- 全新的对象、全新的方法、全新的功能
- 全新的数据类型和数据结构
ES2015
- let 与块级作用域
- 作用域---某个成员能够起作用的范围
- 在ES2015之前只有两种作用域:全局作用域和函数作用域
- 在ES2015新增了块级作用域,以前块没有独立的作用域
- const 恒量 / 常量
最佳实践:不用var,主要用const,配合let - 数组的结构 Destructuring
const arr =[100,200,300]
const [, , three] = arr
console.log(three);
// ... 提取后面所有成员,只能在最后一个位置
const [foo,...rest] = arr
console.log(rest)
- 对象的解构 Destructuring
const obj = {name:'adb',age:18}
const {name} = obj
console.log(name);
//为了防止命名冲突 可以将name重命名 name:objName
const name = 'tom'
const {name:objName}=obj
console.log(objName);
//也可以添加默认值 name:objName = 'jack'
const name = 'tom'
const {name:objName = 'jack' }=obj
console.log(objName);
- 模板字符串字面量 Template literals
//可以换行
const str = `hello es2015
jkjkljl
jkjlk`
//可以加入变量表达式
const name = 'tom'
const msg = `hey,${name} --- ${1+2}-------${Math.random()}`
console.log(msg);
- 模板字符串标签函数 Tagged templates
// console.log 标签函数 会打印出hello world
const str = console.log`hello world`
const name ='tom'
const gender = true
function myTagFunc (strings,name,gender){
console.log( strings,name,gender);
const sex = gender ?'man':'woman'
return strings[0]+name+strings[1] +sex+strings[2]
}
const result = myTagFunc`hey,${name} is a ${gender}.`
console.log(result);
//对模板字符串进行加工
- 字符串对象的扩展方法
- includes()
- startsWith()
- endsWith()
const message = 'Error:foo is not defined'
console.log(message.startsWith('Error));//true
console.log(message.endsWith('.));//true
console.log(message.includes('Error));//true
- 参数默认值 Default parameters
//旧的
function foo(enable){
enable = enable===undefined ? true : enable
console.log(enable)
}
//新 带有默认值的参数要放在最后
function foo(bar,enable = true){
console.log(enable);
}
- 剩余参数 Rest parameters
//旧
function foo(){
console.log(arguments)
}
//新 ... 操作符只能出现在参数的最后一位,并且只能出现一次
function foo(...args){
console.log(args);
}
foo(1,2,3,4,5)
- 展开数组 Spread
//旧
const arr = ['foo','bar','baz']
console.log.apply(console,arr)
//新的
console.log(...arr);
- 箭头函数 Arrow functions
//旧的
function inc(number){
return number +1
}
//新的
const inc = n => n+1;
console.log(inc(100));
const arr =[1,2,3,4,5,6,7]
//旧
arr.filter(function(){
return item%2
})
arr.filter(i+>i%2)
- 箭头函数与this
箭头函数不会改变this指向
const person = {
name:'tom'
// sayHi:function(){
// console.log(`hi,my name is ${this.name}`);
// }
sayHi:()=>{
console.log(`hi,my name is ${this.name}`);
}
}
person.sayHi();
- 对象字面量增强 Enhanced object literals
const bar = 34444
const obj = {
foo:123,
//bar:bar
bar,
// method1:function(){
// console.log('1111');
// }
method1(){
console.log(111);
}
[Math.random()]:123;//现在可以用[ ] 来写
}
//obj[Math.radom()] = 123; 之前可以在外面动态添加Math.radom 的值
console.log(obj);
obj.method1()
- 对象扩展方法 Object.assign
将多个源对象中的属性复制到一个目标对象中
const source1 ={
a:123,
b:123
}
const target = {
a:456,
c:456
}
//合并对象
const resultObject.assign(target,source1)
console.log(target);
console.log(result === target); //后面对象的属性覆盖前面对象的属性 true
function func(obj){
const funcObj = Object.assign({},obj)
funcObj.name = 'func obj'
console.log(funcObj);
}
const obj = {name:'global obj'}
func(obj)
console.log(obj);
- 对象扩展方法 Object.is.
console.log(
0==false, //true
0===false //false
+0 === -0 //true
NaN ===NaN //false
Object.is(+0,-0),//true
Object.is(NaN,NaN),//true
)
- Proxy 代理对象 Object.defineProperty
const person = {
name:'zce',
age:20
}
const personProxy = new Proxy(person,{
get(target,property){
return property in target ? target[property] : 'default
console.log(target,property);
},
set(target,property,value){
if(property ==='age'){
if(!Number.isInteger(value)){
throw new TypeError(`${value} is not an int`)
}
}
target[property] = value
},
})
personProxy.age =100
personProxy.gender = true
console.log(personProxy.name);
console.log(personProxy.xxx);
- Proxy vs Object.defineProperty()
defineProperty 只能监视属性的读写
Proxy 能够监视到更多对象操作
//Proxy 对比 Object.defineProperty()
const person = {
name:'zce',
age:20
}
const personProxy = new Proxy(person,{
deleteProperty(target,property){
console.log('delete',property);
delete target[property]
}
})
delete personProxy.age
console.log(person);
![](https://img.haomeiwen.com/i24717104/172a599a50ca0884.png)
Proxy 更好的支持数组对象的监视
** 如何使用Proxy对象监视数组**
const list = []
const listProxy = new Proxy(list,{
set(target,property,value){
console.log('set',property,value);
target[property] = value
return true //表示设置成功
}
})
listProxy.push(100)
Proxy是以非侵入的方式监管了对象的读写
const person = {}
Object.defineProperty(person,'name',{
get(){
console.log('name被访问');
return person._name
},
set(value){
console.log('name 被设置');
person._name = value
}
})
Object.defineProperty(person,'age',{
get(){
console.log('age被访问');
return person._age
},
set(value){
console.log('age被设置');
person._age = value
}
})
person.name = 'jack'
console.log(person.name)
//Proxy 方式更为合理
const person2 = {
name:'zce'
age:20,
}
const personProxy = new Proxy(person2,{
get(target,property){
return target[property]
}
set(target,property,value){
target[property] = value
}
})
personProxy.name = 'jack'
console.log(personProxy.name);
- ES2015 Reflect 统一的对象操作API
Reflect 属于一个静态类 不能够通过 new Reflect()的方式构建一个实例对象
只能够调用静态类中的静态方法Reflect.get();
Reflect 内部封装了一系列对象的底层操作
Reflect 成员方法就是Proxy处理对象的默认实现
const obj = {
foo:123,
bar:456
}
const proxy = new Proxy(obj,{
get(target,property){
console.log('watch logic~');
return Reflect.get(target.property)
}
})
console.log(proxy.foo);
Reflect 最大的意义就是提供了一套用于操作对象的API
const obj = {
name:'zce',
age:18
}
console.log('name' in obj);
console.log(delect.obj['age']);
console.log(Object.keys(obj));
console.log(Reflect.has(obj,;'name'))
console.log(Reflect.deleteProperty(obj,'age))
console.log(Reflect.ownKeys(obj))
-
Promise 一种更优的异步编程方案,解决了传统异步编程中回调函数嵌套过深的问题
-
class 类
//class 关键词
function Person(name){
this.name = name
}
Person.prototype.say = function(){
console.log(`hi,my name is ${this.name}`);
}
class Person{
constructor(name){
this.name = name
}
say(){
console.log(`hi,my name is ${this.name}`);
}
}
const p = new Person('tom')
p.say();
- 静态方法 static 实例方法vs静态方法、
ES2015 中新增添加静态成员的static 关键词
class Person{
constructor(name){
this.name = name
}
say(){
console.log(`hi,my name is ${this.name}`);
}
static creat(name){
return new Person(name)
}
}
const tom = Person.create('tom')
tom.say();
- 类的继承 extends
class Person{
constructor(name){
this.name = name
}
say(){
console.log(`hi,my name is ${this.name}`);
}
}
class Student extends Person{
constructor(name,name){
super(name);//相当于调用父类构造函数
this.number = number
}
hello(){
super.say();//x相当于调用父类的方法
console.log(`my school Number is ${this.number}`);
}
}
const s = new Student ('jack','100')
s.hello()
- Set 数据结构
const s = new Set();
s.add(1).add(2).add(3).add(4)
console.log(s);
s.forEach(i => console.log(i))
for(let i of s){
console.log(i);
}
console.log(s.size);//获取整个集合的长度
console.log(s.has(100));//判断是否包含某个特定的值
console.log(s.delect(3));//删除
s.clear();//清空
console.log(s);
const arr = [1,2,3,4,5,1,2,3]
const result = Array.from(new Set())
const result = [...new Set(arr)]
console.log(result);
- Map 数据结构
const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{a:1}] = 'value'
console.log(obj);
//如果添加的键 不是一个字符串,内部会将这个键的toString结果作为键
console.log(obj['[object object]']);//value
const m = new Map()
const tom = { name:'tom' }
m.set(tom,90)
console.log(m); // Map { { name : 'tom' } => 90 }
console.log(m.get(tom));//90
m.has();//判断某个键是否存在
m.delete();//删除
m.clear();//清空
m.forEach((value,key)=>{
console.log(value,key);
})
//map数据结构与对象最大的区别就是,map可以用任意类型作为键,对象上只能够使用字符串作为键
- Symbol 一种全新的原始数据类型
//Symbol 数据类型
// shared.js -----------------------
const cache = {}
//a.js --------------------------------
cache['a_foo'] = Math.radom();
//b.js --------------------------------
cache[b_foo] = '123'
console.log(cache);
const s = Symbol()
console.log(s);
console.log(typeof s)
console.log(
Symbol() === Symbol()
);
console.log(Symnbol('foo'));
console.log(Symnbol('foo1'));
console.log(Symnbol('foo3'));
const obj = {}
obj[Symbol()] = '123'
obj[Symbol()] = '456'
console.log(obj);
const obj = {
[Symbol()]:123
}
console.log(obj);
//Symbol 可以避免对象属性名重复产生的问题,还可以借助这种类型的特点模拟实现对象的私有成员
//a.js------------------------------
const name = Symbol()
const person ={
[name]:'zce',
say(){
console.log(this[name]);
}
}
//b.js-------------
person.say()
//最主要的作用就是为对象添加独一无二的属性名, 截止到ES2019,一共定义了7中数据类型(Number、String、Boolean、undefined、object、Null,Symbol),未来还会有一种新的数据类型BigInt ,用于存放更长的数字,目前还处在stage-4阶段,标准化过后就是8种数据类型了
//Symbol 补充
console.log(
Symbol() === Symbol();//false
Symbol('foo') === Symbol('ff') ;//false
);
const s1 =Symbol.for('foo');
const s1 =Symbol.for('foo');
console.log(s1===s2);//true
console.log(
Symbol.for(true) === Symbol.for('true');//true
);
console.log(Symbol.interator);
console.log(Symbol.hasInstance);
const obj ={
[Symbol.toStringTag]:'xobject'
}
console.log(obj.toString);// [object Xobject]
const obj = {
[Symbol()]:'symbol value',
foo:'normal value'
}
for(var key in obj){
console.log(key);//无法拿到Symbol类型的属性名
}
console.log(Object.keys(obj));//无法拿到Symbol类型的属性名
console.log(JSON.stringify(obj));//Symbol 类型也会被忽略掉
console.log(Object.getOwnPrpertySymbols(obj));//获取到的全是Symbol类型的属性名
- for--of 循环
for 常用于遍历普通数组
for..in 常用于遍历键值对
forEach 常用于遍历方法
这些便利方式都有一定的局限性
ES 2015 引入了全新的for...of 循环,作为遍历所有数据结构的同意方式
//for...of 循环
for(const item of arr){
console.log(item);
}
arr.forEach(item =>{
console.log(item);
})
for(const item of arr){
console.log(item);
if(item>100){
break;//可以跳出循环
}
}
//arr.forEach();//不能跳出循环
//arr.some(); //返回true可以终止遍历
//arr.every(); //返回fale 可以终止遍历
const s = new Set(['foo','bar'])
for(const item of s){
console.log(item);
}
const m = new Map();
m.set('foo','132')
m.set('bar','456')
for(const [key,value] of m){
console.log(key,value);
}
const obj = {foo:123,bar:456}
for(const item of obj){
console.log(item);
}
- ES 2015 可迭代接口
for...of 循环是一种数据统一便利方式,ES中能够表示有结构的数据类型越来越多,为了各种各样的数据结构提供统一便利方式,ES2015提供了Iterable 接口
实现Iterable接口就是for...of 的前提
//迭代器Iterator
const set = new Set(['foo','bar',''baz])
const iterator = set[Symbol.iterator]()
console.log(iterator.nex());
console.log(iterator.nex());
console.log(iterator.nex());
console.log(iterator.nex());
console.log(iterator.nex());
- 实现可迭代接口 Iterable
const obj = {
//可迭代接口Iterable:约定内部必须有一个用于返回迭代器的iterator 方法
[Symbol.iterator]:function(){
return:{
// 迭代器方法iterator ,约定内部必须有一个用于迭代的next方法
next:function(){
return :{
//迭代结果IterationResult,约定内部必须有一个value属性,用于表示当前被迭代的数据,值可以是任意类型,还要有一个done属性用来表示是否迭代完成
value:'zce',
done:true
}
}
}
}
}
const obj = {
store:['foo','bar','baz']
[Symbol.iterator]:function(){
let index = 0;
const self = this
return:{
next:function(){
const result = {
value:self.store[index],
done:index>=self.store.length
}
index++
return result
}
}
}
}
for(const item of obj){
console.log('循环体',item);
}
- 迭代器模式 Iterator
const todos ={
life:['吃饭','睡觉','打豆豆'],
learn:['语文','数学','英语'],
work:['喝茶','工作'],
each:function(callback){
const all = [].concat(this.life,this.learn,this.work)
for(const item of all){
console.log(item);
}
},
[Symbol.iterator]:function(){
const all = [...this.life,...this.learn,...this.work]
let index = 0;
return{
next:function(){
return{
value:all[index],
done:index++>=all.length
}
}
}
},
}
todos.each(function(item){
console.log(item);
})
console.log('-------------');
for(const item of todos){
console.log(item);
}
// 对外提供统一遍历接口,让外部不用关心内部结构
// 当前的each 只适用于当前的数据结构,
// ES2015 iterator是语言层面实现迭代器模式,它适用于任何数据结构
- 生成器函数 Generator
避免异步编程中回调嵌套过深所产生的问题,提供更好的异步编程解决方案
// Generator 函数
// function* foo() {
// console.log('sf');
// return 100
// }
// const result = foo();
// console.log(result.next());
function* foo() {
console.log('111');
yield 100;
console.log('222');
yield 200;
console.log('333');
yield 300;
}
const generator = foo();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
- 生成器应用 Generator
// Generator 应用
// 案例1:发号器
function* createIdMaker() {
let id = 1
while (true) {
yield id++
}
}
const idMaker = createIdMaker();
console.log(idMaker.next().value);
console.log(idMaker.next().value);
console.log(idMaker.next().value);
//优化
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '英语'],
work: ['喝茶', '工作'],
[Symbol.iterator]: function* () {
const all = [...this.life, ...this.learn, ...this.work]
for (const item of all) {
yield item
}
}
}
for (const item of todos) {
console.log(item);
}
- ES Modules 语言层面的模块化标准
- ES2016 (6月)
//Array.prototype.includes-------------
const arr = ['foo',1,NaN,false]
console.log(arr.indexOf('foo'));//找到对应的元素的下标,如果没有找到会返回-1
console.log(arr.indexOf('bar'));
console.log(arr.indexOf(NaN));//不能够用来查找数组中的NaN
console.log(arr.includes('foo'));//includes返回true或者false,可以用来查找NaN
console.log(arr.includes(NaN))
//指数运算符-----------------------------
console.log(Math.pow(2,10));//1024
console.log(2 ** 10);//1024
- ES2017 (6月)
const {
Console
} = require("console");
// ES 2017
const obj = {
foo: 'value1',
bar: 'value2'
}
// Object.values----------------
console.log(Object.keys(obj)); //返回所有键组成的对象
console.log(Object.values(obj)); //返回所有值组成的对象
// Object.entries----------------
console.log(Object.entries(obj)); //返回所有键值对
for (const [key, value] of Object.entries(obj)) {
console.log(key, value)
}
console.log(new Map(Object.entries(obj)));
// Object.getOwnPropertyDescriptors(获取对象中属性的完整的描述信息)----------------
const p1 = {
firstName: 'xing',
lastName: 'ming',
get fullName() {
return this.firstName + " " + this.lastName
}
}
console.log(p1.fullName);
// const p2 = Object.assign({}, p1)
// p2.firstName = 'zce'
// console.log(p2);
const descriptors = Object.getOwnPropertyDescriptors(p1); //获取完整的描述信息
// console.log(descriptors);
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'zce'
console.log(p2.fullName);
// String.prototype.padStart / String.prototype.padEnd (字符串填充方法)----------------
const books = {
html: 5,
css: 16,
javasctipt: 123
}
for (const [name, count] of Object.entries(books)) {
console.log(name, count);
}
for (const [name, count] of Object.entries(books)) {
console.log(`${name.padEnd(16,'-')} | ${count.toString().padStart(3,'0')}`)
}
// 在函数参数中添加尾逗号----------------
function foo(bar, baz, ) {
}
const arr = [
100,
200,
300,
400,
]
Async / Await Promist 的一种语法糖