Skip to content

for循环和forEach底层原理 1.基于var声明的时候,for和while性能差不多, 2.基于let声明的时候,for循环性能更好 原理: for循环的i保存在块级上下文中,每轮循环结束i都会被释放,不会占用全局上下文空间 while循环的i只能定义在条件外,所以是全局上下文的i,循环没结束就不能释放,一直被占用

3.重写forEach

for...in循环机制 => 主要用来遍历对象key 1.优先迭代数字属性且从小到大(无法解决) 2.会遍历所有私有和公有的可枚举属性(私有属性大部分是可枚举的(length就不可以), 公有属性出现在所属类的原型上,也有部分是可枚举的) 带来问题: 1.性能最差(原型链一级一级查找) 2.公有属性一般是不需要迭代的(使用if (!o.hasOwnProperty(key)) break解决, 但是性能还是很受影响,因为要先找到一个可枚举的公有属性,找不到还是会一直找下去 ) 3.无法迭代Symbol类型的私有属性

js
const o = {
  // 都是私有属性
  name: 'Tom',
  age: 20,
  [Symbol('foo')]: 'foo',
  0: 100,
  1: 200
}

// 定义公有属性
Object.prototype.bar = 'bar'
Object.prototype[10] = 300

for (let key in o) {
  // if (!o.hasOwnProperty(key)) break // 不拿公有属性
  console.log(key)
  // 0 私有数字优先
  // 1 私有数字优先
  // name 私有其他属性
  // age 私有其他属性
  // 10 公有数字属性优先
  // bar 公有其他属性
}

怎么获取Symbol属性?

js
Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))
// 或
const keys = Object.keys(o).concat(Object.getOwnPropertySymbols(o))

keys.forEach(key => {
  console.log(key, o[key])
})

for...of循环机制 => 遍历value => Array, Map, Set, String, TypedArray, arguments... 1.原理是Iterator迭代器 2.实现对象使用for...of

js
class Iterator {
  constructor (assemble) {
    this.assemble = assemble
    this.index = 0
  }

  next () {
    let { index, assemble } = this

    if (index < assemble.length) {
      // this.index++,不是index++
      return { done: false, value: assemble[this.index++]}
    }

    return { done: true, value: undefined}
  }
}

const iterator = new Iterator([1, 2, 3, 4, 5])
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())

const arr = [1, 2, 3, 4, 5]

arr[Symbol.iterator] = function () {
  let _this = this
  let index = 0

  return {
    next () {
      if (index < _this.length) {
        return { done: false, value: _this[index++]}
      } 

      return { done: true, value: undefined}
    }
  }
}

for (let i of arr) {
  console.log(i)
}

Object.prototype[Symbol.iterator] = function () {
  const _this = this
  const keys = Object.keys(_this).concat(Object.getOwnPropertySymbols(_this))
  let index = 0

  return {
    next () {
      if (index < keys.length) {
        return {
          done: false,
          value: _this[keys[index++]]
        }
      }

      return {
        done: true,
        value: undefined
      }
    }
  }
}

各种循环的性能对比

js
const arr = new Array(10000000).fill(0)
console.time('for')
for (var i = 0; i < arr.length; i++) {

}
console.timeEnd('for') // 26.59423828125 ms

console.time('while')
var i = 0
while(i < arr.length) {
  i++
}
console.timeEnd('while') // 26.297119140625 ms

console.time('for')
for (let i = 0; i < arr.length; i++) {

}
console.timeEnd('for') // 5.042236328125 ms

console.time('while')
let i = 0
while(i < arr.length) {
  i++
}
console.timeEnd('while') // 26.242919921875 ms

console.time('forEach')
arr.forEach(item => {})
console.timeEnd('forEach') // 83.221923828125 ms

console.time('for...in')
for (let key in arr) {}
console.timeEnd('for...in') // 1980.77001953125 ms

console.time('for...of')
for (let key of arr) {}
console.timeEnd('for...of') // 144.93798828125 ms

// for/while > forEach > for...of > for...in

封装一个方面遍历对象所有私有属性 支持回调返回值为false时, 结束循环

js
function eachObject(target, callback) {
  let keys = Object.keys(target)

  // 不兼容Symbol的浏览器就不用拿Symbol属性了
  if (typeof Symbol !== 'undefined') {
    keys = keys.concat(Object.getOwnPropertySymbols(target))
  }

  let i = 0,
    length = keys.length,
    key,
    value,
    result

  // 写成for是因为forEach不支持结束迭代
  for (; i < length; i++) {
    key = keys[i]
    value = target[key]
    result = callback(key, value)

    // 若回调返回false,结束迭代
    if (result === false) {
      break
    }
  }
}