javascript中的那些遍历的方法

579 阅读11分钟

一、前言

javascript里面有关遍历的方法有多种,这里我就总结一下,使能够直观的了解每种方法是怎么使用的。不管是前端还是后端都会涉及到对数据的处理。一般来说,数据经常会以数组或者对象的形式存放在数据库中,这就意味着我们经常要对数组或者对象进行操作,而对数组或者对象进行操作,遍历必然少不了。下面在介绍遍历的方法之前,我会先介绍一下数据类型以及内存模型等一些相关的知识。

二、关于数据类型以及内存模型

1.分类

ECMAScript的数据类型分为:

基本数据类型(NumberStringBooleanUndefinedNullSymbol(ES6)

引用数据类型(ObjectArrayFunctionRegExpDateError

两种数据类型的区别:存储位置不同

2.栈(stack)和堆(heap)

栈(stack):一种先进后出的数据结构,由操作系统(编译器)自动分配,释放的内存空间。

堆(heap):一种树形数据结构,一般由程序员分配(通过new关键字),释放的动态内存空间, 若程序员不释放,程序结束后由系统虚拟机的垃圾回收算法来决定是否回收。

3.内存模型

内存模型与语言有关系

栈区:存放es5中基本数据类型、引用数据类型的引用

堆区:存放数组,对象等引用数据类型本身

变量区:五种数据类型数据本身

代码区:函数体的二进制代码

注意:

  • 栈区对应着堆区,变量区,代码区这三个区,他们数据的引用均存放在栈区中。

  • 栈区存放的是空间小,大小固定的,频繁被使用的数据,读取速度快。

  • 堆区存放的是空间大,大小不固定的数据,如果这些数据存放在栈区将影响程序的性能。

  • 引用数据类型的指针存在栈区,该指针指向堆区该实体的起始地址,当解释器寻找引用值时,会首先检索其

    在栈中的地址,取得地址后从堆中获得实体。

4.基本数据类型的声明与赋值是怎样一个过程

//es6的变量声明
let num = 111;

执行这段代码会经过这些过程

1.创建唯一标识符 num

2.在内存变量区分配一个空间,并将该空间的地址0034HHH(假设)存在栈区

3.在变量区分配的空间里存一个值111

我们认为标识符num等于地址0034HHH,这个地址的空间里保存着一个值111

现在执行下面代码

let num = 111;
	num = num+1;
console.log(num);//112

在这个过程最后打印的num的地址是否还是0034HHH,而保存的值是112?并不是的

执行let num=num+1的时候,系统重新分配了一个空间,并且num等于这个新空间的地址(0064HBC),新的空间保存的值为112

注意:在es6里面,与let不同的是,const声明的常量,其地址是不会变的,因此不允许重新赋值且必须赋初始值

三、关于数组

Array Javascript 中是一个对象,与其他语言的Array是有区别的。在JavaScriptArray 的索引是属性名。Javascript中的 Array 在内存上并不连续,其次, Array 的索引并不是指偏移量。实际上, Array 的索引也不是 Number 类型,而是 String 类型的。我们可以正确使用如 arr[0] 的写法的原因是语言可以自动将Number类型的 0 转换成 String 类型的 “0″ 。所以,在Javascript中从来就没有 Array 的索引,而只有类似 “0″ 、 “1″ 等等的属性。每个 Array 对象都有一个 length 的属性,这一点与其他语言的Array很像,但是 length 属于不可枚举属性,实际上, Array 对象还有许多其他不可枚举的属性。

四、关于对象

关于JavaScript中的对象,我之前的文章js对象与对象原型中有详细的介绍

五、关于判断数据类型的方法

1.typeof

用于检查数据类型,其返回值为字符串类型,返回值有numberstringundefinedfunctionobject

需要特别注意的是:typeof(NAN)-->undefinedtypeof([],{},null)-->object

2.instanceof

用于判断一个实例对象是不是由这个构造函数实例化出来的,如果是,则返回true,否则返回false,如:p instanceof Person

3.原型中的构造器

如果p._proto_.constructor==Person.prototype.constructor,说明实例对象p是由构造函数Person实例化出来的

4.toString.call(value)

鉴于typeof用于判断数据类型的局限性,toString.call()可以准确的判断数据类型

六、什么情况下才能改变数组或者对象的值

在实际的项目中,会有这样的需求,从接口获取到的数组或者对象格式的数据需要通过遍历,并且在遍历的过程做一些操作去改变原来的值,但是有的情况下就仅仅是简单的遍历而已。那么这里涉及到什么样的操作才会改变原数组或者对象的值呢?

1.改变原数组

下面,我们先来看看两个对比的例子

//操作一
var arr=[1,2,3];
for(var i=0,len=arr.length;i<len;i++){
    arr[i]*2;
}
console.log(arr);//(3) [1, 2, 3]

以上操作,原来数组的值并没有变化

//操作二
var arr=[1,2,3];
for(var i=0,len=arr.length;i<len;i++){
    arr[i]=arr[i]*2;//通过引用重新赋值
}
console.log(arr);//(3) [2, 4, 6]

以上操作发现,原来数组的值改变了,这是为什么?上面我有介绍过基本数据类型的声明与赋值过程,可以发现值的改变很大程度其实是地址的改变,上面通过引用重新赋值,所以原数组的值改变了。

2.改变对象的值

json={
    name:'xiaoming',
    sex:'男',
    age:22
}
json.age=json.age+1;
console.log(json);//{name: "xiaoming", sex: "男", age: 23}

以上操作发现,对象 json 的 age 的值被改变了,不管是数组还是对象,都是通过索引或者键值对中的键来找到相应的值的,而索引或者键就是引用也就是地址,所以不管是数组还是对象,值的改变操作一定要涉及到地址的改变,原来的值才会被改变。

七、有关遍历的方法

1.for()

作用:for循环是最基础的遍历方法,一般用来遍历数组,单纯的遍历,不会改变原数组的值

var arr = [1, 2, 3];
for(var i = 0, len = arr.length; i < len; i++) {
    console.log(typeof i)//number
    console.log(arr[i]);//1 2 3
}
console.log(arr);//(3)[1,2,3]

注意:

  • i 是索引,number 类型

  • 为了避免遍历过程重复获取数组长度,应使用临时变量,将长度缓存起来

  • 没有返回值

  • 如果在遍历的过程重新赋值,那必然是会改变数组原来的值的

    var arr = [1, 2, 3];
    for(var i = 0, len = arr.length; i < len; i++) {
        arr[i]=arr[i]*2;
    }
    console.log(arr);//(3)[2,4,6]
    

2.for in

作用:for in 可以遍历数组,对象的属性,也能遍历原型链上的属性(最后遍历),istring类型

遍历数组

在JavaScript中,数组中的索引,类似 “0″ 、 “1″ 等等的属性

var arr=['h','e','l','l','o'];
for(var i in arr){
    console.log(typeof i)//string
	console.log(arr[i]);//h e l l o
}
console.log(arr);//(5)['h','e','l','l','o']

注意:

  • i 为索引(属性),string 类型

  • 如果在遍历过程重新对数组进行赋值,也是会改变数组原来的值的

    const arr = [1, 2, 3];
    for(var i = 0, len = arr.length; i < len; i++) {
        arr[i]=arr[i]*2;
    }
    console.log(arr);//(3)[2,4,6]
    
  • 如果原来的数组扩展了私有属性,那么会在最后把数组的私有属性遍历出来,基于这一点,一般不会使用 for in 来遍历数组

    var arr=['h','e','l','l','o'];
    arr.a='world';//扩展私有属性
    for(var i in arr){
    	console.log(arr[i]);
    }
    console.log(arr);//(5) ["h", "e", "l", "l", "o", a: "world"]
    
遍历对象
json={
    name:'xiaoming',
    sex:'男',
    age:22
}
for(var i in json){
    console.log(json[i]);//xiaoming 男 22
}
console.log(json);//{name: "xiaoming", sex: "男", age: 22}

注意:

  • i 为属性,string类型

  • for in 会将对象的属性遍历出来

  • 如果在遍历的过程重新赋值,会改变原来的值

    json={
        name:'xiaoming',
        sex:'男',
        age:22
    }
    for(var i in json){
        json[i]=json[i]+'1111';
    }
    console.log(json);//{name: "xiaoming111", sex: "男111", age: 22111}
    
  • 如果在对象原型上添加属性,会在最后遍历出来

    function Person(name,age) {
        this.name=name;
        this.age=age;
    }
    //通过原型添加属性
    Person.prototype.sex='男'
    
    var p=new Person("小明",20);
    for(var i in p){
        console.log(p[i]);//小明 20 男
    }
    console.log(p);//Person {name: "小明", age: 20}
    

3.for of

作用:for of 一般与 for in 形成对比,功能与 for in 类似,但是不能用来遍历对象

var arr=[1,2,3];
//for in 遍历数组
for(let i in a){
	console.log(i);//0 1 2
	console.log(a[i]);//1 2 3
}
//for of 遍历数组
var arr=[1,2,3];
for(let i of arr){
	console.log(i);//1 2 3
}

注意:

  • for in 的 i 是索引(属性),for of 的 i 是数组中元素

4.forEach()

作用:forEach ( ) 方法用于调用数组的每个元素,并将元素传递给回调函数,回调函数一个有三个参数。

语法:array.forEach(function(item, index, arr), thisValue)

参数:

参数描述
item必需,当前元素
index可选,当前元素的索引值
arr可选,当前元素所属的数组对象
thisValue可选,传递给函数的值一般用 "this" 值,如果这个参数为空, "undefined" 会传递给 "this" 值
var arr=['a','b','c','d'];
arr.forEach(function(item,index,arr){
    console.log(item,index);
    //a,0
    //b,1
    //c,2
    //d,3
});
console.log(arr);//(4) ["a", "b", "c", "d"]

注意:

  • forEach( ) 没有返回值,对 return 不起作用

  • 回调函数内部不能有break,continue的,否则会报错的

  • 遍历过程不一定会改变原数组的值,具体要看是否涉及到地址的操作

    var arr=[1,2,3];
    arr.forEach(item => {
        item = item * 2;//未涉及地址操作
    })
    console.log(arr);//(3) [1,2,3]
    
    var arr=[1,2,3];
    arr.forEach((item,index) => {
        arr[index] = item * 2;//涉及地址操作
    })
    console.log(arr);//(3) [2, 4, 6]
    
    var arr = [
        {name:'xiaoming',age:16},
        {name:'zhangsan',age:17}
    ];
    arr.forEach(item => { 
        item.age = item.age + 1;//涉及地址操作
    });
    console.log(arr);//(2) [{name:'xiaoming,age:17'}, {name:'zhangsan',age:'18'}]
    

5.map()

作用:map ( ) 方法按照原始数组元素顺序依次处理元素,返回一个新数组,新数组中的元素为原始数组元素调用函数处理后的值

语法:array.map(function(item,index,arr ), thisValue)

参数:

参数描述
item必须,当前元素的值
index可选,当前元素的索引值
arr可选,当前元素属于的数组对象
thisvalue可选,对象作为该执行回调时使用,传递给函数,用作 "this" 的值。如果省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。
var arr = [1,2,3];
var temp=arr.map((item,index)=>{
    console.log(item,index);
    //1 0
    //2 1
    //3 2
})
console.log(temp);//(3) [undefined, undefined, undefined]
console.log(arr);//(3)[1,2,3]
var arr = [1,2,3];
var temp=arr.map(item=>{
	return item*2;
})
console.log(temp);//(3) [2, 4, 6]
console.log(arr);//(3) [1, 2, 3]

注意:

  • map( ) 不会对空数组进行检测

  • map( ) 不会改变原始数组

  • 每个元素都会执行相应的回调函数,必须要有返回值,否则结果为 undefined

    var arr = [10,2,5,22,65,44,32];
    var temp=arr.map((item,index)=>{
        console.log(item);//10,2,5,22,65,44,32
        if(item>30){
        	return item;
        }
    })
    console.log(temp);//(7) [undefined, undefined, undefined, undefined, 65, 44, 32]
    console.log(arr);//(7) [10, 2, 5, 22, 65, 44, 32]
    

6.filter ( )

作用:filter() 是个过滤器,该方法创建一个新的数组,新数组中的元素是通过检查原数组中符合条件的所有元素。

语法:array.filter(function(item,index,arr), thisValue)

参数:

参数描述
item必需,当前元素
index可选,当前元素的索引值
arr可选,当前元素所属的数组对象
thisValue可选,传递给函数的值一般用 "this" 值,如果这个参数为空, "undefined" 会传递给 "this" 值
var arr = [1,2,3];
var temp=arr.filter((item,index)=>{
    console.log(item,index);
    //1 0
    //2 1
    //3 2
})
console.log(temp);//(3) []
console.log(arr);//(3)[1,2,3]
var arr = [1,2,30,11,3];
var temp=arr.filter((item)=>{
	console.log(item);//1,2,30,11,3
	return item>10
})
console.log(temp);//(2) [30, 11]
console.log(arr);//(5) [1, 2, 30, 11, 3]
//数组去重
var arr=['apple','pear','apple','banana'];
var r=arr.filter(function(item,index,self){
	return self.indexOf(item)===index;
})
//这里提供另一种数组去重的方法
var arr1 = ["a","b","a","c","b","d","k"];
var temp = {};
var arr2=[];
arr2 = arr1.filter(function(item){
  if (!temp[item]){
      temp[item]=true;
      return item;
  }
})
console.log(arr2); //  ["a", "b", "c", "d", "k"]

注意:

  • filter() 不会对空数组进行检测
  • filter() 不会改变原始数组
  • 每个元素都会执行相应的回调函数,需要 return
  • indexOf ( ) 可以返回某个指定的子字符串在字符串中首次出现的位置,检索的字符串如果没有出现,则返回-1

7.find ( )

作用:find() 方法返回满足回调条件的指定数组的第一个元素的值,数组中的元素依次执行回调函数,当数组中的元素满足条件时, find() 返回符合条件的元素,之后的元素不再执行函数,如果都没有符合条件的元素返回 undefined

语法:array.find(function(item, index, arr),thisValue)

参数:

参数描述
item必需,当前元素
index可选,当前元素的索引值
arr可选,当前元素所属的数组对象
thisValue可选,传递给函数的值一般用 "this" 值,如果这个参数为空, "undefined" 会传递给 "this" 值
var arr = [1,2,3];
var temp=arr.find((item,index)=>{
    console.log(item,index);
    //1 0
    //2 1
    //3 2
})
console.log(temp);//undefined
console.log(arr);//(3)[1,2,3]
var arr = [1,2,30,11,3];
var temp=arr.find((item)=>{
	console.log(item);//1,2,30
	return item>10
})
console.log(temp);//30
console.log(arr);//(5) [1, 2, 30, 11, 3]
var arr = [1,2,30,11,3];
var temp=arr.find((item)=>{
	console.log(item);//1,2,30,11,3
	return item>100
})
console.log(temp);//undefined
console.log(arr);//(5) [1, 2, 30, 11, 3]
// 在arr2中找出存在arr1中的元素
var arr1 = ["a","b","c","d","k"];
var arr2 = ["b","d","o","p","n"];
arr2 = arr2.filter(function(id) {
    return arr1.find(function(item) {
        return item == id;
    });
});
console.log(arr2); // ["b","d"]

注意:

  • find() 不会对空数组进行检测
  • find() 不会改变原始数组
  • 返回值为数组中的元素,不是索引

8.findIndex()

作用:findIndex() 方法返回满足回调函数条件的指定数组的第一个元素的索引,没有符合条件则返回-1。

语法:array.findIndex(function(item, index, arr), thisValue)

参数:

参数描述
item必需,当前元素
index可选,当前元素的索引值
arr可选,当前元素所属的数组对象
thisValue可选,传递给函数的值一般用 "this" 值,如果这个参数为空, "undefined" 会传递给 "this" 值
var arr = [1,2,3];
var temp=arr.findIndex((item,index)=>{
    console.log(item,index);
    //1 0
    //2 1
    //3 2
})
console.log(temp);//-1
console.log(arr);//(3)[1,2,3]
var arr = [1,2,30,11,3];
var temp=arr.findIndex((item)=>{
	console.log(item);//1,2,30
	return item>10
})
console.log(temp);//2
console.log(arr);//(5) [1, 2, 30, 11, 3]

注意:

  • findIndex() 不会对空数组进行检测

  • findIndex() 不会改变原始数组

  • 并不是数组中所有的元素都执行回调函数,当数组中的元素一旦满足条件时, findIndex() 返回符合条件的元素的索引,之后的元素不会再执行回调函数

9.some ( )

作用:some() 方法用于检测数组中的元素是否满足指定条件,会依次执行数组的每个元素,如果有一个元素满足条件,则表达式返回 true , 剩余的元素不会再执行检测,如果没有满足条件的元素,则返回false

语法:array.some(function(item,index,arr),thisValue)

参数:

参数描述
item必需,当前元素
index可选,当前元素的索引值
arr可选,当前元素所属的数组对象
thisValue可选,传递给函数的值一般用 "this" 值,如果这个参数为空, "undefined" 会传递给 "this" 值
var arr = [1,2,3];
var temp=arr.some((item,index)=>{
    console.log(item,index);
    //1 0
    //2 1
    //3 2
})
console.log(temp);//false
console.log(arr);//(3)[1,2,3]
var arr = [1,2,30,11,3];
var temp=arr.some((item)=>{
	console.log(item);//1,2,30
	return item>10
})
console.log(temp);//true
console.log(arr);//(5) [1, 2, 30, 11, 3]

注意:

  • some ( ) 不会对空数组进行检测

  • some ( ) 不会改变原始数组

  • 并不是数组中所有的元素都执行回调函数,当数组中的元素一旦满足条件时,结果返回 true,之后的元素不再执行了

10.every ( )

作用:every() 方法用于检测数组所有元素是否都符合指定条件,会依次执行数组中的每个元素,如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测,只有所有元素都满足条件,则返回 true

语法:array.every(function(item,index,arr), thisValue)

参数:

参数描述
item必需,当前元素
index可选,当前元素的索引值
arr可选,当前元素所属的数组对象
thisValue可选,传递给函数的值一般用 "this" 值,如果这个参数为空, "undefined" 会传递给 "this" 值
var arr = [1,2,3];
var temp=arr.every((item,index)=>{
    console.log(item,index);//1 0
})
console.log(temp);//false
console.log(arr);//(3)[1,2,3]
var arr = [1,2,30,11,3];
var temp=arr.every((item)=>{
	console.log(item);//1,2,30,11,3
	return item>0
})
console.log(temp);//true
console.log(arr);//(5) [1, 2, 30, 11, 3]

注意:

  • every ( ) 不会对空数组进行检测

  • every ( ) 不会改变原始数组

  • every ( ) 与 some ( ) 形成比较