你的位置:首页 > Java教程

[Java教程]改变你代码习惯的ES6


最近读阮一峰老师的ES6标准入门,让我感觉到了ES6的强大之处,读书之余整理了一些笔记,因为边读边记录的,所以可能会比较杂乱。

ECMAScript和Javascript的关系

1996年11月,Javascript的创造者--Netscape公司,决定将Javascript提交给国际标准化组织ECMA,希望这种语言能够成为国际标准。次年ECMA发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言叫做ECMAScript。 ECMAScript并不是Javascript的唯一,也不是唯一被标准化的部分。 ECMAScript是Javascript的规格,它描述了语法,类型,语句,关键词,保留字,运算符,对象等,而Javascript是ECMA的一种实现,同时一个完整的Javascript实现由三部分组成:ECMAScript,DOM,BOM。

ECMAScript简史

1997年,ECMAScript 1.0发布 1998年6月,ECMAScript 2.0发布 1999年13月,ECMAScript 3.0发布,并取得了巨大的成功,奠定了JS的基本语法,直到现在,初学者一开始学习的都是ES3。 2000年,ECMAScript 4.0开始酝酿。 2007年10月,ES4草案发布。 2008年7月,因为其过于激进,对ES3做了彻底的升级,ECMA开会决定终止4.0的开发。 2009年12月,ES5.0发布 2011年6月,ES5.1发布 2013年3月,ES6草案冻结,不再增添新功能,新的功能被加入ES7。 2013年12月,ES6草案发布,进行为期12个月的讨论期 2015年6月,ES6正式通过,成为国际标准,因为其继承了ES4的大部分内容,所以从2000年算起经历了15年。

 

1,转码器:babel和Traceur  ps:此处自行百度

 

2,let,const命令和block作用域

ES6新增了,let命令,用法类似var,但是所声明的变量只在let命令所在的代码块中有效,if(true){ var a = 10; let b = 9; }

注意:

1,let不存在变量提升:不会像var一样存在变量赋值先执行,所以let声明变量一定要在使用前就进行声明。

2,暂时性死区:只要块级作用域内存在let命令,它所声明的变量就“绑定”在了这个区域不再受外部影响。ES6明确规定,如果块级作用域中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域。只要声明之前使用这些变量,就会报错。 3,不允许重复声明,let不允许在相同作用域内重复声明同一个变量。

 

ES5只有全局作用域和函数作用域,没有块级作用域,所以就带来了很多不合理的场景。

1,内层变量可能会覆盖外层变量

2,用来计数的循环变量泄露为全局变量。 let为Javascript新增了块级作用域。ES6允许块级作用域任意嵌套: function test(){{{{{ let a =1; }}}}} 上面的代码用了一个五层的块级作用域,每个外层的作用域无法读取内层作用域的内容。 用let定义的作用域是定义他们的区块内,以及包含在这个块中的子块,这点有点像var,但是var定义的变量的作用域是定义他们的函数内。

 

const用来声明常量。一旦声明,其值就无法进行改变,这就意味着const一旦声明常量,就必须立即初始化,不可留到以后赋值。 const和let相同,只在声明所在的块级作用域内有效,并且声明的常量不提升,存在暂时性死区,只能在声明后使用,不能重复声明变量。

3,解构赋值

ES6允许按照一定的模式,从数组和对象中提取值,再对变量进行赋值,这被称为解构。

数组结构:demo 对于set结构,也可以使用数组的解构赋值。事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的结构赋值。 let[ x, y, z ] = new set([“a”,”b”,”c”])

 

对象的解构需要注意,数组结构是按照次序排列的,变量的取值由他的位置决定,而对象的取值没有次序,变量必须与属性同名,才能取到相应的值。

var { Oa } = { Oa: '1', Ob: '2' } 如果变量名与属性名不一致。必须写成 var { Oa: a } = { Oa: '1', Ob: '2' },这时的Oa是模式,不是变量。

对象解构的内部机制,是先找到同名的属性,然后再赋值给相对应的变量。 对象解构也可以指定默认值,但是条件是,对象属性严格等于undefined。

 

4,字符串

ES6为字符串添加了遍历接口。使字符串可以用for...of循环遍历。

at():返回字符串给定位置的字符。 一般情况下我们可以使用indexOf来确定一个字符串是否包含在另一个字符串内。

es6又为我们提供了三种新方法。

includes():返回布尔值,是否找到参数字符串。 startsWith():返回布尔值,参数字符串是否在源字符串头部。 endsWith():返回布尔值,参数字符串是否在源字符串尾部。

