JavaScript中的遍历详解(多种遍历)


Posted in Javascript onApril 07, 2017

对象遍历

为了便于对象遍历的测试,我在下面定义了一个测试对象 obj。

测试对象

// 为 Object 设置三个自定义属性(可枚举)
Object.prototype.userProp = 'userProp';
Object.prototype.getUserProp = function() {
 return Object.prototype.userProp;
};
// 定义一个对象,隐式地继承自 Object.prototype
var obj = {
 name: 'percy',
 age: 21,
 [Symbol('symbol 属性')]: 'symbolProp',
 unEnumerable: '我是一个不可枚举属性',
 skills: ['html', 'css', 'js'],
 getSkills: function() {
  return this.skills;
 }
};
// 设置 unEnumerable 属性为不可枚举属性
Object.defineProperty(obj, 'unEnumerable', {
 enumerable: false
});

ES6 之后,共有以下 5 种方法可以遍历对象的属性。

for…in: 遍历对象自身的和继承的可枚举属性(不含 Symbol 类型的属性)

for (let key in obj) {
 console.log(key);
 console.log(obj.key); // wrong style
 console.log(obj[key]); // right style
}

不要使用 for…in 来遍历数组,虽然可以遍历,但是如果为 Object.prototype 设置了可枚举属性后,也会把这些属性遍历到,因为数组也是一种对象。

Object.keys(obj):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 类型的属性)

Object.keys(obj); 
// ["name", "age", "skills", "getSkills"]

Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(不含 Symbol 类型的属性,不包含继承属性,但是包括不可枚举属性)

Object.getOwnPropertyNames(obj);
// ["name", "age", "unEnumerable", "skills", "getSkills"]

Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有 Symbol 类型的属性(不包括继承的属性)

Object.getOwnPropertySymbols(obj);
// [Symbol(symbol 属性)]

Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有属性(包含 Symbol 类型的属性,还有不可枚举的属性,但是不包括继承的属性)

Reflect.ownKeys(obj);
// ["name", "age", "unEnumerable", "skills", "getSkills", Symbol(symbol 属性)]

以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则

  • 首先遍历所有属性名为数值的属性,按照数字排序
  • 其次遍历所有属性名为字符串的属性,按照生成时间排序
  • 最后遍历所有属性名为Symbol值的属性,按照生成时间排序

如何判断某个属性是不是某个对象自身的属性呢?
in 操作符(不严谨,它其实判定的是这个属性在不在该对象的原型链上)

'age' in obj;  // true
'userProp' in obj; // true (userProp 是 obj 原型链上的属性)
'name' in Object; // true 
// 上面这个也是 true 的原因是,Object 是一个构造函数,而函数恰巧也有一个 name 属性
Object.name;   // 'Object'
Array.name;   // 'Array'

hasOwnProperty(),这个方法只会检测某个对象上的属性,而不是原型链上的属性。

obj.hasOwnProperty('age');  // true
obj.hasOwnProperty('skills'); // true
obj.hasOwnProperty('userProp'); // false

但是它还是有不足之处的。举例~

// 利用 Object.create() 新建一个对象,并且这个对象没有任何原型链
var obj2 = Object.create(null, {
 name: { value: 'percy' },
 age: { value: 21 },
 skills: { value: ['html', 'css', 'js'] }
});
obj2.hasOwnProperty('name'); // 报错 
obj2.hasOwnProperty('skills'); // 报错

针对上面的情况,我们用一个更完善的解决方案来解决。
使用 Object.prototype.hasOwnProperty.call(obj,'prop'…)

Object.prototype.hasOwnProperty.call(obj2,'name');  // true
Object.prototype.hasOwnProperty.call(obj2,'skills'); // true
Object.prototype.hasOwnProperty.call(obj2,'userProp'); // false

数组遍历

数组实际上也是一种对象,所以也可以使用上面对象遍历的任意一个方法(但要注意尺度),另外,数组还拥有其他遍历的方法。
最基本的 for 循环、while 循环遍历(缺陷是多添加了一个计数变量)
ES6 引入:for…of ,这下就没有这个计数变量了,但是也不够简洁(这里不做详细介绍,以后写)

