• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >JavaScript > 无循环 JavaScript(map、reduce、filter和find)

无循环 JavaScript(map、reduce、filter和find)

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-11

通过本文主要向大家介绍了javascript reduce,map filter reduce,map reduce,map reduce原理,map reduce模型等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

之前有讨论过,缩进(非常粗鲁地)增加了代码复杂性。我们的目标是写出复杂度低的 JavaScript 代码。通过选择一种合适的抽象来解决这个问题,可是你怎么能知道选择哪一种抽象呢?很遗憾的是到目前为止,没有找到一个具体的例子能回答这个问题。这篇文章中我们讨论不用任何循环如何处理 JavaScript 数组,最终得出的效果是可以降低代码复杂性。

循环是一种很重要的控制结构,它很难被重用,也很难插入到其他操作之中。另外,它意味着随着每次迭代,代码也在不断的变化之中。——Luis Atencio

我们先前说过,像循环这样的控制结构引入了复杂性。但是也没有给出确切的证据证明这一点,我们先看看 JavaScript 中循环的工作原理。

循环

在 JavaScript 中,至少有四、五种实现循环的方法,最基础的是 while 循环。我们首先先创建一个示例函数和数组:

// oodlify :: String -> String
function oodlify(s) {
 return s.replace(/[aeiou]/g, 'oodle');
}

const input = [
 'John',
 'Paul',
 'George',
 'Ringo',
];
</div>

现在有了一个数组,我们想要用 oodlify 函数处理每一个元素。如果用 while 循环,就类似于这样:

let i = 0;
const len = input.length;
let output = [];
while (i < len) {
 let item = input[i];
 let newItem = oodlify(item);
 output.push(newItem);
 i = i + 1;
}
</div>

注意这里发生的事情,我们用了一个初始值为 0 的计数器 i,每次循环都会自增。而且每次循环中都和 len 进行比较以保证循环特定次数以后终止循环。这种利用计数器进行循环控制的模式太常用了,所以 JavaScript 提供了一种更加简洁的写法: for 循环,写起来如下:

const len = input.length;
let output = [];
for (let i = 0; i < len; i = i + 1) {
 let item = input[i];
 let newItem = oodlify(item);
 output.push(newItem);
}
</div>

这一结构非常有用,while循环非常容易把自增的 i 给忘掉,进而引起无限循环;而for循环把和计数器相关的代码都放到了上面,这样你就不会忘掉自增 i,这确实是一个很好的改进。现在回到原来的问题,我们目标是在数组的每个元素上运行 oodlify() 函数,并且将结果放到一个新的数组中。

对一个数组中每个元素都进行操作的这种模式也是非常普遍的。因此在 ES2015 中,引入了一种新的循环结构可以把计数器也简化掉: for...of 循环。每一次返回数组的下一个元素给你,代码如下:

let output = [];
for (let item of input) {
 let newItem = oodlify(item);
 output.push(newItem);
}
</div>

这样就清晰很多了,注意这里计数器和比较都不用了,你甚至都不用把元素从数组里面取出来。for...of 帮我们做了里面的脏活累活。如果现在用 for...of 来代替所有的 for 循环,其实就可以很大程度上降低复杂性。但是,我们还可以做进一步的优化。

mapping

for...of 循环比 for 循环更清晰,但是依然需要一些配置性的代码。如不得不初始化一个 output 数组并且每次循环都要调用 push() 函数。但有办法可以让代码更加简洁有力,我们先扩展一下问题。

如果有两个数组需要调用 oodlify 函数会怎么样?

const fellowship = [
 'frodo',
 'sam',
 'gandalf',
 'aragorn',
 'boromir',
 'legolas',
 'gimli',
];

const band = [
 'John',
 'Paul',
 'George',
 'Ringo',
];
</div>

很容易想到的方法是对每个数组都做循环:

let bandoodle = [];
for (let item of band) {
 let newItem = oodlify(item);
 bandoodle.push(newItem);
}

let floodleship = [];
for (let item of fellowship) {
 let newItem = oodlify(item);
 floodleship.push(newItem);
}
</div>

这确实ok,有能正确执行的代码,就比没有好。但是重复的代码太多了——不够“DRY”。我们来重构它以降低重复性,创建一个函数:

function oodlifyArray(input) {
 let output = [];
 for (let item of input) {
  let newItem = oodlify(item);
  output.push(newItem);
 }
 return output;
}

let bandoodle = oodlifyArray(band);
let floodleship = oodlifyArray(fellowship);
</div>

这看起来好多了,可是如果我们想使用另外一个函数该怎么办?

function izzlify(s) {
 return s.replace(/[aeiou]+/g, 'izzle');
}
</div>

上面的 oodlifyArray() 一点用都没有了。但如果再创建一个 izzlifyArray() 函数的话,代码又重复了。不管那么多,先写出来看看什么效果:

