浅谈前端常见的数组和对象的遍历问题

前端遍历有多种情况,除最常用的for循环外,还有for in、for of 等等,有针对数组的map遍历、forEach遍历,有针对对象的Object.keys、object.values等等。

1、for循环,for in,for of的区别在哪里?

先说说数组,举个栗子。

  1. 普通的for循环,就是遍历我们的数组。
  2. for in循环,会把原型上的属性和属性值都遍历下来,遍历的是键名。
  3. for of循环,只会遍历数组中的每一项,不是遍历的键名。
var arr = [1,2,3];
arr.a = 4;
Array.prototype.name = 'mapbar_front';
for(var i = 0; i < arr.length; i++){
    console.log(arr[i]);
}
//普通的for循环打印出来是 123
for(var i in arr){
    console.log(arr[i]);
}
//for in 循环打印的是 1234,mapbar_front
for(var i of arr){
    console.log(i);//这里是i,而不是arr[i]
}
//打印出来的是 123

再说说对象的遍历

  1. 对象无法直接被for循环遍历。
  2. 对象的遍历一般使用的是for in 循环,不过for in 循环会把原型上的属性和方法给遍历出来。
  3. for of只能遍历iterable类型的数据,不能遍历对象。
  4. 如果只想遍历对象自身的属性和方法,可以使用Object.hasOwnProperty()方法进行判断。
  5. Object.keys方法,也是一种获取对象的属性的方式,这种方式不会把原型上面的属性和方法的键名获取。
var obj = {
    name: 'mapbar_front',
    age: 28,
}
Object.prototype.work = 'do work!';
for(var i in obj) {
    console.log(obj[i]);
}
var keylist = Object.keys(obj);
console.log(keylist);// ['name', 'age']

//hasOwnProperty的使用
console.log(obj.hasOwnProperty('name'));//true
console.log(obj.hasOwnProperty('work'));//false

2、数组遍历的方法。forEach、map、filter、find、findIndex、includes的使用

任何的数组操作,都离不开遍历,但是因为需求不一样,所有会有不同的方法。

如果你想对数组中的每一项,执行某一个函数,可能你需要用到forEach方法:

var arr = [1,2,3];
arr.forEach((item, index) => {
    console.log(item, index);
    //也可以有其他的操作。
})

如果你想对数组,做某一种处理,返回一个处理后的数组,map方法可能就是你的想要的:

var arr = [1, 2, 3];
//给每一项乘以2,然后返回一个结果
var newarr = arr.map((item, index) => {
    return item * 2;
})

如果你想对数组,筛选出符合条件的项,最终得到一个新的筛选后的数组,可能你要用到filter方法:

var arr = [1, 2, 3, 4];
var newarr = arr.filter((item, idx) => {
    return item % 2 === 0;
})

如果你想要知道,我们的数组是不是有那么一项符合某一个条件,那么可能你要用到some方法,这个方法的返回值是一个布尔值。

var arr = [1, 2, 3, 4];
var isTrue = arr.some((item, idx) => {
    return item % 2 === 0;
})

some方法和filter方法,看起来都是是否符合某个条件,但是不一样的是,filter方法会把符合条件的每一项push到一个新的数组,并且返回。而some方法只要符合条件,我就返回一个true就行了。

如果你想确保你的数组是否每一项都符合某一个条件,可能你要用到any方法:

var arr = [1, 2, 3, 4];
var isTrue = arr.any((item, idx) => {
    return item % 2 === 0;
})

如果你想知道一个数组中是不是包含某一项,你可以使用includes方法:

var arr = [1,2,3];
arr.includes(1);//true

如果你想知道一个数组是不是可以找到符合条件的某一项,可以使用find方法:

var arr = [1, 2, 3];
var my = arr.find((item, index) => {
    return item === 2;
})

3、对象相关的遍历API总结。

  • Object.entries,返回一个键值对的数组,这个键值对也是以数组的方式来进行排布。(需要注意的是,Object.entries不像for in 那样,连原型上面的属性和方法也行进遍历)
var obj = {
    name: 'mapbar_front',
    age: 28,
    occupation: 'web fronted'
}
Object.entries(obj);//[["name","mapbar_front"], ["age", 28], ["occupation": "web fronted"]]

一般而言,通过Object.entries 对象可以转换为Map数据结构。

var obj = { name: 'mapbar_front', age: 27 };
var map = new Map(Object.entries(obj));
  • Object.keys 以及 Object.values 方法的使用。
    对于对象而言,获取它的键名的列表很重要,可以使用Object.keys获取一个对象的所有键名,返回值是一个键名的列表。他们同样不会获取原型上面的属性或者方法。