for(let value of arr){
 console.log(value);
}

下面说几种数组内置的一些遍历方法
Array.prototype.forEach(): 对数组的每个元素执行一次提供的函数

Array.prototype.forEach(callback(currentValue, index, array){
 // do something
}[,thisArg]);
// 如果数组在迭代时被修改了,则按照索引继续遍历修改后的数组
var words = ["one", "two", "three", "four"];
words.forEach(function(word) {
 console.log(word);
 if (word === "two") {
 words.shift();
 }
});
// one
// two
// four

Array.prototype.map(): 返回一个新数组,每个元素都是回调函数返回的值

Array.prototype.map(callback(currentValue, index, array){
  // do something 
}[,thisArg]);
``` 
```js
// map 的一个坑
[1,2,3].map(parseInt); // [1, NaN, NaN]
// 提示 map(currentValue,index,array)
//  parseInt(value,base)
  • 一些有用的数组内置方法(类似 map,回调函数的参数都是那 3 个)

    • Array.prototype.every(callback[,thisArg]): 测试数组的各个元素是否通过了回调函数的测试,若都通过,返回 true,否则返回 false(说地本质点儿,就是如果回调函数每次返回的值都是 true 的话,则 every() 返回 true,否则为 false)
    • Array.prototype.filter(callback[,thisArg]): 返回一个新数组,数组的元素是原数组中通过测试的元素(就是回调函数返回 true 的话,对应的元素会进入新数组)
    • Array.prototype.find(callback[,thisArg]): 返回第一个通过测试的元素
    • Array.prototype.findIndex(callback[,thisArg]): 与上面函数类似,只不过这个是返回索引
    • Array.prototype.some(callback[,thisArg]): 类似 find() ,只不过它不返回元素,只返回一个布尔值。只要找到一个通过测试的,就返回 true
  • Array.prototype.reduce(callback,[initialValue]): 习惯性称之为累加器函数,对数组的每个元素执行回调函数,最后返回一个值(这个值是最后一次调用回调函数时返回的值)

    • 这个函数的回调函数有 4 个参数
      • accumulator: 上一次调用回调函数返回的值
      • currentValue: 当前在处理的值
      • currentIndex
      • array
    • initialValue: 可选项,其值用于第一次调用 callback 的第一个参数
  • Array.prototype.reduceRight(callback[, initialValue]): 用法和上面的函数一样,只不过遍历方向正好相反

// 一些相关的案例
// 对数组进行累加、累乘等运算
[1,10,5,3,8].reduce(function(accumulator,currentValue){
 return accumulator*currentValue;
}); // 1200
// 数组扁平化
[[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
 return a.concat(b);
}); // [0, 1, 2, 3, 4, 5]
[[0, 1], [2, 3], [4, 5]].reduceRight(function(a, b) {
 return a.concat(b);
}); // [4, 5, 2, 3, 0, 1]

总结一下上面这些函数的共性

  • 都是通过每次的回调函数的返回值进行逻辑操作或判断的
  • 回调函数都可以写成更简洁的箭头函数(推荐)
  • 都可以通过形如 Array.prototype.map.call(str,callback) 的方式来操作字符串
var str = '123,hello';
// 反转字符串
Array.prototype.reduceRight.call(str,function(a,b){
 return a+b;
}); // olleh,321
// 过滤字符串,只保留小写字母
Array.prototype.filter.call('123,hello', function(a) {
 return /[a-z]/.test(a);
}).join(''); // hello
// 利用 map 遍历字符串(这个例子明显举得不太好 *_*)
Array.prototype.map.call(str,function(a){
 return a.toUpperCase();
}); // ["1", "2", "3", ",", "H", "E", "L", "L", "O"]

最下面的文章想说的就是让我们用更简洁的语法(比如内置函数)遍历数组,从而消除循环结构。

参考资料:无循环 JavaScript

