网站首页 > 技术文章 正文
在2009年十月ECMAScript 5被定义正式规范以来,一些可以提高工作效率的数组方法被提出。但是,由于ES5糟糕的浏览器支持率,这些方法并未被开发者广泛使用。
“多余的”数组方法
没有人会质疑这些方法的可用性,但是如果针对它们而编写polyfill(关于polyfill,个人理解就是为了使新方法兼容各浏览器而编写的浏览器“插件”或者扩展,可以参考这篇文章)却并不值得。所以,这些新方法被认为是“good-to-have”而不是“must-have”的,或者干脆被称为“多余的”方法。Ouch!
但是前端技术发展到今天,如果你时常关注Github上一些流行的开源JS项目,你会发现这样一种趋势:开发者们越来越倾向于使用原生代码,而不依赖与第三方库。
所以,推进原生语法的广泛使用度已经非常必要了。
5个值得关注的数组方法
下面,我将介绍ES 5中的非常有用的5个数组方法,这5个方法可以提高开发者的工作效率。
1. indexOF
indexOf方法返回某个元素在数组中的索引值,如果数组中不存在此元素则返回-1
举个栗子:检查“orange”在数组中的位置
(1) 不使用indexOf()
var arr = ['apple','orange','pear'],
 found = false;
for(var i= 0, l = arr.length; i< l; i++){
 if(arr[i] === 'orange'){
 found = true;
 }
}
console.log("found:",found);
(2) 使用indexOf()
var arr = ['apple','orange','pear'];
console.log("found:", arr.indexOf("orange") != -1);
代码简洁了很多吧!
现在我有了新的需求:我想知道数组中所有符合给定条件的元素。怎么办?
2. filter
filter()方法创建一个指定数组中符合给定条件的所有元素组成的新数组
举个栗子:找出数组中name为“orange”的所有元素
(1) 不用filter()
var arr = [
 {"name":"apple", "count": 2},
 {"name":"orange", "count": 5},
 {"name":"pear", "count": 3},
 {"name":"orange", "count": 16},
];
 
var newArr = [];
for(var i= 0, l = arr.length; i< l; i++){
 if(arr[i].name === "orange" ){
 newArr.push(arr[i]);
 }
}
console.log("Filter results:",newArr);
(2) 使用filter()
var arr = [
 {"name":"apple", "count": 2},
 {"name":"orange", "count": 5},
 {"name":"pear", "count": 3},
 {"name":"orange", "count": 16},
];
 
