横向对比ES5、ES6、ES7、ES8

您所在的位置:网站首页 es6es8 横向对比ES5、ES6、ES7、ES8

横向对比ES5、ES6、ES7、ES8

2024-05-04 19:20| 来源: 网络整理| 查看: 265

前言 常用JS的代码片段 //连接数组 let arrA =[10,20,30] let arrB = [40,50,60] let arrC = [...arrA,...arrB] //数组去掉重复项 let arrD = [30,30,40,40,50,50] let arrE =[...new Set(arrD)] //查找索引 [10,20,30].indexOf(20) //遍历数组 [10,20,30].forEach((value,index)=>{ console.log(`${value} ${index}`) }) //映射新数组 let arrF = [10,20,30].map(v=>v*2) //检验数组中每个元素 [10,20,30].every(v=>v>10) //是否有元素通过测试 [10,20,30].some(v=>v>10) //过滤数组 [10,20,30].filter(v=>v>10) //获取对象的key、value、es6遍历数组 Object.keys({a:1,b:2},{a:3,b:11}) Object.values({a:1,b:2},{a:3,b:11}) Object.entries({a:1,b:2},{a:3,b:11}) //获取对象里元素数量 Object.keys({a:1,b:2},{a:3,b:11}).length 前言

ECMAScript是ECMA组织制定的脚本语言规范,该规范来源于网景的Javascript和微软的Jscript,ECMAScript-262明确的定义了JavaScript。最初,JavaScript由Brendan Eich发明,最先出现在Navigator 2.0浏览器中。ES大会定期整理并发布新的JS版本。

ES版本 发布时间 新增特性 ES5 200911 扩展了Object、Array、Function等的功能 ES6(2015) 201506 类、模块化、剪头函数、函数参数默认值、模版字符串、解构赋值、延展操作符、对象属性简写(对象扩展,还有计算属性)、Promise、let、const(块级作用域)、新数据结构Set、Map、Symbol ES7(2016) 201603 Array.prototype.includes()、指数操作符 ES8(2017) 201706 async/await、Object.values()、Object.entries()、String padding、函数参数列表结尾允许逗号、Object.getOwnPropertyDescriptors()等

延展操作符严格来说,不算ES6标准,但是babel支持,就写在这里了

ES5我就不说了,主要说说其他几个都更新了什么,另外可以使用babel将其他语法转换为es5,此外babel还支持装饰器、async-await因此,就是浏览器有些不支持es6等更高级的语法,也还是可以基于babel来使用更高级的语法

部分ES6 新增特性说明 新数据结构

Set(不可重复元素集合)、Map、Symbol

JS语言原来实现面向对象是直接通过原型的方式进行,这就让Java、Object-c、C#等面向对象语言的程序员比较难以理解了。随着JS能做的事情增多,因此,才会原生的引入class这个概念。我们来看看下边这段代码,来看看类与继承的代码如何写,此次需要注意的是,继承的时候,super要写在子类构造函数的最顶部。另外,从源码层面可以看出,class、extends、constructor都是prototype的语法糖

