ECMAScript 6 新特性总结

本文作者:IMWeb 张颖 原文出处:IMWeb社区 未经同意,禁止转载

前言

个人感觉ECMAScript 6总体上来说:添加了块级作用域,增加了一些语法糖,增强了字符串的处理,引入Generator函数控制函数的内部状态的变化,原生提供了Promise对象,引入了Class(类)的概念,并且在语言规格的层面上实现了模块功能。 注:

1、ES6的支持性可以查看:http://kangax.github.io/compat-table/es6/

2、Google V8引擎已经部署了ES6的部分特性,使用Node.js 0.12版,可以试验这些特性。

3、使用Traceur转码器、Babel转码器等可以将ES6方式编写的程序转为ES5代码。

一、let和const命令

1.1 块级作用域

一个花括号{}代表一个块级作用域,作用域嵌套时外层代码块不受内层代码块的影响,立即执行匿名函数(IIFE)原本的作用是为了形成局部作用域,防止变量污染,块级作用域的的出现使得获得广泛应用的立即执行匿名函数不再必要了。

需要注意: 函数本身的作用域,在其所在的块级作用域之内。ES5存在函数提升,不管函数在何处声明,函数声明都会提升到当前作用域的顶部,得到执行;而ES6支持块级作用域,其内部声明的函数皆不会影响到作用域的外部。

function f() { console.log('I am outside!'); }
(function () {
  if(false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());
//ES5 result: I am inside!
//ES6 result: I am outside!

1.2 let命令

let命令,用来声明变量,它的用法类似于var,但是所声明的变量,只在let命令所在代码块内有效。

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); 
//result: 10
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6]();
//result: 6

使用时有几点需要需要注意:

  • let不会像var一样声明提前,只能在定义之后使用,之前使用会抛出ReferenceError;
  • 并且只要作用域内有let声明的变量,这个变量就会被绑定,不受原来变量声明规则的影响。即ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些命令,就会报错。这在语法上,称为“暂时性死区”(temporal dead zone,TDZ)。
var tmp = 123;
if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}
  • 函数的作用域是其声明时所在的作用域。
  • 不允许在相同作用域内,重复声明同一个变量。因此,不能在函数内部重新声明参数。

1.3 const命令

const用来声明常量,一旦声明,常量的值就不能改变。

使用时需注意:

  • 对常量重新赋值不会报错,只会默默地失败。
  • 与let命令相同,只在声明所在的块级作用域内有效。
  • const命令也不存在提升,只能在声明的位置后面使用,提前使用同样会抛出ReferenceError。
  • 同样不可重复声明。
  • const命令只是指向变量所在的地址,如果将const变量赋值为一个对象,则此常量储存的是一个地址,不可变的只是这个地址,但对象本身是可变的,依然可以为其添加新属性。如果真的想将对象冻结,应该使用Object.freeze方法。
const foo = {};
foo.prop = 123;

foo.prop
// 123

foo = {} // 不起作用
const foo = Object.freeze({});
foo.prop = 123; // 不起作用

1.4 全局对象的属性

全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js指的是global对象。ES5规定,所有全局变量都是全局对象的属性。

ES6规定,var命令和function命令声明的全局变量,属于全局对象的属性;let命令、const命令、class命令声明的全局变量,不属于全局对象的属性。

二、变量的解构赋值

ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。解构只能用于数组或对象,所以应该注意,其他原始类型的值都可以转为相应的对象,除了undefined和null。

本质上,解构写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

  • 对数组的结构赋值,允许指定默认值。(只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值,所以对于Set结构是可以使用的。)
let [foo, [[bar], baz]] = [1, [[2], 3]];
let [x, y='b'] = ['a', undefined]; // x='a', y='b'
let [a, b, c] = new Set(["a", "b", "c"]);
  • 对对象的解构,属性没有次序,变量必须与属性同名,才能取到正确的值。对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。对象的解构同样可以指定默认值,并且可以与函数参数的默认值一起使用。
var { foo, bar } = { foo: "aaa", bar: "bbb" };

var {x, y = 5} = {x: 1};
console.log(x, y) // 1, 5

let { log, sin, cos } = Math;
function move({x=0, y=0} = {}) {
  return [x, y];
}

move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

三、字符串的扩展

3.1 字符串处理的增强

JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode码点大于0xFFFF的字符),JavaScript会认为它们是两个字符。在ECMAScript6中,增强了对码点大于0xFFFF的字符的整体处理和正则匹配。

