我们知道for..of循环首先会向被访问对象请求一个迭代器对象,然后通过调用迭代器对象的next()方法来遍历所有返回值。
对于一个数组,因为数组有内置的@@iterator,所以for..of可以直接用在数组上,利用这个@@iterator来手动遍历数组如下:
var myArray = [1,2,3];
var it = myArray[Symbol.iterator]();
it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { done:true }
@@iterator本身并不是一个迭代器对象,而是一个返回迭代器对象的函数——这点非常精妙并且非常重要。
Symbol
了解@@iterator是个什么东西,首先要了解Symbol类型。它是ES6新增加的一种基本数据类型。
数据类型 “symbol” 是一种原始数据类型,该类型的性质在于这个类型的值可以用来创建匿名的对象属性。
该数据类型通常被用作一个对象属性的键值——当你想让它是私有的时候。例如,symbol 类型的键存在于各种内置的 JavaScript 对象中。
特别地:
Symbol 类具有一些静态属性,对于匿名命名来说,这带有一点讽刺意味。这类属性只有几个; 它们是所谓的“众所周知”的 symbol。 它们是在某些内置对象中找到的某些特定方法属性的 symbol。 暴露出这些 symbol 使得可以直接访问这些行为;这样的访问可能是有用的,例如在定义自定义类的时候。
普遍的 symbol 的例子有:“Symbol.iterator”用于类似数组的对象,“Symbol.search”用于字符串对象。
用法如下:
const foo = Symbol()
const bar = Symbol()
let obj = {}
obj[foo] = "foo"
obj[bar] = "bar"
JSON.stringify(obj) // {}
Object.keys(obj) // []
Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [ foo, bar ]
defineProperty
在创建普通属性时属性描述符会使用默认值,我们也可以使用Object.defineProperty(..)
来添加一个新属性或者修改一个已有属性(如果它是configurable
)并对特性进行设置。
对于一个普通对象属性来说,除了value之外,还包含另外三个特性:writable(可写)、enumerable(可枚举)和configurable(可配置)
举例来说:
var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: true,
configurable: true,
enumerable: true
} );
myObject.a; // 2
遍历对象
通过上文的预备知识,我们可以这样去定义@@iterator以便遍历对象
var myObject = {
a: 2,
b: 3
};
Object.defineProperty( myObject, Symbol.iterator, {
enumerable: false,
writable: false,
configurable: true,
value: function() {
var o = this;
var idx = 0;
var ks = Object.keys( o );
return {
next: function() {
return {
value: o[ks[idx++]],
done: (idx > ks.length)
};
}
};
}
} );
// 手动遍历myObject
var it = myObject[Symbol.iterator]();
it.next(); // { value:2, done:false }
it.next(); // { value:3, done:false }
it.next(); // { value:undefined, done:true }
// 用for..of遍历myObject
for (var v of myObject) {
console.log( v );
}
// 2
// 3
网友评论