bibi7 / fe-daily-increase

一个记录开发日常和奇奇怪怪的点的repo
MIT License
5 stars 0 forks source link

for in,for of和迭代器 #27

Open bibi7 opened 5 years ago

bibi7 commented 5 years ago

日常开发遇到了太多遍历相关的场景,稍微写一点东西。

for in

for in可以遍历对象/数组,遍历的i为键/下标:

const array = [1, 2, 3, 4, 5]
for(i in array) {
    console.log(i)  //0 1 2 3 4
    console.log(array[i]) //1 2 3 4 5
}

for in 循环只遍历可枚举属性,甚至会包括原型链上的属性。举例如下:

const obj = {'a':'a'}
Object.defineProperty(Object.prototype, 'toString', {
    enumerable: true,
})
for(i in obj) {
    console.log(i)  
}
//a
//toString

for in自身存在着一定的特性,比如遍历中并不一定能按照顺序遍历,所以并不是很适合用于遍历数组,一般都是结合hasOwnProperty遍历对象。可以瞅瞅mdn上的说法:

数组索引只是具有整数名称的枚举属性,并且与通用对象属性相同。不能保证for ... in将以任何特定的顺序返回索引。for ... in循环语句将返回所有可枚举属性,包括非整数类型的名称和继承的那些。

因为迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素。因此当迭代访问顺序很重要的数组时,最好用整数索引去进行for循环(或者使用 Array.prototype.forEach() 或 for...of 循环)。

for of

for of是es6新增的遍历方法,适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合。与forEach()不同的是,它可以正确响应break、continue和return语句。另外,由于迭代器语法的原因,for of的遍历是按照顺序遍历的。

for of不能直接用于遍历对象,除非对象自身实现迭代器属性,也就是我们要显示的指定迭代。具体语法可见MDN

var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};
for (var value of iterable) {
  console.log(value);
}
// 0
// 1
// 2

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。

一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内。

周末时光不打算做api卡卡西了,迭代器和生成器相关,直接传送门

bibi7 commented 5 years ago

对一些原型链和遍历的原生api不是很熟,稍微记录下。

Object.keys

Object.keys: 返回一个由一个给定对象的自身 可枚举 属性组成的数组,不会追溯到原型链上面的属性。返回的是键名或下标。

const foo = {a: 1}

const bar = Object.create(foo)
bar.b = 2

const baz = Object.create(bar)
baz.c = 3

Object.keys(baz)  //['c']

Reflect.ownKeys()

Reflect.ownKeys: 静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组,与Object.keys不同的是,返回的包括自身对象 不可枚举 的属性。相当于getOwnPropertyNames()Object.getOwnPropertySymbols()的结合。

Object.defineProperty(baz, 'd', {
  enumerable: false,
  value: 'd'
})

Object.keys(baz)
//["c"]
Reflect.ownKeys(baz)
//(2) ["c", "d"]

Reflect.ownKeys({z: 3, y: 2, x: 1}); // [ "z", "y", "x" ]
Reflect.ownKeys([]); // ["length"]

该方法同样会返回Symbol类属性键:

var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {[sym]: 0, "str": 0, "773": 0, "0": 0,
           [sym2]: 0, "-1": 0, "8": 0, "second str": 0};
Reflect.ownKeys(obj);
// [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]

Object.getOwnPropertyNames

Object.getOwnPropertyNames: 返回一个由指定对象的所有自身属性的属性名(包括 不可枚举 属性但不包括Symbol值作为名称的属性)组成的数组。

var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

Object.getOwnPropertyNames(baz)
//["c", "d"]

Object.prototype.isPrototypeOf

prototypeObj.isPrototypeOf(object): 返回布尔值,表示调用对象是否在另一个对象的原型链上。

bar.isPrototypeOf(baz)
//true

Object.getPrototypeOf

Object.getPrototypeOf(object): 返回指定对象的原型

Object.getPrototypeOf(baz) === bar
//true

包括不可枚举属性:

  1. Object.getOwnPropertyNames
  2. Reflect.ownKeys

不包括不可枚举属性:

  1. Object.keys
  2. for in

不包括原型链:

  1. Object.keys
  2. Object.getOwnPropertyNames
  3. Reflect.ownKeys

包括原型链:

  1. for in