具体增加的一些处理方法如下:

  • codePointAt():会正确返回四字节的UTF-16字符的码点,对于那些两个字节储存的常规字符,它的返回结果与charCodeAt方法相同。
  • String.fromCodePoint():正确返回编号大于0xFFFF的码点对应的字符,弥补了String.fromCharCode方法的不足。
  • at():返回字符串给定位置的字符,如果该字符的Unicode编号大于0xFFFF,可以返回正确的字符。而charAt()方法只能返回UTF-16编码的第一个字节,不能正确返回。
  • 字符的Unicode表示法:"\u{20BB7}"的形式可以正确表示超出\uFFFF的双字节字符。
  • 正则表达式的u修饰符:对于正则表达式中的.字符、\u{20BB7}大括号字符、量词、\S、i修饰符等,如果需要正确识别码点编号大于0xFFFF的字符,必须添加了u修饰符。比如:
var s = " ";

/^.$/.test(s) // false
/^.$/u.test(s) // true
  • normalize():ES6提供String.prototype.normalize()方法,用来将Unicode中字符的不同表示方法统一为同样的形式。(目前不能识别三个或三个以上字符的合成。)
  • includes():返回布尔值,表示是否找到了参数字符串。支持第二个参数,表示开始搜索的位置。
  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。支持第二个参数,表示开始搜索的位置。
  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。支持第二个参数,表示对前n个字符进行搜索。
  • repeat():返回一个新字符串,表示将原字符串重复n次。
  • “粘连”(sticky)修饰符y:全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始,y修饰符确保匹配必须从剩余的第一个位置开始。换而言之,y修饰符号隐含了头部匹配的标志?。 3.2 模板字符串 模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

使用规则:

  • 在模板字符串中嵌入变量,需要将变量名写在${}之中。
  • 如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
  • 大括号内部可以进行运算,以及引用对象属性,其中还能调用函数。 // 普通字符串
`In JavaScript'\n' is a line-feed.`

// 多行字符串
console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

//反斜杠转义
var greeting = `\`Yo\` World!`;

//对象属性
var obj = {x: 1, y: 2};
console.log(`${obj.x + obj.y}`)

//函数调用
function fn() {
  return "Hello World";
}
console.log(`foo ${fn()} bar`);

模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。函数的参数第一个为模板字符串中没有变量替换的部分组成的数组,第一个参数之后的参数,都是模板字符串各个变量依次被替换后的值。

var a = 5;
var b = 10;

tag`Hello ${ a + b } world ${ a * b}`;
//等价于
tag(['Hello ', ' world '], 15, 50);

注意处理函数的第一个参数,拥有一个raw属性。它也是一个数组,成员与处理函数的第一个参数完全一致,唯一的区别是字符串是被转义前的原始格式。

四、数值的扩展

4.1 二进制和八进制表示法

二进制和八进制数值的新的写法,分别用前缀0b和0o表示

0b111110111 === 503 // true
0o767 === 503 // true

4.2 扩展函数

  • Number.isFinite()用来检查一个数值是否非无穷(infinity);Number.isNaN()用来检查一个值是否为NaN。它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,非数值一律返回false。
  • Number.parseInt(), Number.parseFloat():ES6将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
  • Number.isInteger():用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。
  • Number.isSafeInteger()则是用来判断一个整数是否落在Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量表示的上下限范围内。
  • ES6在Math对象上还提供了许多新的数学方法:
    • Math.trunc(x)方法用于去除一个数的小数部分,返回整数部分;
    • Math.sign(x)方法用来判断一个数到底是正数、负数、还是零;
    • Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine);
    • Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine);
    • Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent);
    • Math.cbrt(x) 返回x的立方根;
    • Math.clz32(x) 返回x的32位二进制整数表示形式的前导0的个数;
    • Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine);
    • Math.expm1(x) 返回e?x - 1;
    • Math.fround(x) 返回x的单精度浮点数形式;
    • Math.hypot(...values) 返回所有参数的平方和的平方根;
    • Math.imul(x, y) 返回两个参数以32位整数形式相乘的结果;
    • Math.log1p(x) 返回1 + x的自然对数;
    • Math.log10(x) 返回以10为底的x的对数;
    • Math.log2(x) 返回以2为底的x的对数;
    • Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)。

五、数组的扩展

5.1 数组推导

