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 相关文章推荐
音乐播放用的的几个函数
Sep 07 Javascript
简单的邮箱登陆的提示效果类似于yahoo邮箱
Feb 26 Javascript
js支持键盘控制的左右切换立体式图片轮播效果代码分享
Aug 26 Javascript
bootstrap下拉列表与输入框组结合的样式调整
Oct 08 Javascript
基于vue的fullpage.js单页滚动插件
Mar 20 Javascript
探讨Vue.js的组件和模板
Oct 27 Javascript
promise和co搭配生成器函数方式解决js代码异步流程的比较
May 25 Javascript
详解Vue CLI3配置解析之css.extract
Sep 14 Javascript
vue发送ajax请求详解
Oct 09 Javascript
js实现随机div颜色位置 类似满天星效果
Oct 24 Javascript
JS数组方法reduce的用法实例分析
Mar 03 Javascript
微信小程序使用GoEasy实现websocket实时通讯
May 19 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
关于shopex同步ucenter的redirect问题,导致script不运行
2013/04/10 PHP
浅谈PHP Cookie处理函数
2016/06/10 PHP
Yii2框架类自动加载机制实例分析
2018/05/02 PHP
jquery 屏蔽一个区域内的所有元素,禁止输入
2009/10/22 Javascript
javascript showModalDialog 多层模态窗口实现页面提交及刷新的代码
2009/11/28 Javascript
情人节专属 纯js脚本1k大小的3D玫瑰效果
2012/02/11 Javascript
node.js下when.js 的异步编程实践
2014/12/03 Javascript
由浅入深讲解Javascript继承机制与simple-inheritance源码分析
2015/12/13 Javascript
BootStrap智能表单实战系列(七)验证的支持
2016/06/13 Javascript
js仿百度切换皮肤功能(html+css)
2016/07/10 Javascript
Bootstrap Table从服务器加载数据进行显示的实现方法
2016/09/29 Javascript
jquery的父、子、兄弟节点查找,节点的子节点循环方法
2016/12/07 Javascript
利用JS实现文字的聚合动画效果
2017/01/22 Javascript
jQuery EasyUI ProgressBar进度条组件
2017/02/28 Javascript
Javascript中toFixed计算错误(依赖银行家舍入法的缺陷)解决方法
2017/08/22 Javascript
vue 点击展开显示更多(点击收起部分隐藏)
2019/04/09 Javascript
JS利用prototype给类添加方法操作详解
2019/06/21 Javascript
解决一个微信号同时支持多个环境网页授权问题
2019/08/07 Javascript
javascript实现简易的计算器
2020/01/17 Javascript
Javascript执行上下文顺序的深入讲解
2020/11/04 Javascript
python备份文件的脚本
2008/08/11 Python
python处理cookie详解
2014/02/07 Python
python网络编程学习笔记(一)
2014/06/09 Python
windows系统下Python环境搭建教程
2017/03/28 Python
django mysql数据库及图片上传接口详解
2019/07/18 Python
对Python _取log的几种方式小结
2019/07/25 Python
Django框架视图层URL映射与反向解析实例分析
2019/07/29 Python
PYTHON EVAL的用法及注意事项解析
2019/09/06 Python
基于Django signals 信号作用及用法详解
2020/03/28 Python
波兰家居饰品和厨房配件网上商店:Maleomi
2020/12/15 全球购物
历史学专业毕业生求职信
2013/09/27 职场文书
历史系自荐信范文
2013/12/24 职场文书
《蜗牛》教学反思
2014/02/18 职场文书
大学生精神文明先进个人事迹材料
2014/05/02 职场文书
2016十一国庆节感言
2015/12/09 职场文书
golang中实现给gif、png、jpeg图片添加文字水印
2021/04/26 Golang