箭头函数
es6允许使用箭头 (=>) 定义函数。例如:
var f = function(v) { return v; };
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。例如:
var sum = (num1, num2) => num1+num2;
等同于:
var sum = function(num1,num2) { return num1+num2; }
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。例如:
var sum = (num1,num2) => { return num1+num2; }
提示:由于大括号被解释为代码块,如果箭头函数直接返回一个对象,必须在对象外面加上括号。例如:
var getTempItem = id => ({id: id, name:"Temp"})
使用箭头函数可以简化回调函数
var result = values.sort(function(a,b) { return a - b; }) //箭头函数写法 var result = values.sort((a,b) => a - b);
提示: 使用箭头函数应该注意下面几点问题:
函数体内的this对象,绑定定义时所在的对象,而不是使用时所在的对象。
箭头函数不可以当作构造函数,即不可以使用new命令,否则会抛出一个错误。
箭头函数不可以使用arguments对象,该对象在函数体内不存在。
数据结构
es6完善了JavaScript数据结构,新增了两个数据类型: Set和Map。下面分别进行说明。
Set
es6提供了新的数据结构Set。它类似于数组,但成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,调用该函数可以生成Set数据结构。
下面代码使用add()方法向Set结构添加成员,结果显示Set结构不会包含重复的值。
var s = new Set(); [2,3,4,5,2,2].map(x => s.add(x)); for(i of s) {console.log(i)} //2 3 4 5
Set结构的原型 (Set.prototype) 定义两个属性,说明如下
constructor: 构造函数,默认就是Set函数
size: 返回Set结构的成员数
Set数据结构的原型 (Set.prototype) 定义了4个方法,说明如下
add(value): 添加某个值。
delete(value): 删除某人值
has(value): 返回一个布尔值,表示该值是否为set的成员
clear(): 清除所有成员
WeakSet
WeakSet结构与Set类似,也是不重复的值的集合。但是,它与Set有两个区别
WeakSet的成员只能是对象,而不能是其他类型的值
WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的。
WeakSet是一个构造函数,可以使用new命令创建WeakSet数据结构
var ws=new WeakSet();
作为构造函数,WeakSet可以接收一个数组或类似数组的对象作为参数。该数组的所有成员都会自动成为WeakSet实例对象的成员。
在下面代码中,a是一个数组,它有两个成员,也都是数组。将a作为WeakSet构造函数的参数,a的成员会自动成为WeakSet的成员
var a=[[1,2],[3,4]]; var ws=new WeakSet(a);
WeakSet结构的原型 (WeakSet.prototype) 定义了4个方法:
add(value): 向WeakSet实例添加一个新成员
clear(): 清除WeakSet实例的所有成员
delete(value): 清除WeakSet实例的指定成员
has(value): 返回一个布尔值,表示某个值是否在WeakSet实例中。
Map
JavaScript对象都是键值对的集合,只能使用字符串作为键。使用起来不是很方便
下面代码计划将一个DOM节点作为对象data的键,但是由于对象只接收字符串作为键名,所以element被自动转为字符串[Obiect HTMLDivElement]。
var data = {}; var element=document:getElementById("myDiv"); data[element]=metadata;
为了解决这人问题,es6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,对象也可以当作键
Map数据结构定义了下面这些属性和方法
size: 返回成员总数。
set(key,value): 设置一个键值对
get(key): 读取一个键
has(key): 返回一个布尔值,表示某个键是否在Map数据结构中
delete(key): 删除某个键
clear(): 清除所有成员
Map提供3个原生遍历器。具体说明如下
keys(): 返回键名的遍历器
values(): 返回键值的遍历器
entries(): 返回所有成员的遍历器
WeakMap
WeakMap结构与Map结构基本类似,唯一的区别是它只接收对象作为键名 (null除外),不接收原始类型的值作为键名
设计WeakMap结构的目的: 键名是对象的弱引用,当对象被回收后,WeakMap自动移除对应的键值对
WeakMap常用于DOM元素,当某个DOM元素被清除,其所对应的WeakMap记录就会自动被移除,这样可以防止内存泄漏。
var map = new WeakMap(); var element = document.querySelector(".element"); map.set(element,"Original"); var value=map.get(element); console.log(value); //"Original" element.parentNot.removeChild(element); element=null; value=map.get(element); console.log(value); //undefined
WeakMap支持has和delete方法,但是没有size属性,也无法遍历它的值,这与WeakMap的键不被计入引用、被垃圾回收机制忽略有关。
循环遍历
es6新增的遍历器和for of循环语句。它们都是针对for循环的局限进行功能改善和增强
遍历器
遍历器 (lterator)是一种协议,任何对象只要部署该协议,就可以完成遍历操作。在ECMAScript 6中,遍历操作特指for of循环。遍历器的作用如下:
为遍历对象的属性提供统一的接口
使得对象的属性能够按次序排列。
es6的遍历器协议规定,部署了next()方法的对象,就具备了遍历器功能.next()方法必须返回一个包含value和done两个属性的对象。其中,value属性是当前遍历位置的值,done属性是一个布尔值,表示遍历是否结束
下面代码定义了一个makelterator()函数,该函数能够返回一个遍历器对象用来遍历参数数组。
function makeIterator(array) { var nextIndex = 0; return { next:function() { return nextIndex < array.length? {value:array[nextIndex++],done:false}; {value:undefined,done:true}; } } } var it = makeIterator(['a','b']); it.next().value; //'a' it.next().value; //'b' it.next().done; //true
状态机
Generator就是一个内部状态的遍历器,即每调用一次遍历器,内部状态发生一次改变(可以理解成发生某些事件) 。ECMAScript 6引入Generator函数,作用就是可以完全控制内部状态的变化,依次遍历这些状态
使用Generator函数
Generator函数就是普通函数,但是有以下两个特征
function关键字后面有一个星号。
函数体内部使用vield语句,定义遍历器的每个成员,即不同的内部状态
下面代码定义了一个Generator函数helloWorldGenerator(),它的遍历器有两个成员“hello”和“world”。调用这个函数,就会得到遍历器。
function helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator();
当调用Generator函数时,该函数并不执行,而是返回一个遍历器 (可以理解成暂停执行)。以后,每次调用这个遍历器的next()方法,就从函数体的头部或者上一次停下来的地方开始执行 (可以理解成恢复执行),直到遇到下一个vield语句为止。也就是说next()方法就是在遍历vield语句定义的内部状态
hw.next() //{value:'hello',done:false} hw.next() //{value:''world},done:false} hw.next() //{value:'ending',done:true} hw.next() //{value:undefined,done:true}
上面代码一共调用了4次next()方法
第一次调用,函数开始执行,直到遇到第一句yield语句为止。next()方法返回一个对象,它的value属性就是当前yield语句的值hello,done属性的值false,表示遍历还没有结束。
第二次调用,函数从上次vield语句停下的地方,一直执行到下一个yield语句。next()方法返回的对象的value属性就是当前vield语句的值world,done属性的值false,表示遍历还没有结束。
第三次调用,函数从上次vield语句停下的地方,一直执行到return语句 (如果没有return语句,就执行到函数结束)。next()方法返的对象的value属性,就是紧跟在return语句后面的表达式的值 (如果没有return语句,则value属性的值为undefined) ,done属性的值true,表示遍历已经结束
第四次调用,此时函数已经运行完毕,next()方法返回对象的value属性为undefineddone属性为true。以后再调用next()方法,返回的都是这个值。
总之,Generator函数使用iterator接口,每次调用next()方法的返回值,就是一个标准的iterator返回值: 有着value和done两人属性的对象。其中,value是vield语句后面那个表达式的值,done是一个布尔值,表示是否遍历结束。
Generator函数的本质,其实是提供一种可以暂停执行的函数。yield语句就是暂停标志,next()方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next()方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined.
由于yield后面的表达式,直到调用next()方法时才会执行,因此等于为JavaScript提供了手动的“情性求值”(Lazy Evaluation) 的语法功能。
yield语句与return语句有点像,都能返回紧跟在语句后面的那人表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。
next()方法
vield语句本身没有返回值,或者说总是返回undefined。next()方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。
下面代码先定义了一个可以无限运行的Generator函数f(),如果next()方法没有参数,每次运行到yield语句,变量reset的值总是undefined。当next方法带一个参数true时,当前的变量reset就被重置为这个参数 (即true) ,因此i会等于-1,下一轮循环就会从-1开始递增
function f() { for(var i=0;true;i++) { var reset = yield i; if(reset) {i=-1;} } } var g = f(); g.next() //{value:0,done:false} g.next() //{value:1,done:false} g.next(true) //{value:0,done:false}
异步操作
Generator函数的这种暂停执行的效果,意味着可以把异步操作写在yield语句里面,等到调用next()方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield语句下面,反正要等到调用next()方法时再执行。所以Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数
下面代码表示,第一次调用loadUI)函数时,该函数不会执行,仅返回一个遍历器。下一次对该遍历器调用next()方法,则会显示Loading界面,并且异步加载数据。等到数据加载完成,再一次使用next()方法,则会隐藏Loading界面。可以看到,这种写法的好处是所有Loading界面的逻辑都被封装在一个函数,按部就班非常清晰
function loadUI() { showLoadingScreen(); yield loadUIDataAsynchronously(); hideLoadingScreen(); } var loader = loadUI(); //加载UI loader.next() //卸载UI loader.next()
for of循环
for of循环可以自动遍历Generator函数,且此时不再需要调用next()方法
下面代码使用for of循环,依次显示5个yield语句的值。这里需要注意,一目next0)方法的返回对象的done属性为true,for of循环就会中止,目不包含该返回对象所以上面代码的return语句返回的6,不包括在for of循环中。
function foo() { yield 1; yield 2; yield 3; yield 4; yield 5; yield 6; } for(let v of foo()) { console.log(v); } //1 2 3 4 5