Javascript 相关文章推荐
引用外部脚本时script标签关闭的写法
Jan 20 Javascript
JavaScript中获取Radio被选中的值
Nov 11 Javascript
解决bootstrap导航栏navbar在IE8上存在缺陷的方法
Jul 01 Javascript
jquery popupDialog 使用 加载jsp页面的方法
Oct 25 Javascript
浅析如何利用angular结合translate为项目实现国际化
Dec 08 Javascript
两种简单的跨域方法(jsonp、php)
Jan 02 Javascript
bootstrap中模态框、模态框的属性实例详解
Feb 17 Javascript
BootStrap模态框闪退问题实例代码详解
Dec 10 Javascript
使用vue开发移动端管理后台的注意事项
Mar 07 Javascript
Vue 实现输入框新增搜索历史记录功能
Oct 15 Javascript
Element的el-tree控件后台数据结构的生成以及方法的抽取
Mar 05 Javascript
浅谈vue 组件中的setInterval方法和window的不同
Jul 30 Javascript
分享十三个最佳JavaScript数据网格库
Apr 07 #Javascript
Google 爬虫如何抓取 JavaScript 的内容
Apr 07 #Javascript
正则表达式基本语法及表单验证操作详解【基于JS】
Apr 07 #Javascript
js实现图片加载淡入淡出效果
Apr 07 #Javascript
AngularJS中的拦截器实例详解
Apr 07 #Javascript
Vue.js如何优雅的进行form validation
Apr 07 #Javascript
详解Node.js实现301、302重定向服务
Apr 07 #Javascript
You might like
php Ajax乱码
2008/04/09 PHP
PHP数组对比函数,存在交集则返回真,否则返回假
2011/02/03 PHP
PHP list() 将数组中的值赋给变量的简单实例
2016/06/13 PHP
基于PHP实现用户注册登录功能
2016/10/14 PHP
PHP基于回溯算法解决n皇后问题的方法示例
2017/11/07 PHP
PHP快速排序算法实现的原理及代码详解
2019/04/03 PHP
javascript 动态设置已知select的option的value值的代码
2009/12/16 Javascript
Jquery调用webService远程访问出错的解决方法
2010/05/21 Javascript
神奇的7个jQuery 3D插件整理
2011/01/06 Javascript
JS中的prototype与面向对象的实例讲解
2013/05/22 Javascript
jquery获得option的值和对option进行操作
2013/12/13 Javascript
Javascript获取表单名称(name)的方法
2015/04/02 Javascript
Javascript aop(面向切面编程)之around(环绕)分析
2015/05/01 Javascript
JavaScript判断图片是否已经加载完毕的方法汇总
2016/02/05 Javascript
深入浅析Extjs中store分组功能的使用方法
2016/04/20 Javascript
解析JavaScript中的字符串类型与字符编码支持
2016/06/24 Javascript
Javascript 判断两个IP是否在同一网段实例代码
2016/11/28 Javascript
详解Sea.js中Module.exports和exports的区别
2017/02/12 Javascript
浅谈jQuery中的$.extend方法来扩展JSON对象
2017/02/12 Javascript
使用vue-router完成简单导航功能【推荐】
2018/06/28 Javascript
layui清除radio的选中状态实例
2019/11/14 Javascript
python使用PyGame播放Midi和Mp3文件的方法
2015/04/24 Python
详解Django框架中用context来解析模板的方法
2015/07/20 Python
简单谈谈Python中的反转字符串问题
2016/10/24 Python
Python实例方法、类方法、静态方法的区别与作用详解
2019/03/25 Python
pandas 强制类型转换 df.astype实例
2020/04/09 Python
Python接口测试文件上传实例解析
2020/05/22 Python
python matplotlib工具栏源码探析三之添加、删除自定义工具项的案例详解
2021/02/25 Python
css图标制作教程制作云图标
2014/01/19 HTML / CSS
详解canvas绘制多张图的排列顺序问题
2019/01/21 HTML / CSS
艺术设计专业求职自荐信
2014/05/19 职场文书
毕业生班级鉴定评语
2015/01/04 职场文书
保险公司客户经理岗位职责
2015/04/09 职场文书
2016年小学生教师节广播稿
2015/12/18 职场文书
python3.9之你应该知道的新特性详解
2021/04/29 Python
MySQL提取JSON字段数据实现查询
2022/04/22 MySQL