JavaScript中的各种遍历

803 阅读7分钟

前言

一直想写些总结性的文章,事情太多,无从下手,加上太懒,迟迟没有动笔。一天天过去,日积月累的,心头像被压了一层层的稻草,难受极了。不想太多,一件件地做,每做一件就少一件,尽管杂乱繁重,最终总会清空的。

在学习和实践过程中也许会碰到更多的有关遍历的知识,每遇到一点就追加一点,持续更新。现在把能想到的总结下来。

对象的遍历

对象属性的遍历,方法有以下几种

  • for...in
  • Object.keys()
  • Object.getOwnPropertyNames()

1. for...in

for...in是通过in运算符遍历对象中的可枚举属性,包括原型中的属性

// 构造函数中定义私有属性
function Animal(name) {
    this.name=name
    this.run = function() {
    	console.log(this.name + ' is running')
    }
}
// 定义原型上的共享属性
Animal.prototype.age=12
Animal.prototype.say = function() {
    console.log(this.name + ' is saying')
}
// 创建对象,枚举对象中的属性
var animal = new Animal('kikky')
for (prop in animal) {
    console.log(prop + ':' + animal[prop])
}

输出结果为:

name:kikky
run:function() {
    console.log(this.name + ' is running')
}
age:12
say:function() {
    console.log(this.name + ' is saying')
}

通常为了只枚举对象的私有属性,会用一个判断函数hasOwnProperty()来过滤掉原型中的属性。

var animal = new Animal('kikky')
for (prop in animal) {
    if (animal.hasOwnProperty(prop)) {
        console.log(prop + ':' + animal[prop])
    }
}

输出结果为:

name:kikky
run:function() {
    console.log(this.name + ' is running')
}

如果只想枚举对象的私有属性,也可以直接用Object.keys()

2. Object.keys()

Object.keys()是构造函数Object的一个方法函数,它获取的是自身属性中的可枚举属性,不包括原型中的属性,返回的是一个包含对象属性名称的数组。

// 构造函数中定义私有属性
function Animal(name) {
    this.name=name
    this.run = function() {
    	console.log(this.name + ' is running')
    }
}
// 定义原型上的共享属性
Animal.prototype.age=12
Animal.prototype.say = function() {
    console.log(this.name + ' is saying')
}
// 创建对象,枚举对象中的属性
var  animal= new Animal('kikky')
var props = Object.keys(animal)
console.log(props) // 输出 ["name", "run"]

3. Object.getOwnPropertyNames()

Object.getOwnPropertyNames()是构造函数Object的另一个方法函数,它返回的是对象自身属性中的所有属性(不论是否可枚举),不包括原型属性。

// 构造函数中定义私有属性
function Animal(name) {
    this.name = name
    this.address = 'somewhere' // 新添加一个address属性,用来测试可枚举性
    this.run = function() {
    	console.log(this.name + ' is running')
    }
}
// 定义原型上的共享属性
Animal.prototype.age=12
Animal.prototype.say = function() {
    console.log(this.name + ' is saying')
}

// 创建对象
var animal = new Animal('kikky')
// 通过defineProperty()函数将address属性的可枚举性设为否
Object.defineProperty(animal, 'address', {
    enumerable: false, // 设置为不可枚举
    value: 'anywhere'
})

// 输出属性
var allProps = Object.getOwnPropertyNames(animal)
console.log(allProps) // 输出 ["name", "run", "address"]
// 输出可枚举属性作为对比
var enumProps = Object.keys(animal)
console.log(enumProps) // 输出 ["name", "run"]

小结

  • for..in 遍历的是对象的可枚举属性,包括原型
  • Object.keys 遍历的是对象可枚举属性,不包括原型
  • Object.getOwnPropertNames 遍历的是对象的所有属性,不包括原型

数组

  1. 数组项的全部遍历,方法有以下几种
    • for 循环
    • forEach()函数 (ES5新增)
    • map()函数 (ES5新增)
    • for...of (ES6新增)
    • for...in (特殊方式)
  2. 数组项的有操作的遍历,方法有以下几种
    • filter()函数 (ES5新增)
    • every()函数 (ES5新增)
    • some()函数 (ES5新增)
    • reduce()函数 (ES5新增)
    • reduceRight()函数 (ES5新增)

数组项的全部遍历

全部遍历所列举的方法,是指主要用来枚举数组各项的方法。

1. for循环

for循环遍历是最原始,也是性能最高的一种遍历方法。

var array = [1,4,7,9]
for (var i = 0, len = array.length; i < len; i++) {
    console.log(array[i]) // 输出 1 4 7 9
}

2. forEach函数

forEach()函数是构造函数Array的原型上的函数,即Array.prototype.forEach,因此所有数组都可以用这个方法。它接受一个处理函数作为参数,处理函数的参数分别是数组的元素、数组索引、和数组本身。函数没有返回值,因此主要用来对数组的过程处理。