class Animal { constructor(name, feature) { this.name = name this.feature = feature } toString() { console.log('name:' + this.name + ',feature:' + this.feature) // return 'Animal done' } } var animal = new Animal('monky', 'beauty')//实例化 animal.toString() console.log(animal.hasOwnProperty('name'))//true console.log(animal.hasOwnProperty('toString'))//false console.log(animal.__proto__.hasOwnProperty('toString'))//true //继承 class Cat extends Animal { constructor(action) { super('cat', 'sex') this.action = action } toString(){ // console.log(super.toString()) super.toString() } } var cat = new Cat('catch') cat.toString() console.log(cat instanceof Cat)//true console.log(cat instanceof Animal)//true 模块化(Module)

ES6实现了模块化,于是告别了基于commonjs的标准,使用require.js的AMD模式和sea.js的CMD模式来实现模块化的古老方式。

模块化主要基于export(导出,还有export default)和import(引用,还有import * as)两个关键字来实现,这就为JS引入了一个相对容易理解的封装的概念(以前是基于函数作用域和闭包,通过底层直接实现,现在可以优雅的通过关键字原生实现)

export定义了对外开放的接口,import定义了引用哪些接口。模块化为js创建了比较容易理解的命名空间,防止函数的命名冲突。

export export var name = 'Ryan'//导出变量 export const sort = Math.sqrt//导出常量 export {name, sort}//导出对象(模块) export function aModule(){return 1000}//导出函数 import import defaultMethod,{otherMethod} from 'aModule'//xxx.js 箭头(Arrow)函数

1.=>是function的缩写 2.箭头函数与包围它的代码共享同一个this。这种情况下,就很好的解决了this的指向问题。我在写一个钱包服务器的时候,做过这样一个事情,通过定义var self = this,或者var that = this来引用外围的this,也就是我想用其他function中的this,我就需要想将其复制到一个全局的或者是类的属性上。借助=>,就不需要这种模式了。箭头函数的书写形式如下

()=>100 v=>x+1 (a,b)=>a-b ()=>{var=u}//return undefined p=>{return 100} 箭头函数与bind的注意事项

箭头函数和bind方法,每次被执行后都会返回一个新的函数引用,因此,如果还需要对函数的引用做些事情的话(例如卸载监听器),那么你还需要保存之前的引用。以bind为例,我们说明一下:

class PauseMenu extends React.Component { componentWillMount() { AppStateIOS.addEventListener('change', this.onAppPaused.bind(this)) } componentWillUnmount() { AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this)) } onAppPaused(event) { } }

此处,因为bind每次都会调用一个新的函数引用,因此,造成卸载的不是原来的监听,造成卸载失败。此处应该修改为

class PauseMenu extends React.Component { constructor(props) { super(porps) this._onAppPaused = this.onAppPaused.bind(this) } componentWillMount() { AppStateIOS.addEventListener('change', this._onAppPaused) } componentWillUnmount() { AppStateIOS.removeEventListener('change', this._onAppPaused) } onAppPaused(event) { } }

那么基于剪头函数,我们还可以使用箭头函数来做,因为箭头函数会共享包围它的this,因此,这样就可以简单明了的处理返回新的函数引用的问题了。