repeat(n):返回一个新字符串,并将源字符串重复n次。 padStart(minlength,String):从头补齐字符串 padEnd(minlength,String):从尾补齐字符串

 

模板字符串:模板字符串是增强型的字符串,用反引号(`)来进行标识,他可以当作普通字符串来使用,也可以定义多行字符串,或者在字符串中嵌入变量, 在模板字符串中使用多行字符串,所有的空格和缩进都会保留在输出中。 在模板字符串中嵌入变量,需要将变量名写在${ }中。 同时大括号内可以放任意的javascript表达式,可以进行运算以及引用对象的属性。

 

5,数组扩展

 

Array.from():将两类对象转为真正的数组,将类似数组的对象和可遍历(iterable)的对象,包括(ES6新增的数组结构set和map)

Array.of():将一组值转化为数组,为了弥补Array()数组构造函数的不足而存在,Array()会因参数的不同而导致其行为有差异。例如Array()得到[]而Array(3)得到[ , , ],Array(3,4,5)得到[3,4,5] copyWithin:将当前数组的指定位置的成员复制到其他位置(覆盖),然后返回数组。三个参数target(从该位置开始替换),start(从该位置开始读取,默认为0),end(到该位置前停止,默认为数组长度)

find(),findIndex(),fill(),includes(),entries(),keys(),values()

 

6,函数扩展

 

在ES6之前,不能为函数参数设定默认值,只能采用变通的方法。

ES6允许我们使用箭头“=>”定义函数。var f = v => v; 如果函数不需要参数或者需要多个参数,就可以使用圆括号代表参数部分。

var f = () => 5; var f = (x,y) => x+y; 利用箭头函数可以简化回掉函数[1,2,3].map( x => x*x )

箭头注意: 1,函数体的this对象的指向就是定义时所在的对象,而不是使用时候所在的对象。 2,不可当作构造函数,不可以使用new命令; 3,不可以使用arguments对象,该对象在函数体内不存在。如果要用,用rest参数代替。 4,不可以使用yield命令。不能作为Generator函数。

 

7,对象扩展

 

ES6允许直接写入变量和函数作为对象的属性和方法。

Object.is()用来比较两个值是否严格相等。和===的行为基本一致。

Object.assign()用来将源对象的所有的可枚举属性复制到目标对象。它至少需要两个对象作为参数,第一个参数是目标对象,后面的的参数都是源对象。

 

8,Symbol

 

ES5的对象的属性都是字符串。其实这容易导致属性名的冲突,如果你使用了一个别人提供给你的对象。但是又想为这个对象添加新的方法,新的方法的名字就有可能与现有的方法产生冲突。

于是,ES6引入两人一种新的数据类型Symbol,表示独一无二的值。 它是Javascript语言的第七种数据类型,之前的六种是Undefined,Null,Boolean,String,Number,Object。

Symbol值通过Symbol函数来生成,现在,对象的属性名可以有两种类型:一种是原来就有的字符串,另一种就是新增的Symbol。只要属性名是Symbol类型,就是独一无二的。 let s = Symbol(); Symbol函数不能使用new命令,因为Symbol是和对象一样的原始类型的值,他不是对象,所以他也不能添加属性。基本上来说,他就是一种类似于字符串的数据类型。

Symbol()可以接受一个字符串作为参数,表示对Symbol实例的描述。主要是为了在控制台显示或者转化为字符串时候比较容易区分。

 

 

9,Set和weakSet

 

ES6提供了一种新的数据结构——Set。它类似数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成数据结构。 var s = new Set();

属性和方法

Set.prototype.constructor:构造函数,默认为Set本身 Set.prototype.size:返回Set实例的成员总数

add(value):添加某个值,返回Set本身 has(value):返回一个布尔值,表示参数是否为Set的成员

delete(value):删除某个值,返回一个布尔值,表示是否删除成功 clear():清除所有成员,无返回值

keys()返回一个键名的遍历器,values()返回一个键值的遍历器,entries()返回一个键值对的遍历器,forEach()使用回掉函数遍历每个成员。

 

WeakSet结构与Set类似,它与Set有两个区别。 1,成员只能是对象,不能是其他类型的值。 2,WeakSet中的对象都是弱引用。

 

10,Map和weakMap

JS的Object本质上是键值对的集合,但是它只能以字符串为键,这给它的实验带来了很大的限制。”字符串:值“

为了解决这个问题ES6提供了Map数据结构,它类似对象,也是键值对的集合,但是“键“的范围不限于字符串,各种类型的值包括对象都可以当作它的键。 ”值:值“

属性和方法

size属性:返回Map结构的成员总数

set(key,value):设置key对应的键值,然后返回整个Map。如果key已经有值,会被更新。

get(key):获取key对应的键值,如果找不到key,返回undefined

has(key):返回布尔值,表示某个键是否在Map数据结构中。

delete(key):删除某个键,返回布尔值表示成功与否

keys()返回一个键名的遍历器,values()返回一个键值的遍历器,entries()返回一个键值对的遍历器,forEach()使用回掉函数遍历每个成员。

Map和数组可以互相转换,Map和对象可以互相转换,Map和JSON可以互相转换。

 

WeakMap结构与Map结构类似,唯一的区别就是它只接受对象作为键名。

 

11,Iteratror和for...of

Javascript原有表示”集合“的数据结构,主要是数组(Array)和对象Object,ES6又添加了Map和Set。

这样就有了四种数据结构,用户可以组合使用它们定义属于自己的数据结构。这时就需要一种统一的接口机制来处理不同的数据结构。

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制,即for...of循环。

Iterator的遍历过程:

1,创建一个指针对象,指向当前数据结构开始的地方。

2,第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

3,第二次调用指针对象的next方法,可以将指针指向数据结构的第二个成员。

4,不断调用next方法,直到它指向数据结构的结束位置。

在ES6中,有些数据结构原生具备Iterator接口(数组),就是不需要任何处理就可以被for...of循环,有些就不行(对象)。

原因在于,有些数据结构部署了Symbol.iterator属性,有些没有。凡是部署这个属性的数据结构就称为部署了遍历器接口,也就是可遍历的(iterable)。

调用Symbol.iterator方法,就会得到当前数据结构默认的遍历器生成函数,因他本身是一个表达式,返回Symbol对象的iterator属性,这是一个预定好的,类型为Symbol的特殊值,所以要放在方括号内。

在ES6中,有三类数据结构原生具备Iterator接口:数组,某些类似数组的对象,以及Set和Map结构。

 

12,Generator函数

 

Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。

形式上Generator函数是一个普通函数,但是内部有两个特征:一个是function命名与函数之间有一个星号;二是函数体内部使用yield语句定义不同的状态。

Generator函数的调用与普通函数一样,不同的是在调用函数后,该函数并不执行,只是返回一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object),然后调用next方法,使得指针移向下一个状态,也就是说每次调用next方法,内部指针就从函数头部或者上次停下来的地方开始执行,直到遇到下一个yield或者return语句为止。

yield语句本身没有返回值,或者说返回的是undefined,next方法可以带一个参数,该参数会被当作上一句yield语句的返回值。

 

13,Promise对象

Promise对象其实在JS语言早就有所实现,ES6在此将其写进了语言标准,统一了用法,并原生提供了Promise对象。用它来传递异步的操作的消息。

它代表某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。

它有两个特点:

1.对象的状态不受外界的影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中),Resolved(已完成),Rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

2.一旦状态改变就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变只有两种可能,从进行中到失败和从进行中到成功。只要其中之一发生改变,状态就凝固了,不会再变。 ES6规定,Promise对象是一个构造函数,用来生成Promise实例

 

14,Class

JS语言的传统方法是通过构造函数定义并生成对象。

ES6提供了更加接近传统语言的写法,引入了Class类这个概念作为对象的模板,通过class关键词来定义类。

constructor方法:它是类的默认方法,通过new命令生成对象实例自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的方法会被默认添加。

Class内部不存在变量提升。Class之间可以通过extends关键字实现继承。在子类中super关键字代表父类实例,同时可以继承原生的构造函数,因此可以在原生的数据结构上定义自己的数据结构。

Class内部可以使用set和get关键字对某个属性设置存值函数和取值函数,拦截该函数的存取行为。 在方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类调用。

 

15,Module

历史上,javascript一直没有模块体系,无法将一个大的程序拆分成相互依赖的小文件,再用简单的方法拼接起来。其他语言都有这项功能,甚至css都有@import。

在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJs和AMD两种。前者用于服务器,后者用于浏览器。ES6在语言规格的层面上实现了模块化,而且实现得相当简单,完全可以取代CommonJs和AMD规范,成为浏览器和服务器通用的模块解决方案。

export命令 export命令主要用于规定模块对外的接口。

import命令 import命令用于输入其他模块提供的功能

一个模块就是一个独立的文件,我们可以使用export default来指定默认输出。 区别于CommonJS模块,CommonJS模块输出的是一个值得拷贝,而ES6输出的是值引用,换句话说原始值变了,输入值也会跟着变,因此ES6模块是动态的引用,并不会缓存值,模块里的变量绑定其所在的模块。

 

当然ES6的新特性还不只这些,还包括了:正则扩展,数值扩展,proxy,二进制数组,修饰器等。