var newArr = arr.filter(function(item){
 return item.name === "orange";
});
console.log("Filter results:",newArr);
代码简洁了很多。
3. foreach()
foreach()方法为数组中的每个元素执行一次给定的方法
举个栗子:for循环和foreach()方法对比
function test(){
 var arr = [1,2,3,4,5,6,7,8];
 // Uses the usual "for" loop to iterate
 for(var i= 0, l = arr.length; i< l; i++){
 console.log(arr[i]);
 }
 console.log("========================");
 //Uses forEach to iterate
 arr.forEach(function(item,index){
 console.log(item);
 });
}
foreach()方法是for循环的一种升级写法。本人建议如果可以选择,应该尽量使用foreach()方法。
使用for循环时存在一个容易被忽视的问题:在for循环中声明的变量(比如上例中的var i=0)并不是for循环中的局部变量,而是for循环所在作用域内的局部变量。上例中,在for循环中声明的变量 var i = 0 实际上是方法test()作用域内的局部变量,i非常容易被test()作用域内的其他逻辑访问和重写,从而造成一些问题。
实际上,通过jsperf的测试表明,for循环的性能比foreach()要好很多。
但是,我个人始终认为,除非是处理百万级以上的大数据,否则仍然坚持使用foreach()方法。节省毫秒级的时间并不作为提升产品性能的主要考虑因素。
4. map()
对数组的每个元素调用定义的回调函数并返回包含结果的数组
举个栗子:解析一个数组,为数组中每个元素新增一个fullname属性,并返回新数组
(1) 不使用map()
var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];
function getNewArr(){
 
 var newArr = [];
 
 for(var i= 0, l = oldArr.length; i< l; i++){
 var item = oldArr[i];
 item.full_name = [item.first_name,item.last_name].join(" ");
 newArr[i] = item;
 }
 
 return newArr;
}
console.log(getNewArr());
(2) 使用map()
var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];
function getNewArr(){
 
 return oldArr.map(function(item,index){
 item.full_name = [item.first_name,item.last_name].join(" ");
 return item;
 });
 
}
console.log(getNewArr());
map()方法在处理包含服务器数据交互操作的重型JavaScript应用中非常适用。
5. reduce()
对数组中的所有元素调用指定的回调函数。 该回调函数的返回值为累积结果,并且此返回值在下一次调用该回调函数时作为参数提供
老实说,在使用reduce()之前我斟酌了很久。reduce()的概念对我来说非常抽象,尤其是“累积”这个词。直到在nodeschool中开始学习一系列的JavaScript方法之后才逐渐掌握reduce()的概念。但是我仍没有发现它有多大作用,直到有一天我在重构自己代码的时候才发现,reduce()简直吊炸天!
举个栗子:解析一个数组,并且返回一个包含每个元素在数组中出现次数的对象
(1) 不用reduce()
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
 var obj = {};
 
 for(var i= 0, l = arr.length; i< l; i++){
 var item = arr[i];
 obj[item] = (obj[item] +1 ) || 1;
 }
 
 return obj;
}
console.log(getWordCnt());
(2)使用reduce()
var arr = ["apple","orange","apple","orange","pear","orange"];
function getWordCnt(){
 return arr.reduce(function(prev,next){
 prev[next] = (prev[next] + 1) || 1;
 return prev;
 },{});
}
console.log(getWordCnt());
下面我将解释一下对于reduce()的个人理解。
reduce(callback,initialValue) 有两个参数,callback函数和initialValue。其中callback函数包含4个参数:prev,next,index和array。我们一般只需要用到prev和next两个参数。
prev参数代表数组中的第一个元素,next代表数组中的第二个元素。注意:如果initialValue参数被设置,prev则代表initialValue,而next代表数组中的第一个元素。比如上例中。initialValue被设置为空白对象{},则prev为{}。
/*
* Difference between not passing any parameters
* and passing in a additional parameter into `reduce()`
*/
var arr = ["apple","orange"];
function noPassValue(){
 return arr.reduce(function(prev,next){
 console.log("prev:",prev);
 console.log("next:",next);
 
 return prev + " " +next;
 });
}
function passValue(){
 return arr.reduce(function(prev,next){
 console.log("prev:",prev);
 console.log("next:",next);
 
 prev[next] = 1;
 return prev;
 },{});
}
console.log("No Additional parameter:",noPassValue());
console.log("----------------");
console.log("With {} as an additional parameter:",passValue());
上面的代码中,每次迭代都返回一个值,这个值被当做下一次迭代的prev参数传入。
下面的代码会更加清晰的显示reduce()的功能:
var arr = ["apple","orange","apple","pear"];
function getWordCnt(){
 return arr.reduce(function(prev,next,index){
 console.log("<b>Iteration "+index+"</b>");
 console.log("prev:",prev);
 console.log("next:",next);
 
 prev[next] = ++prev[next] || 1;
 console.log("Passing this to the 'prev' of the next iteration if any:",prev);
 console.log("---------------");
 return prev;
 },{});
}
console.log("<b>Final Object:</b>",getWordCnt());
Demethodizing
以上提到的这些方法虽然是只针对数组的,但是同时可以被节点列表、Jquery对象甚至字符串使用。我们可以通过一种“demethodizing”的技术来扩展这些数组方法。
// Demethodizing the Array method, forEach(), into a generic "each"
var each = Function.prototype.call.bind([].forEach);
var nodeList = document.querySelectorAll("p");
each(nodeList,bold);
function bold(node){
 node.style.fontWeight ="bold";
}
我们通过 Function.prototype.call.bind 将foreach方法扩展为function类的each方法,现在each方法可以被数组以外的对象使用了。
浏览器支持度
根据ECMAScript 5 compatibility table,以上5个方法可以再所有的手机浏览器和几乎所有桌面浏览器中使用(当我说“所有”的时候,IE9以下的浏览器请自觉离开)。
猜你喜欢
- 2024-10-23 「jQuery-5」 JavaScript对象和json
 - 2024-10-23 超级好用的轻量级JSON处理命令jq(jsonpath js)
 - 2024-10-23 $.ajax()常用方法详解(推荐)(ajax详细讲解)
 - 2024-10-23 Jquery相关(jqueryui)
 - 2024-10-23 jQuery知识点总结(jquery的认识)
 - 2024-10-23 前端工程师面试题汇总(前端工程师面试题汇总怎么写)
 - 2024-10-23 jQuery基础教程学习笔记(二)样式操作
 - 2024-10-23 前端基础进阶(十三)详细图解jQuery扩展jQuery插件
 - 2024-10-23 jQuery对象和DOM对象互相转换(jquery对象怎么转换dom对象)
 - 2024-10-23 Help!AJAX到底怎么传多文件数组?知道的赶紧进来
 
- 最近发表
 - 
- 聊一下 gRPC 的 C++ 异步编程_grpc 异步流模式
 - [原创首发]安全日志管理中心实战(3)——开源NIDS之suricata部署
 - 超详细手把手搭建在ubuntu系统的FFmpeg环境
 - Nginx运维之路(Docker多段构建新版本并增加第三方模
 - 92.1K小星星,一款开源免费的远程桌面,让你告别付费远程控制!
 - Go 人脸识别教程_piwigo人脸识别
 - 安卓手机安装Termux——搭建移动服务器
 - ubuntu 安装开发环境(c/c++ 15)_ubuntu安装c++编译器
 - Rust开发环境搭建指南:从安装到镜像配置的零坑实践
 - Windows系统安装VirtualBox构造本地Linux开发环境
 
 
- 标签列表
 - 
- cmd/c (90)
 - c++中::是什么意思 (84)
 - 标签用于 (71)
 - 主键只能有一个吗 (77)
 - c#console.writeline不显示 (95)
 - pythoncase语句 (88)
 - es6includes (74)
 - sqlset (76)
 - apt-getinstall-y (100)
 - node_modules怎么生成 (87)
 - chromepost (71)
 - flexdirection (73)
 - c++int转char (80)
 - mysqlany_value (79)
 - static函数和普通函数 (84)
 - el-date-picker开始日期早于结束日期 (76)
 - js判断是否是json字符串 (75)
 - c语言min函数头文件 (77)
 - asynccallback (87)
 - localstorage.removeitem (77)
 - vector线程安全吗 (73)
 - java (73)
 - js数组插入 (83)
 - mac安装java (72)
 - 无效的列索引 (74)
 
 