数组推导就是直接通过现有数组生成新数组的一种简化写法,通过for...of结构,允许多重循环。注:新数组会立即在内存中生成,这时如果原数组是一个很大的数组,将会非常耗费内存。

var a1 = [1, 2, 3, 4];
var a2 = [for (i of a1) i * 2];
a2 // [2, 4, 6, 8]

var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ];
[for (year of years) if (year > 2000 && year < 2010) year];
// [ 2006]

5.2 数组处理的扩展方法

  • Array.from():用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象,其中包括ES6新增的Set和Map结构。Array.from()还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理。
Array.from({ 0: "a", 1: "b", 2: "c", length: 3 });
// [ "a", "b" , "c" ]

Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
  • Array.of()方法用于将一组值,转换为数组。弥补数组构造函数Array()的不足。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]

Array(3) // [undefined, undefined, undefined]
  • 数组实例的find()用于找出第一个符合条件的数组元素;数组实例的findIndex()用于返回第一个符合条件的数组元素的位置。这两个方法都可以发现NaN,弥补了IndexOf()的不足。
  • 数组实例的fill()使用给定值,填充一个数组。
  • 数组实例的entries(),keys()和values()用于遍历数组,它们都返回一个遍历器,可以用for...of循环进行遍历。keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

六、对象的扩展

6.1 增强的对象写法

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

var Person = {
  name: '张三',

  //等同于birth: birth
  birth,

  // 等同于hello: function ()...
  hello() { console.log('我的名字是', this.name); }
};

ES6允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内,允许变量渗入 key 中。

var lastWord = "last word";

var a = {
    "first word": "hello",
    [lastWord]: "world"
};

a["first word"] // "hello"
a[lastWord] // "world"
a["last word"] // "world"

6.2 Object.is()和Object.assign()

Object.is():用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致,不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

Object.assign():用来将源对象(source)的所有可枚举属性,复制到目标对象(target)。它至少需要两个对象作为参数,第一个参数是目标对象,后面的参数都是源对象。如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

var target = { a: 1, b: 1 };

var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

6.3 proto属性

  • 用来读取或设置当前对象的prototype对象。
  • Object.setPrototypeOf()方法的作用与proto相同,用来设置一个对象的prototype对象,它是ES6正式推荐的设置原型对象的方法。
  • Object.getPrototypeOf()方法用于读取一个对象的prototype对象。(浏览器(包括IE11)早就部署了这个属性,只是在 ES6 才被纳入标准中,之前我们常用这个属性来判断是否为 IE 。)6.4 Symbol Symbol是一种新的原始数据类型,表示独一无二的ID,它通过Symbol函数生成。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。Symbol函数可以接受一个字符串作为参数,表示Symbol实例的名称。
var mySymbol = Symbol('Test');
mySymbol.name
// Test

// 有参数的情况
var s1 = Symbol("foo");
var s2 = Symbol("foo");

s1 === s2 // false

//不能与其他类型的值进行运算
var sym = Symbol('My symbol');
'' + sym
// TypeError: Cannot convert a Symbol value to a string

//可以转换为字符串
String(sym)
// 'Symbol(My symbol)'

注意:

  • Symbol.for():在全局环境中搜索指定key的Symbol值,如果存在就返回这个Symbol值,否则就新建一个指定key的Symbol值并返回。与Symbol()的区别是,Symbol.for()会被登记在全局环境中供搜索,不会建立相同Key的Symbol值,而Symbol()则完全相反。
  • Symbol.keyFor()方法返回一个已登记的Symbol类型值的key。
  • Symbol作为属性名,该属性不会出现在for...in循环中,也不会被Object.keys()、Object.getOwnPropertyNames()返回,要使用对应的Object.getOwnPropertySymbols方法,以及Object.getOwnPropertyKeys方法获取相应的Symbol属性名。 6.5 Proxy Proxy用于修改某些操作的默认行为,等于在目标对象之前,架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。而Proxy.revocable()方法则返回一个可取消的Proxy实例。
var person = {
  name: "张三"
};

var proxy = new Proxy(person, {
    get: function(target, property) {
        return property in target ? target[property] : "米有";
    }
});

proxy.name // "张三"
proxy.title // "米有"
let target = {};
let handler = {};

let {proxy, revoke} = Proxy.revocable(target, handler);

proxy.foo = 123;
proxy.foo // 123

revoke();
proxy.foo // TypeError: Revoked

Proxy支持的拦截操作:

属性

返回值