function oodlifyArray(input) {
 let output = [];
 for (let item of input) {
  let newItem = oodlify(item);
  output.push(newItem);
 }
 return output;
}

function izzlifyArray(input) {
 let output = [];
 for (let item of input) {
  let newItem = izzlify(item);
  output.push(newItem);
 }
 return output;
}
</div>

这两个函数惊人的相似。那么是不是可以把它们抽象成一个通用的模式呢?我们想要的是:给定一个函数和一个数组,通过这个函数,把数组中的每一个元素做操作后放到新的数组中。我们把这个模式叫做 map 。一个数组的 map 函数如下:

function map(f, a) {
 let output = [];
 for (let item of a) {
  output.push(f(item));
 }
 return output;
}
</div>

这里还是用了循环结构,如果想要完全摆脱循环的话,可以做一个递归的版本出来:

function map(f, a) {
 if (a.length === 0) { return []; }
 return [f(a[0])].concat(map(f, a.slice(1)));
}
</div>

递归解决方法非常优雅,仅仅用了两行代码,几乎没有缩进。但是通常并不提倡于在这里使用递归,因为在较老的浏览器中的递归性能非常差。实际上,map 完全不需要你自己去手动实现(除非你自己想写)。map 模式很常用,因此 JavaScript 提供了一个内置 map 方法。使用这个 map 方法,上面的代码变成了这样:

let bandoodle  = band.map(oodlify);
let floodleship = fellowship.map(oodlify);
let bandizzle  = band.map(izzlify);
let fellowshizzle = fellowship.map(izzlify);
</div>

可以注意到,缩进消失,循环消失。当然循环可能转移到了其他地方,但是我们已经不需要去关心它们了。现在的代码简洁有力,完美。

为什么这个代码这么简单呢?这可能是个很傻的问题,不过也请思考一下。是因为短吗?不是,简洁并不代表不复杂。它的简单是因为我们把问题分离了。有两个处理字符串的函数: oodlify 和 izzlify,这些函数并不需要知道关于数组或者循环的任何事情。同时,有另外一个函数:map ,它来处理数组,它不需要知道数组中元素是什么类型的,甚至你想对数组做什么也不用关心。它只需要执行我们所传递的函数就可以了。把对数组的处理中和对字符串的处理分离开来,而不是把它们都混在一起。这就是为什么说上面的代码很简单。

reducing

现在,map 已经得心应手了,但是这并没有覆盖到每一种可能需要用到的循环。只有当你想创建一个和输入数组同样长度的数组时才有用。但是如果你想要向数组中增加几个元素呢?或者想找一个列表中的最短字符串是哪个?其实有时我们对数组进行处理,最终只想得到一个值而已。

来看一个例子,现在一个数组里面存放了一堆超级英雄:

const heroes = [
 {name: 'Hulk', strength: 90000},
 {name: 'Spider-Man', strength: 25000},
 {name: 'Hawk Eye', strength: 136},
 {name: 'Thor', strength: 100000},
 {name: 'Black Widow', strength: 136},
 {name: 'Vision', strength: 5000},
 {name: 'Scarlet Witch', strength: 60},
 {name: 'Mystique', strength: 120},
 {name: 'Namora', strength: 75000},
];
</div>

现在想找最强壮的超级英雄。使用 for...of 循环,像这样:

let strongest = {strength: 0};
for (hero of heroes) {
 if (hero.strength > strongest.strength) {
  strongest = hero;
 }
}
</div>

虽然这个代码可以正确运行,可是实在太烂了。看这个循环,每次都保存到目前为止最强的英雄。继续提需求,接下来我们想要所有超级英雄的总强度:

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 无循环 JavaScript(map、reduce、filter和find)

相关文章

  • 2017-05-11js实现省份下拉菜单效果
  • 2017-05-11jquery实现轮播图效果
  • 2017-05-11JavaScript实现垂直滚动条效果
  • 2017-05-11从零开始学习Node.js系列教程之SQLite3和MongoDB用法分析
  • 2017-05-11JS实现点击表头表格自动排序(含数字、字符串、日期)
  • 2017-05-11jQuery中用on绑定事件时需注意的事项
  • 2017-05-11JS中如何实现Laravel的route函数详解
  • 2017-05-11详解微信小程序开发之——wx.showToast(OBJECT)的使用
  • 2017-05-11js图片延迟加载(Lazyload)三种实现方式
  • 2017-05-11JS查找字符串中出现最多的字符及个数统计

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 如何获取元素的最终background-color
    • 微信小程序实战之顶部导航栏(选项卡)(1)
    • vue2.0嵌套路由实现豆瓣电影分页功能(附demo)
    • Bootstrap.css与layDate日期选择样式起冲突的解决办法
    • bootstrap table实例详解
    • jquery PrintArea 实现票据的套打功能(代码)
    • js实现年月日表单三级联动
    • 微信小程序 聊天室简单实现
    • jQuery内容筛选选择器实例代码
    • Javascript for in的缺陷总结

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有