var array = ['a', 'b', 'c']
array.forEach(function(item, index, array) { // 处理函数作为参数
    console.log(index + ' is ' + item)
})

输出

0 is a
1 is b
2 is c

3. map函数

map()函数也是构造函数Array的原型上的函数,即Array.prototype.map,所以所有数组都可以用这个方法。和forEach一样,它接受一个处理函数作为参数,处理函数的参数分别是数组的元素、数组索引、和数组本身;与forEach不同的是,函数有返回值,返回的是处理函数return的值组成的数组。

var array = ['a', 'b', 'c']
var result = array.map(function(item, index, array) { // 处理函数作为参数
    console.log(index + ' is ' + item) // 过程处理
    return item + index // 返回结果
})
console.log(result) // 输出["a0", "b1", "c2"]

4. for...of

for...of是ES6新增的语法,它不仅可以用来遍历数组,还可以遍历字符串,以及ES6中的Map,Set。

var array = ['a', 'b', 'c']
for (let item of array) {
    console.log(item) // 输出 a b c
}

5. 其他特殊方式

由于数组也是对象,所以能遍历对象的方法,自然也可以用来遍历数组,只是不推荐这样用。例如:

用for...in遍历数组

var array = ['a', 'b', 'c']
// for...in遍历的是对象属性,数组中的索引就相当于对象的属性,因此枚举出来的是索引
for (let index in array) {
    console.log(index + ':' + array[index])
}

输出

0:a
1:b
2:c

数组项的有结果处理的遍历

这里列举的方法通常指对数组项有操作或选择的遍历。

1. filter()函数

filter函数是过滤函数,根据传入的处理函数,得到处理函数返回值为true的元素所组成的数组,函数结果为过滤后得到的数组。

function getEven(item, index, array) {
    return item % 2 === 0
}
var array = [1, 2, 3, 5, 8]
var evenArray = array.filter(getEven)
console.log(evenArray) // 输出 [2, 8]

2. every()函数

every()函数是一个判断函数,检测是否每个元素都通过了测试。通过传入的处理函数判断,如果每个元素处理后得到的返回值都为true,则最后结果为true。

every()函数不会遍历所有数组项,当遇到处理结果为false时,函数立即返回false,否则遍历到最后,直到返回true。

var array = [1, 2, 3, 5, 8]
// 判断是否每个元素都小于3
var result = array.every(function(item, index, array) {
    console.log('processing: ' + item)
    return item < 3
})
console.log('result: ' + result)

输出结果

processing: 1
processing: 2
processing: 3  // 执行到3后停止了,3不小于3
result: false

3. some()函数

some()函数是判断函数,检测数组中是否有符合条件的元素。只要有元素的处理结果为true,则返回值为true。

some()函数也不会遍历所有项,只要遇到处理结果为true,立即返回true;否则执行到最后,直到返回false。

var array = [1, 2, 3, 5, 8]
// 判断是否存在小于3的元素
var result = array.some(function(item, index, array) {
    console.log('processing: ' + item)
    return item < 3
})
console.log('result: ' + result)

输出结果

processing: 1 // 找到一个满足条件的元素立即返回了
result: true

4. reduce()函数

reduce()函数是聚合函数,从左到右两两合并,缩减执行。接受两个参数,第一个参数是处理函数,第二个参数为初始值,可选,是作为处理函数第一次调用时的第一个值。语法:array.reduce(callback, [initialValue])

处理函数包含四个参数

  • previousValue 上一次调用处理函数返回的值,或者第一次调用时的初始值
  • currentValue 数组中当前被处理的元素
  • index 当前元素的索引
  • array 当前数组对象

当提供了初始值时,处理函数的previousValue即为初始值,currentValue为数组第一个元素;没提供初始值时,preveiousValue为数组第一个元素,currentValue为第二个元素。

function add(prev, cur, index, array) {
    return prev + cur
}
var array = [1, 2, 3, 5, 8]
var sum = array.reduce(add)
var sumWithBase = array.reduce(add, 100) // 提供初始值
console.log(sum, sumWithBase) // 输出 19 119

5. reduceRight()函数

reduceRight()函数与reduce()函数的功能一致,只不过聚合方向是从右到左,即从最后一个元素开始遍历。

function sub(prev, cur, index, array) {
    return prev - cur
}
var array = [1, 2, 3, 5, 8]
var leftSub = array.reduce(sub) // 1-2-3-5-8 = -17
var rightSub = array.reduceRight(sub) // 8-5-3-2-1 = -3
console.log(leftSub, rightSub) // 输出 -17 -3

小结

  1. 除了reduce, reduceRight之外,各高阶遍历函数(forEach, map, filter, every, some)的处理函数的参数顺序都是item, index, array。reduce, reduceRight因为聚合的原因,前两个参数是数组的项。

    这好理解,我们最需要的首先是数组的项,其次是它的索引,最后是数组自身引用

  2. 2个聚合函数reduce, reduceRight;2个检测函数every, some;3个全遍历函数forEach, map, filter。