如果你仅仅是想要一个对象的value的集合,一般使用Object.values来解决。

var obj = {
    name: 'mapbar_front',
    age: 28,
    occupation: 'web fronted'
}
console.log(Object.keys(obj));//["name", "age", "occupation"];
console.log(Object.values(obj));//["mapbar_front", 28, "web fronted"];
  • Object.getOwnPropertyNames,获取对象的属性名,并且包括不可枚举的属性的属性名。返回值也是一个数组。
Object.getOwnPropertyNames(obj);
//["name", "age", "occupation", "sex"];

但是Object.getOwnPropertyNames方法,只能获取键名类型为string类型的属性,一般而言,属性的类型也可以是Symbol类型,这个时候就需要另外的一种获取Symbol类型的键名的方法。

  • Object.getOwnPropertySymbols
    该方法用于获取一个对象的属性键名类型为Symbol类型的键名。返回的是一个Symbol类型的数组。并且也能获取到不可枚举的属性的键名。
var a = Symbol();
var obj = {
    name: 'mapbar_front',
    age: 28,
    occupation: 'web fronted',
    [a]: 1234,
}
Object.defineProperty(obj, 'sex', {
    value: 1,
    configurable: true,
    enumerable: true,
    writable: true
})
var symbol = Object.getOwnPropertySymbols(obj);
console.log(symbol);// [Symbol()]这样的一个数组
var names = Object.getOwnPropertyNames(obj);
console.log(names);

在遍历的时候,对于对象的属性,我们可能要判断,这个属性是自己的实例属性,还是原型上的属性,Object的API中,有这样的一些方法:

  • Object.prototype.hasOwnProperty,用于判断一个对象的属性是不是属于自身。并且也能判断出,一个不可枚举的属性,是不是属于自身对象。
  • Object.prototype.isPrototypeOf,用于判断一个对象,是不是在另一个对象的原型链上面。
function Foo(){}
function Bar(){}
Bar.prototype = Object.create(Foo.prototype);
var bar = new Bar();
Bar.prototype.isPrototypeOf(bar);//true。    说明Bar.prototype在bar这个对象的原型链上。
Foo.prototype.isPrototypeOf(bar);//true。    说明Foo.prototype在bar这个对象的原型链上。
Object.prototype.isPrototypeOf(bar);//true。 说明Object.prototype在bar这个对象的原型链上。

有时候,我们可能还会在想,为什么一个对象的属性没有被for in遍历出来,很有可能这个对象的属性是不可枚举的,使用下面的方法进行处理:

  • Object.prototype.propertyIsEnumberable,用于判断一个对象的属性是不是可枚举的,也就是能不能被for in 循环遍历到。
  • Object.getOwnPropertyDescriptor(O,property),用于获取对象O的描述对象——Descriptor

关于原型,我们可能要对一个对象设置一个新的原型,或者想要判断一个对象是不是在另一个对象的原型链上面。

  • Object.setPrototypeOf(obj, proto),用于设置一个对象的原型。
  • Object.prototype.isPrototypeOf,用于判断一个对象,是不是在另一个对象的原型链上面。
  • Object.create(proto),用于创建一个对象,并且这个对象的原型指向proto。

关于对象的操作,有时候你可能在想,我能不能对一个对象做一些限制,比如我不想给一个对象增加新的属性,或者我这个对象不能删除一个属性,又或者我这个对象我都不能改变它的属性值这类的操作,可能你需要知道这三个关于对象的方法:

  • Object.preventExtensions(O) / Object.isExtensible,保证了这个对象无法被扩展,isExtensible函数是返回一个true或者false,用来表示这个对象能不能被扩展。
  • Object.seal(O) / Object.isSealed,表示一个对象不仅不能被扩展,也不能随意删除一个对象的属性。
  • Object.freeze(O) / Object.isFrozen,表示一个对象不仅不能被扩展,也不能随意删除一个属性,而且连属性值都不能被改变。
var obj = {
    name: 'mapbar_front',
    age: 23
}
Object.preventExtensions(obj);//让这个对象不可以扩展
obj.name = 'mapbar';
obj.age1 = 1234;//这里这一句不起作用
console.log(obj);//{ name: 'mapbar', age: 23 }

var obj1 = {
    name: 'mapbar_front',
    age: 123
}
Object.seal(obj1);
delete obj1.name;
console.log(obj1);//这个obj1没有被删除
console.log(Object.isSealed(obj1));//true

var obj2 = {
    name: 'mapbar_front',
    age: 123
}
Object.freeze(obj2);
obj2.name = '1111';
console.log(obj2);//这个obj2没有变
console.log(Object.isFrozen(obj2));//true

你可能感兴趣的:(JavaScript)