class PauseMenu extends React.Component { componentWillMount() { AppStateIOS.addEventListener('change', this.onAppPaused) } componentWillUnmount() { AppStateIOS.removeEventListener('change', this.onAppPaused) } onAppPaused = event => { } } 函数参数默认值 function foo(height = 50,color = '#fff")

如果不使用函数默认值,就会有个小问题。

function foo (height,color) { var height = height||50 var color = color||'red' }

如果参数的布尔值是false,例如,foo(0,''),因为0的布尔值是false,这样height的取值将会是50,color也会取red,因此,函数的默认值不仅能使代码简洁,也帮助规避一些风险

模版字符串 let name = 'name'+first+''+last+'' 使用模版字符串,就简洁了很多。从此告别 +号拼接字符串的尴尬,并且还支持多行字符串 let name = `name is ${first} ${last}` let names = ` xiaohong zhangzhang dada ` 解构赋值

解构赋值可以方便快速的从数组或者对象中提取赋值给定义的变量。

获取数组中的值

从数组中获取值并赋值到变量中,变量的顺序与数组中对象顺序对应。

var foo = [1,2,3,4,5] var [one,twe,three] = foo console.log(one)//1 console.log(twe)//2 console.log(three)//3 如果想要会略某些值,则可以 var [first,,last] = foo console.log(first)//1 console.log(last)//5 也可以先声明变量 var a,b [a,b] = [1,2] console.log(a)//1 console.log(b)//2 如果没有从数组中获取到值,可以为变量设置一个默认值 var a,b [a=5,b=7]=[1] console.log(a)//1 console.log(b)//7 方便的交互两个变量的值 var a=1 var b = 3 [a,b]=[b,a] console.log(a)//3 console.log(b)//1 获取对象中的值 const student={ name:'xxx', age:'19', city:'bj' } const {name, age,city}=student console.log(name)//xxx console.log(age)//19 console.log(city)//bj 延展操作符(Spread operator)

延展操作符 = ...可以在函数调用/数组构造时,将数组表达式或者string在语法层面展开,还可以在构造对象时,将对象表达式按key-value的方式展开。

函数调用 function(...iterableObj) 数组构造或者字符串 [...iterableObj,'4',...'hello',6] es2018下构造对象时,进行克隆或者属性拷贝 let objClone={...obj} 应用场景 function sum(x,y,x){ return x+y+z } const numbers = [1,2,3] 不使用延展操作符 sum.apply(null, numbers) 使用延展操作符 sum(...numbers) 或者在构造数组时 如果没有展开语法,只能组合使用push,splice,concat,slice 将已有数组元素变为新数组的一部分 const people=['jan','tom'] const person = ['ali',...people,'alliance','ketty'] console.log(person)//Ali,jan,tom,alliance,ketty 另外,还有一个例子 var arr =[1,2,3] var arr2=[...arr] arr2.push(4) console.log(arr2)//1,2,3,4

展开语法与Obj.assign()行为一致,都是执行浅拷贝,也就是只遍历一层,不会遍历父对象相关的数据

var arr1=[0,1,2] var arr2=[3,4,5] var arr3=[...arr1,...arr2] 等同于var arr4 = arr1.concat(arr2)

es2018中增加了对对象的支持

var obj1 = {foo:1,foo2:2} var obj2={foo3:12,foo4:30} var clonedObj={...obj1} var mergedObj={...obj1,...obj2} ...在react中的应用

我们封装组件的时候,会对外公开props用于参数传递。我们一般都会使用...props进行参数的传递与遍历数据

等价于 const params = { name:'jine', age:21 }

我们还可以配合结构赋值,避免传入一些不必要的参数

var param = { name:123, title:456, type:789 } var {type,...other} = param 等价于 对象属性简写

设置一个对象时,不指定属性名

es5 const name=ming,age=18,city=Shanghai const student={ name:name, age:age, city:city } es6 const name=ming,age=18,city=Shanghai const student={ name, age, city } Promise

Promise是异步编程的一种解决方案,对callback做进一步的封装和优化。Promise由社区提出和实现,es6将其写进了语言标准,并统一了用法,提供了原生的promise支持。此处详细解答,请看我的另外一篇blog

let、const

let、const提供块级作用域,之前只能通过函数+闭包的方式来模拟块级作用域。那么let与const的区别呢?let声明可变变量,类型和值都可以改变。const声明不变变量(可以认为是常量,声明后就不能再次赋值了),声明变量后,必须马上初始化,不能留到以后赋值。

es5 {var a=19} console.log(a)//19 es6 { let a=20 } console.log(a)//a is not defined 部分ES7新增特性说明 Array.prototype.includes()

用来判断一个数组是否包含指定的值,包含返回true,不包含返回false

arr.includes(x)等价于arr.indexOf(x)>0 指数操作符(**)

** 与Math.pow(..)等效的计算结果

Math.pow(2,10)等价于2**10 部分ES8新增特性说明 async/await

异步转同步,可以看我的另外一片blog。我们主要是用async/await来顺序获取异步函数返回值。 另外,依托于Promise.all还可以实现await的并发调用,这样就避免了同步调用的时间等待,提高了程序的整体效率。

async function charCountAdd(data1, data2) { const [d1, d2] = await Promise.all([charCount(data1), charCount(data2)]) // const d1 = await charCount(data1) // const d2 = await charCount(data2) return d1 + d2 } charCountAdd('Hello','Hi').then(console.log) function charCount(data){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve(data.length) },5000) }) } //捕捉错误,可以使用try catch,也可以在then后追加catch Object.values()

Object.values()类似于Object.keys(),返回Object自身的所有属性值,不包括继承的值。

const obj={a:12,b:13,c:14} es7 const vals=Object.keys(obj).map(key=>obj[key]) console.log(vals)//12,13,14 es8 const vals =Object.values(obj) console.log(vals)//12,13,14 Object.entries

Object.entries()函数返回一个给定对象自身可枚举属性的键值对的数组

const obj={a:12,b:13,c:14} es7 Object.keys(obj).forEach(key=>{ console.log('key:'+key+'values:'+obj[key])//12,13,14 }) es8 for (let [key,value] of Object.entries(obj)){ console.log('key: ${key} values: ${value}')//12,13,14 } String padding

es8中,新增String.prototype.padStart(targetLength,[padString])和String.prototype.padEnd允许将空字符串或者其他字符串添加到原始字符串的开头或者结尾

console.log('0.0'.padStart(4,'10'))//10.0 console.log('0.0'.padStart(20))// 0.0 console.log('0.0'.padEnd(4,'0'))//0.00 console.log('0.0'.padEnd(10,'0'))//0.000000000 编码规范 单引号、双引号

node中,单引号双引号有很多的时候是可以混用的,因此,此处也是一个重点,如果不注意,很容易出现奇怪的错误。在JSON中,全部请使用双引号,在其他地方也请使用双引号,如果在双引号中又存在引号,请使用单引号做区分。

分号

必须记得加分号,不加分号就跟耍流氓一样。

缩进、空格、大括号、小括号、逗号、数组、对象等的基本格式

通过IDE协助缩进,具体方式请根据不同的IDE进行总结,本处简单介绍webstrom的使用方式----全选之后,剪切代码,然后重新粘贴到代码编辑文本输入框中。

变量声明

如果是ES5的话永远都要加var,如果是ES6.....如果是ES7....

命名

变量命名:名词+小驼峰 方法命名:动词开头+名词+小驼峰 类命名(ES6开始有类喽,MD以前都是原型....艹):大驼峰 常量命名:大写字母 + 下划线 文件命名:小写字母 + 下划线 中间件(包)命名:小写字母

==和===

==用于直接比较数值的情况 ===用于其他情况

字面量

这个字面量的相关说明来源于朴灵的深入浅出,我在这里做一下搬运工。书中说“请尽量使用{}、[]替代new Object()、new Array(),不要使用string(new String)、bool(new Boolean)、number(new Number)对象类型”,当然,此处应该更多的是针对ES5下的相关语法,如果是ES6、ES7应该完全不一样了。(需要补充ES6、ES7的相关语法和规范)

作用域

依然会存在ES5、ES6、ES7不一致的地方。 ES5中没有块级语句,最多是通过with来增加一个临时的块级变量复用区域,但是with又不是特别安全的。 ES6有了块级区域,变量范围跟java差不多了

数组

数组不要用for in进行循环,详见代码:

var foo = []; foo[100] = 100; //循环1 for (var i in foo) { console.log(i); } //循环2 for (var i = 0; i < foo.length; i++) { console.log(i); }

循环1只会执行一次,循环2则会执行0~100,因此数组就不要for in 了 ,因为,我们可能希望遍历数组中的全部数据,而不是非undefine的数据。

异步

异步回调第一个参数应该是错误指示。

类与模块

依然会存在ES5、ES6、ES7不一致的地方。(之后做整理) 类继承、导出都不一样。

在es5下,类继承推荐的方式是

function Socket(options){ //.... stream.Stream.call(this); //.... } util.inherits(Socket,stream.Stream); 最佳实践的理论

1.注释要写成/** */的形式,方面工具导出 2.遵循原来项目的编码规范 3.基于JSLint或者JSHint进行代码质量管理,可以在编辑器中提醒不规范的信息,我们可以增加.jshintrc文件。这个可以去github上去查找并学习。 4.代码提交的hook脚本,例如precommit,这个需要学习整理。 5.持续集成,一方面,持续集成代表着代码质量的扫描,可以定时扫描或者触发式扫描,另一方面,可以通过集中的平台统计代码质量的好坏趋势,根据统计结果可以判定团队中的个人对编码规范的执行情况,决定用宽松的质量管理还是严格的质量管理。 6.可以使用CoffeeScript规范(编译式的js,还有typescript等),来避免一些不必要的编码规范的问题

说明 规则或方法 空格 利用IDE的排版功能自动增加需要的空格。(+、-、*、/、%、(、)等操作符前后都增加空格) 缩进 利用IDE的排版功能自动增加需要的缩进。与Java编码规范相同 变量声明 使用var声明变量 单引号、双引号 json中必须使用双引号,es5 use strict下也必须使用双引号,其他地方单引号双引号可以随便用 分号 必须增加分号在语句的结尾 变量命名 小驼峰 方法命名 小驼峰,动词开头 类命名 大驼峰 常量命名 都是大写字母,使用下划线隔开每个单词 文件命名 都是小写字母,使用下划线隔开每个单词 对外文件命名 都是小写字母,使用下划线开头,并使用下划线隔开每个单词 包名 都是小写字母,仿照npm库进行包的命名 比较操作 尽量使用===,数值比较可以用==


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3