拦截操作

defineProperty(target, propKey, propDesc)

布尔值(Boolean)

Object.defineProperty(proxy, propKey, propDesc)

deleteProperty(target, propKey)

布尔值

delete proxy[propKey]

enumerate(target)

遍历器

for (x in proxy)

get(target, propKey, receiver)

类型不限

对象属性的读取

set(target, propKey, value, receiver)

布尔值

对象属性的设置

getOwnPropertyDescriptor(target, propKey)

属性的描述对象

Object.getOwnPropertyDescriptor(proxy, propKey)

getPrototypeOf(target)

对象

Object.getPrototypeOf(proxy)

has(target, propKey)

布尔值

propKey in proxy

isExtensible(target)

布尔值

Object.isExtensible(proxy)

ownKeys(target)

数组

Object.getOwnPropertyPropertyNames(proxy)、Object.getOwnPropertyPropertySymbols(proxy)、Object.keys(proxy)

preventExtensions(target)

布尔值

Object.preventExtensions(proxy)

setPrototypeOf(target, proto)

布尔值

Object.setPrototypeOf(proxy, proto)

apply(receiver, ...args)

不限

函数的调用、call和apply操作

construct

对象

Proxy实例作为构造函数调用的操作,比如new proxy(···)

### 6.6 Object.observe(),Object.unobserve() Object.observe()方法用来监听对象(以及数组)的变化。一旦监听对象发生变化,就会触发回调函数。Object.unobserve()方法用来取消监听。 Object.observe方法目前共支持监听以下六种变化:

操作

作用

add

添加属性

update

属性值的变化

delete

删除属性

setPrototype

设置原型

reconfigure

属性的attributes对象发生变化

preventExtensions

对象被禁止扩展

七、函数的扩展

7.1 函数参数的默认值

ES6允许为函数的参数设置默认值,使用=形式直接写在参数定义的后面。

使用注意事项:

  • 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。
  • 参数默认值所处的作用域,不是全局作用域,而是函数作用域。
function Point(x = 0, y = 0) {
  this.x = x;
  this.y = y;
}

var p = new Point();
// p = { x:0, y:0 }
  • 还可以设置双重默认值,比如下面代码调用函数fetch时,如果不含第二个参数,则默认值为一个空对象;如果包含第二个参数,则它的method属性默认值为GET。
fetch(url, { method='GET' } = {}){
  console.log(method);
}

7.2 rest运算符(...)

...+变量名形式与...+数组形式相当于互逆操作:

  • ...变量名:将多余的参数放入一个数组中,rest参数必须在最后一个;函数的length属性,不包括rest参数。
function add(...values) {
   let sum = 0;

   for (var val of values) {
      sum += val;
   }

   return sum;
}

add(2, 5, 3) // 10
(function(a, ...b) {}).length  // 1
  • ...数组:将一个数组转为用逗号分隔的参数序列。
const date = new Date(...[2015, 1, 1]);

7.3 箭头函数(=>)

函数定义的简化表示法,

var f = () => 5;
// 等同于
var f = function (){ return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
    return num1 + num2;
};

使用箭头函数需要注意:

  • 函数体内的this对象,绑定定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。7.4 尾调用优化 尾调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是调用另一个函数。

为什么尾调用会优化:函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。所以如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。

尾调用的应用——尾递归:递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。比如阶乘函数的为递归改写:

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

八、Set和Map数据结构

8.1 数据结构Set

Set结构类似于数组,但是成员的值都是唯一的,没有重复的值。

var items = new Set([1,2,3,4,5,5,5,5]);

for (i of items) {console.log(i)}
// 1 2 3 4 5
items.size 
// 5

注意:向Set加入值的时候,不会发生类型转换,所以5和“5”是两个不同的值。Set内部判断两个值是否不同,使用的算法类似于精确相等运算符(===),唯一的例外是NaN等于自身。这意味着,两个对象总是不相等的。

let set = new Set();

set.add({})
set.size // 1

set.add({})
set.size // 2

Set结构有以下属性:

  • Set.prototype.constructor:构造函数,默认就是Set函数。
  • Set.prototype.size:返回Set的成员总数。

Set数据结构有以下方法:

  • add(value):添加某个值,返回Set结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。

Set结构有一个values方法,返回一个遍历器,同时Set结构的默认遍历器就是它的values方法,所以可以直接用for...of循环进行遍历。

