es6语法最全深入浅出(三)

简介: es6语法最全深入浅出

箭头函数

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

相关文章
|
4月前
|
前端开发
【面试题】如何使用ES6 ... 让代码优雅一点?
【面试题】如何使用ES6 ... 让代码优雅一点?
|
6月前
|
Web App开发 JavaScript 前端开发
|
6月前
|
存储 JavaScript 前端开发
|
6月前
|
前端开发 JavaScript 编译器
|
6月前
|
JavaScript
ES6的基础用法
对js es6的用法进行简单介绍
|
9月前
|
前端开发
es6 语法简单使用2
es6 语法简单使用
40 0
|
9月前
|
网络架构
es6 语法简单使用1
es6 语法简单使用
51 0
|
11月前
|
JavaScript 前端开发
|
11月前
ES6 —— 函数进阶
ES6 —— 函数进阶
|
Web App开发 前端开发 JavaScript
ES9语法知识点回顾
ES9语法知识点回顾
229 31