WeakSet是一个与Set类似的结构,也是不重复的值的集合。但是,它与Set有两个区别:

  1. WeakSet的成员只能是对象,而不能是其他类型的值。任何具有iterable接口的对象,都可以作为WeakSet的对象,比如数组或者类数组的对象。
  2. WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。(WeakSet的一个用处,是储存DOM节点,而不用担心这些节点从文档移除时,会引发内存泄漏。)8.2 数据结构Map Map结构类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

注意Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。只有对同一个对象的引用,Map结构才将其视为同一个键。

var map = new Map();

map.set(['a'], 555); 
map.get(['a']) // undefined

Map结构的属性和方法:

  • size:返回成员总数。
  • set(key, value):设置一个键值对。
  • get(key):读取一个键。
  • has(key):返回一个布尔值,表示某个键是否在Map数据结构中。
  • delete(key):删除某个键。
  • clear():清除所有成员。
  • keys():返回键名的遍历器。
  • values():返回键值的遍历器。
  • entries():返回所有成员的遍历器。

同样与WeakSet类似,WeakMap结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受原始类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制,有助于防止内存泄漏。

九、遍历器(Iterator)

9.1 Iterator(遍历器)

遍历器属于一种接口规格,任何数据结构只要部署这个接口,就可以完成遍历操作,即依次处理该结构的所有成员。它的作用有两个,一是为各种数据结构,提供一个统一的、简便的接口,二是使得数据结构的成员能够按某种次序排列。

简化的讲,所谓Iterator接口,就是指调用这个接口,会返回一个遍历器对象。该对象具备next方法,每次调用该方法,会返回一个具有value和done两个属性的新对象,指向部署了Iterator接口的数据结构的一个成员。下面代码就展示了一个典型的Iterator接口:

function idMaker(){
  var index = 0;

  return {
    next: function(){
      return {value: index++, done: false};
    }
  }
}

var it = idMaker();

it.next().value // '0'
it.next().value 

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏代码拾遗

反射基础之Field

java.lang.reflect.Field 类的方法可以查询字段的信息。比如:名字,类型,修饰符和注解。同样也有方法可以动态访问和修改字段的值。

1601
来自专栏刘望舒

kotlin到底好在哪里?

最近在学kotlin,虽然还没有像其他博主一样立马就爱上它.但是不得不说,kotlin对比起java还是有不少优势的. 1、语法简洁 首先是语法比较简洁,能不简...

3577
来自专栏Hongten

python开发_python代码风格(coding style)

1261
来自专栏函数式编程语言及工具

Scalaz(17)- Monad:泛函状态类型-State Monad

  我们经常提到函数式编程就是F[T]。这个F可以被视为一种运算模式。我们是在F运算模式的壳子内对T进行计算。理论上来讲,函数式程序的运行状态也应该是在这个运算...

3048
来自专栏xingoo, 一个梦想做发明家的程序员

Oozie分布式工作流——EL表达式

oozie支持使用EL(expression language)表达式。 基本的EL常量 KB MB GB TB PB 基本EL函数 string fir...

2678
来自专栏牛肉圆粉不加葱

[6] - 类和对象之进阶(二)

Scala 中的可见性非常灵活且复杂,这篇文章希望通过大量的示例来说清楚各种情况下的可见性是怎么样的。

732
来自专栏偏前端工程师的驿站

(cljs/run-at (->JSVM :browser) "语言基础")

前言  两年多前知道cljs的存在时十分兴奋,但因为工作中根本用不上,国内也没有专门的职位于是搁置了对其的探索。而近一两年来又刮起了函数式编程的风潮,恰逢有幸主...

2267
来自专栏余林丰

多个构造器参数使用构建器

标题一眼看过去可能不是很明白要讲什么,先来看看下面一段代码。 1 package example; 2 3 /** 4 * 重叠构造器 5 * ...

2008
来自专栏AndroidTv

学点Groovy来理解build.gradle代码

在写这篇博客时,搜索参考了很多资料,网上对于 Groovy 介绍的博客已经特别多了,所以也就没准备再详细的去介绍 Groovy,本来也就计划写一些自己认为较重要...

4118
来自专栏LIN_ZONE

java反射机制的简单使用

通过上面的代码可以获得 运行时类的对象,然后下面使用运行时类的对象来构造一个反射工具类,通过下面这个类 可以利用反射机制实例化该类的对象,设置对象的属性并调用对...

902

扫码关注云+社区

领取腾讯云代金券