欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

宁皓 Underscore 基础

处理数据的类库,比如数组,对象等等。

来自 https://ninghao.net/course/1479#info


1)介绍

在 Underscore 里面,提供了非常多,而且非常实用的一些函数去处理数据。比如你可以用一个函数去处理一个数组或对象里面的每一个项目,你可以查找,过滤数据,可以对数据进行排序还有分组,可以找出两组数据之间的不同的地方,或者相交的地方,你可以去掉数据列表里面的重复的东西。

Underscore 还提供了一些跟函数相关的方法,比如我们可以把一些方法跟对象绑定到一起,这样在方法里面,可以使用对象里面的属性,我们还可以延时去执行函数,可以防止滥用函数,可以创建一次性的函数。

最后,我们再看一下 Underscore 里的链式调用。

来自  https://ninghao.net/video/1480#info

2)准备

下面我们先介绍一下这个课程里用到的东西,你可以在课程的资源包里,找到一个启动项目 ... 用 Bracks 编辑器打开这个项目目录,就是你现在看到的这个样子 ..

index.html 是项目的主页 ... 在这个文档上,用到了 bootstrap 框架的样式表 ... 还有 jquery ... underscore.js ... 这是 underscore 库的文件 ...

下面的 data.js ... 在 js 这个目录的下面 ... 在这个文件里面,事先定义好了一些要在这个课程里面用到的数据 ... 一些数组还有对象 ...

最后的 app.js ,是一个空白的脚本文件 ... 在课程里面,我们可能需要在它里面去输入几行代码 ...

这个课程的大部分的测试,都是在 Chrome 浏览器的控制台上完成的 ...

在浏览器里,打开这个 index.html ...

打开控制台,可以点击 视图 菜单 .. 开发者 ... 选择 JavaScript 控制台 ... 或者也可以直接使用快捷键 alt + command + J 打开控制台。

windows 上应该是 alt + ctrl + J

点击 右上角 的这个按钮 ... 可以在新窗口打开控制台 ...

再点击这个按钮 ... 会回到原来的样式 ...

在控制台,输入在 data.js 里面定义好的数据,可以直接输出里面的内容 ... 比如输入 fruits ... 会返回在 fruits 里面的东西 ...

来自  https://ninghao.net/video/1481#info


集合

3)集合 - 循环处理列表中的每一个项目 - each

如果你想循环去处理一个列表,比如一个数组,或者一个对象。可以使用 Underscore 的 each 方法 ... 这个方法有两个参数,第一个参数是要处理的列表,第二个参数是一个迭代器。each 方法会把列表里面的每一个项目,都交给这个迭代器去处理一下 ...

如果列表是一个数组的话,这个迭代器接收的第一个参数就是 element ,表示当前这次处理的元素 ... 第二个参数是 index,表示这个元素在数组里面的索引号 .. 最后一个参数是 list ,表示这个列表本身。

如果要处理的列表是个对象的话,这个遍历器的第一个参数是对象的值,可以用 value 表示 ... 第二个参数是 key ,表示对象里的属性的名字 .. 最后一个参数是 list ,表示要处理的这个列表对象。

下面, 我们去试一下这个方法 ...

先处理一下 tracks 这个数组 ... 要做的事就是,在控制台上输出这个数组里面的每个项目的索引号,还有对应的值 ... underscore 点 each ,使用 underscore 的 each 这个方法 ... 要处理的列表是 tracks ... 再设置一下处理这个列表的函数,也就是遍历器 ...

可以使用一个函数的名字 ... 这里我们直接使用一个匿名函数 ... 给它两个参数,用 track 来表示当前要处理的元素 ... 然后再用 index 表示元素的索引号 ...

在这个函数里面,用一个 console.log ,在控制台上输出 tracks 里面的项目的索引号,还有对应的值 ...

_.each(tracks, function(track, index){ console.log(index + ' ' + track);})

回车 ... 你会看到这里每一行会输出 tracks 数组里面的一个项目的索引号还有值 ... 注意在最后,这个方法会返回处理的这个列表 ...

这个方法还有一个别名,叫 forEach ...

_.forEach(tracks, function(track, index){ console.log(index + ' ' + track);})

这两个方法的效果是一样的 ...

对象

下面,我们再用 each 这个方法去遍历一个对象 ... 整理一下屏幕 ... command + K 清空一下 ... 或者 command + R 刷新一下页面 ...

要处理的对象是 album ... (#输入 album)

下划线 ... 点 each ... 要遍历的列表是 album 这个对象 ... 再定义一下遍历器 ... 用一个匿名函数 .. . 处理对象的时候,第一个参数是对象的属性的值,这里我们用 value 表示 ... 逗号分隔一下 ... 第二个参数是对象的属性的名字 .. 可以用 key 来表示 ...

在这个函数里面,我们把 album 这个对象里的属性名字还有对应的值输出到控制台上 ...

_.each(album, function(value, key){ console.log(key + ':' + value); })

回车 ... 在控制台上,每一行显示 album 这个对象里面的一个属性 ... 包括属性的名字,还有对应的值 ...

在最后,会返回这个被遍历的对象 ...

来自  https://ninghao.net/video/1482#toc


4)遍历列表项目生成新的处理之后的列表 - map

遍历列表里面的每一个项目映射到一个迭代器里面去处理,然后返回一个新的处理之后的列表。这就是 map 这个方法的功能。它的用法跟上个视频里介绍的 each 方法差不多 ...

下面,我们用 map 再去处理一下 tracks 这个数组 ... 比如我们要给这个数组里面的每一个项目的值都添加一个书名号 ...

_.map ... 指定一下要遍历的列表 ... 这里就是 tracks 这个数组 ... 然后再设置一下,迭代器,或者叫循环处理器 ... 其实就是一个处理列表里面的项目的函数 ... 这里我用一个匿名函数 ...

然后用 track 表示当前要处理的列表里面的项目 ... 你可以根据自己的需求去命名这个参数。

在这个函数里面,返回处理之后的结果 .. . 用一个 return ... 一个书名号 .. 加上 track ... 表示当前这个项目 ... 后面再加上书名号的另一部分 ...

_.map(tracks, function(track) { return '《 ' + track + ' 》';})

回车 ...

会给我们返回处理之后生成的新的列表 ... 在这个数组里面,每一个项目的值的周围都会有一组书名号 ...

这个方法也有个别名,叫 collect ...

_.collect(tracks, function(track) { return '《 ' + track + ' 》';})

得到的是一样的结果 ...

来自  https://ninghao.net/video/1483#info

5)把列表里的所有值转换成一个值 - reduce

Underscore 里面的 reduce 方法,可以把一个列表里面的所有的值,用你想要的方法换算成一个值。比如一个数字列表,你可以得到它们彼此相加的结果。

下面,我们使用这个方法去处理一下 numbers 这个数组 ... 让这个数组里的值彼此相加,返回这个相加之后的结果 ...

这个方法的第一个参数是要处理的列表,第二个参数是遍历器,或者叫迭代器,循环处理器 .. 第三个参数是存储结果的一个初始的状态 ... 最后有一个可选的 context 参数。

_.reduce ... 先指定一下要处理的列表 ..  这里就是 numbers 这个数组 .. 第二个参数是迭代器 ... 用一个匿名函数 ... 第三个参数是存储结果的初始状态 ... 这里我们设置成 0 ...

在这个迭代器函数里面支持几个参数 ... 第一个参数表示当前结果,一般用 memo 来表示,第二个参数表示本次循环的列表里面的值 .. 第三个参数是 index ,如果处理的列表是个对象的话,这个参数就应该是 key ,最后一个参数表示列表本身 ...

这里我们只需要两个参数 .. memo 还有 number ... number 列表 numbers 里面的一个数字。

然后在这个函数里面,返回处理的结果 ... return ... 用 memo 再加上 number ...

_.reduce(numbers, function(memo, number){ return memo + number; }, 0)

回车 ... 得到的结果是 10 ... 迭代器首先会处理 numbers 里面的第一个项目,也就是数字 3 ,存储的结果的初始状态是 0 ,要处理的动作就是,让 memo 加上 number ,第一次就应该是 0 加上 3 ... 结果是 3 ...

然后继续第二次循环 .. 这里的 memo 就是上一次处理的结果,也就是 0 + 3 算出来的结果 .. number 表示列表里面的第二个项目 ... 也就是数字 6 ... 然后用 3 加上 6 .. 再返回这个结果 ... 现在 memo 的值,就会是 9 了 ...

继续执行 ... 9 加上 numbers 里面的最后一个数字 1 ,结果就是这里显示的出来的数字 10 ...

对象

下面,我们再用这个方法去处理一个对象的属性 ... 先清理一下屏幕 .. command + K

要处理的对象是 album ... (#输入 album 回车)

要做的就是,把这个对象里的属性的名字和值连接到一块 ... 属性和名字之间用一个冒号 ... 不同的属性之间使用一个分号 ...

_.reduce ... 要处理的东西是 album ... 设置一下处理器 ... 用一个匿名函数 ... memo 的初始值设置成一个空白的字符串 ...

这个处理器要接受 memo 参数,还有 value 和 key 参数,value 表示对象属性的值,key 表示对象属性的名字 ...

在这个处理器里面,返回 memo ,也就是当前存储结果 ... 加上对象属性的名字 ... 再连接一个冒号 ... 然后是属性的值 ... 用 value 表示 ... 最后是一个分号 ..

_.reduce(album, function(memo, value, key){ return memo + key + ':' + value + ';';}, '' )

回车 ...

得到的结果就是用我们设置的形式连接的对象里面的属性 ... 属性名和值之间有一个冒号,不同属性之间有一个分号 ..

来自  https://ninghao.net/video/1484

6)查找,过滤,反过滤 - find, filter, reject

使用 find 这个方法,可以去找到列表里面第一个符合我们设置的规则的结果。下面,去试一下 ..

比如这个 tracks 数组 .. 找出这个数组里面的元素的字符长度大于 2 的第一个项目 ... _.find ... 要查找的列表是 tracks ... 再设置一下它的第二个参数,是一个函数 ... 在这个函数里面,我们可以去设置一下查找的规则 ...

这里我们让它返回元素字符长度大于 2 的第一个项目 ...

_.find(tracks, function(track){ return track.length > 2; })
// "不可一世"

回车 ... 返回的结果是 不可一世 ... 因为在 tracks 这个数组里面,第一个字符长度大于 2 的项目就是这个 不可一世 ... 如果你想得到所有的符合设置的规则的项目 ...

可以使用 filter 这个方法 ...

_.filter(tracks, function(track){ return track.length > 2; })
// ["不可一世", "温暖的家乡", "无语问苍天", "早班火车"]

返回的结果就是在 tracks 这个数组里面,所有的字符长度大于 2 的项目 ... filter 是过滤的意思 ... 这个方法的意思就是,过滤出符合规则的结果 ... 也就是,在这个函数里面,所有通过验证的项目都会包含在这个返回来的结果里面。

跟 filter 相反的是 reject ... 使用它,我们可以得到所有的验证失败的项目 ...

_.reject(tracks, function(track){ return track.length > 2; })
// ["长城", "农民", "遥望"]

得到的结果是,tracks 里面,所有的字符数小于 2 的项目 ...

来自  https://ninghao.net/video/1485#info

7)找到包含特定属性和值的项目 - where, findWhere

要找出包含某些属性还有对应的值的项目,可以使用 where 这个方法。 现在你看到的 albums 是一个数组,数组里面的每一个项目都是一个对象 ... 对象的内容表示某张音乐专辑 ...

比如我们要找到 artist 属性的值是 Beyond 的项目 ... 输入 _.where ... 指定一下要查找的列表 ... 这里就是 albums ... 第二个参数是要查找的属性还有对应的值 ... 它是一个对象 ... 属性是 artist ... 值是 Beyond ...

_.where(albums, {artist: 'Beyond'})

回车 ... 得到的结果就是包含 artist 这个属性并且它的值是 Beyond 的项目 ... 我们也可以查找多个属性还有对应的值 ... 比如要找到 artist 属性的值是 Beyond ,还有 releaseYear 是 1992 的项目 ...

在这个要找的属性里面,再加上一个 releaseYear ... 值是 1992

_.where(albums, {artist: 'Beyond', releaseYear: 1992})

这次只给我们返回一个项目 ... 因为只有它里面的 artist 属性的值是 Beyond,releaseYear 的值是 1992 ...

findWhere

如果只想找到第一个包含特定属性和值的项目 .. 可以使用 findWhere 这个方法 ...

_.findWhere(albums, {artist: 'Beyond'})

返回的就是第一个包含 artist 属性并且值是 Beyond 的项目 .. .

来自  https://ninghao.net/video/1486


8)判断列表里的项目 - every, some, contains

every 这个方法,可以去判断一下一个列表里面,是否全部都符合我们设置的规则,如果是,这个方法就会返回 true ,如果不是,就会返回 false 。

类似的还有一个 some 方法,如果列表里面有一个项目符合我们要判断的规则,就会返回 true ...

contains 这个方法,可以去判断一下列表里是否包含指定的值 ...

下面,我们去看一下这几个方法...

在 albums 里面,有三个对象 ... 我们判断一下这些对象里面是否都有 title 这个属性 ... _.every ... 先指定一下要判断的列表 ... 这里就是 albums ..

再去设置一下要判断的东西 ... 它应该是一个函数 ... 可以给它一个参数 ... 这个参数可以表示列表里面的单个项目 ... 这里我们用 album 来表示 ... 然后在这里面,用一个 return ... 再使用 Underscore 的 has 方法 ... 去判断一下对象里面,是否包含指定的属性 .. 这个对象就是 album ... 要检查的属性是 title ...

如果列表里面的每一个对象里面都有 title 这个属性,它就会返回 true ...

_.every(albums, function(album){ return _.has(album, 'title') })

回车 ... every 这个方法给我们返回来的结果是 true ... 这就表示,在 albums 里面的所有的对象,都包含 title 这个属性 ...

再去试一下返回 false 的情况 ...

要判断的条件是,对象里面的 artist 属性的值等于 Beyond ...

_.every(albums, function(album){ return album.artist === 'Beyond' })

返回的是 false ,因为最后一个对象的 artist 属性的值是 Coldplay ,不是 Beyond ...

some

some 这个方法只要有一个项目符合判断的条件,就会返回 true ... 把 every 换成 some ... Underscore 里面的很多方法都有别名,比如 every 的别名是 all ,some 的别名是 any ...

_.some(albums, function(album){ return album.artist === 'Beyond' })

回车 ... 结果是 true ... 因为 albums 里面有项目的 artist 属性的值是 Beyond ... 如果一个也没有的话,它就会返回 false ..

contains

下面我们再去试一下 contains 这个方法 ... 它可以判断列表里是否包含指定值 ... 用它去判断一下 fruits 这个要数组 ... (# 输入 fruites )

_.contains ... 要判断的列表是 fruits ... 再指定一下要查看的值 ... 这里设置成 apple ... 意思就是,看一下 fruits 这个列表里面,有没有 apple 这个值 ...

_.contains(fruits, 'apple')

返回的结果是 true ... 表示 fruits 里面包含 apple 这个值 ...

来自  https://ninghao.net/video/1487#info


9)挑出列表里面的指定属性的值 - pluck

使用 pluck 这个方法, 我们可以把一个列表对象里的某个特定的属性的值找出来,返回的结果是这个属性的值的数组。比如,albums 这个数组里面有几个对象 ... 每个对象里都有 title 这个属性 ... 现在,我们想把所有这些对象里的 title 属性的值都找出来放在一个数组里 ...

_.pluck ... 指定一下要处理的列表 ... albums ... 第二个参数是要挑选的属性的名字 ... 这里就是 title 这个属性。

_.pluck(albums, 'title')
["继续革命", "海阔天空", "Ghost Story"]

返回的结果是一个数组,在这个数组里面,会包含 albums 这个数组里面的对象的 title 属性的值。

来自 https://ninghao.net/video/1488

10)找出列表里面值最大或最小的项目 - max, min

找出列表里面的最大值,还有最小值,可以使用 max 和 min 方法。

比如找到 numbers 这个数组里面最大的数字 .. 可以这样 .. _.max ... 给它指定一个列表参数 ... 这里就是 numbers ...

_.max(numbers)
// 6

返回的结果是 6 ... 也就是在 numbers 这个数组里面,最大的数字就是 6 ... 找出最小值,用的是 min .. 把 max 换成 min ..

_.min(numbers)
// 1

返回的结果是 1 ,因为 1 是 numbers 里面最小的一个数字 ...

这两个方法还支持一下迭代器或者叫遍历器的参数 ... (#albums)

下面, 我们可以找到在 albums 里面, duration 属性的值最大或者最小的项目 ... 这次要处理的列表是 albums ... 再设置一下 max 方法的第二个参数 .. 用一个匿名函数 ... 用 album 表示列表里面的每一个项目 ... 在这个函数里,返回一个要判断值的属性... return album.duration ...

_.max(albums, function(album){ return album.duration; })

返回的结果是 海阔天空 这张专辑项目 ... 因为在 albums 里面的所有的项目里面,这个项目的 duration 属性的值是最大的 ...

再试一下 min 这个方法...

_.min(albums, function(album){ return album.duration; })

得到的结束是 ghost story 这张专辑项目 ... 因为它的 duration 属性的值是最小的。

来自  https://ninghao.net/video/1489#info

11)排序 - sortBy

对列表进行排序可以使用 sortBy 这个方法。它的第一个参数是要排序的列表,第二个参数是一个迭代器。最后还有一个 context ,上下文的参数。

现在我这里有一个 numbers 列表 ... 它是一个数组,里面有一些数字,现在项目的顺序,排第一个的是数字 3,然后是,6,最后是,1 .. 下面,我们可以使用 _.sortBy 去对这个列表排下顺序 ... 把 numbers 传递给这个方法 ..

_.sortBy(numbers)
// [1, 3, 6]

方法会返回一个排序以后的数组 ... 在这个数组里面,会按数字从小到大排列 ... 排第一个的数字 1 ,然后是 3 ,最后是 6 ..

下面,我们再去排列一个数组 ... (#输入 fruits)

fruits 这个数组里面有一些字符串项目 ... 下面,我们把它交给 sortBy 去处理一下 ...

_.sortBy(fruits)
// ["apple", "banana", "orange", "pear"]

得到的结果是按照字母顺序排列的项目。 在用 sortBy 这个方法的时候,我们也可以给它一个迭代器,作为它的第二个参数。这样在这个迭代器里面,可以设置一下排序的方式。

(#输入 albums)

albums 是一个数组,数组里有一些对象 ... 这些对象都有一个 duration 属性,我们可以让 sortBy 按照 duration 这个属性去为列表里的项目排下顺序。

sortBy ... 要排列顺序的列表是 albums ... 再给它一个迭代器 .. 也就是处理这个列表里的每一个项目的函数 ... 用一个匿名函数 ... 也可以在这里指定一下函数的名字 ... 把列表里的项目交给这个迭代器 .. 这里可以使用 album 表示 ...

然后在这个函数里面,返回排序的方式 ... return .. 按照 duration 这个属性的值来排序 ... album.duration ...

_.sortBy(albums, function(album){ return album.duration; })

回车 ...

这个方法返回来的列表,你会看到 ... 排在第一个的是 Ghost Sotry 这张专辑项目 .. 因为它的 duration 属性的值是所有项目里面最小的 ... 然后是 继续革命 ... 最后是 海阔天空 ...

来自  https://ninghao.net/video/1490#info

12)分组 - groupBy

groupBy 这个方法,可以对一个列表,用某种方式进行分组 ... 比如这里有一个叫 fruits 的数组 ... 里面是一些水果的英文名字 ... 下面,我们可以使用 groupBy ,按照项目的字符数去分一下组 ...

_.groupBy ... 要分组的列表是 fruits ... 然后再指定一下按照什么标准去分组,这里可以使用一个迭代器,也可以直接使用属性的名字 .. 我们用一个 length 属性,它表示项目的字符长度 ...

_.groupBy(fruits, 'length')

返回的是一个对象 ... 这个对象里面有几个属性 ... 属性名是 4 的这个项目里面,包含一个 pear ... 这个数字 4 实际上就是 fruits 这个列表里面的项目的字符串的长度 ... 也就是,在这个列表里面,所有的长度是 4 的项目,都会在这里面 ... 因为在 fruits 里面,只有 pear 是 4 个字符 .. 所以这个小组里面,只有一个 pear ..

下面还有一个 5 .. 里面有 apple 这个项目 ... 最后还有一个 6 ,这里面有两个项目 ... orange ,还有 apple ..

我们再看一下 albums 这个对象 ... (#albums)

下面, 用项目里的 artist 这个属性去给这个列表分一下组 ... groupBy ... 要分组的是 albums ... 给它一个迭代器参数 ... 用 album 表示这个列表里面的单个项目 ... 然后在函数里面,返回要分组用的标准 .. 这里就是 album 的 artist 这个属性 ...

_.groupBy(albums, function(album){ return album.artist; })

回车 ... 返回的结果同样是一个对象 ... 里面有两个属性 ... 也就是把 albums 这个列表分成了两个小组 ... 一个是 Beyond ... 在这里有包含两个项目 ... 应该就是 继续革命,还有海阔天空这两张专辑项目 ...

因为这两个项目的 artist 属性的值都是 Beyond ...

最后还有一个 Coldplay 这个属性 ... 在这里只有一个项目 ... 就是 Ghost Story 这张专辑项目 ...

来自  https://ninghao.net/video/1491#info

13)分组计数 - countBy

我们可以先对一个列表按照自己设置的标准分一下组,然后得到每个小组里的项目的数量 ... 这就是 countBy 方法的功能。它有点像是前面介绍过的 groupBy ... 不同的是 ... groupBy 返回的是分组以后的项目 ... countBy 返回的是分组之后的项目的数量。

(#albums)

我们可以看一下 albums 这个列表 .. 比如我们想要得到在这个列表里面,artist 这个属性的值是 Beyond 的项目一共有多少个 .. 值是 Coldplay 的项目一共有多少 ...

countBy ... 要分组统计的是 albums ... 再给它一个迭代器 ... 在它里面返回要统计的分组属性 ...

_.countBy(albums, function(album){ return album.artist; })

回车 ... 返回的结果是一个对象 ... 这个对象里有两个属性 .. 表示按照 artist 属性的值,把列表分成了两个组 ... 其中值是 Beyond 的项目一共有两个 ... artist 的值是 Colplay 的项目一共有一个。

来自  https://ninghao.net/video/1492#info

14)随机与取样 - shuffle, sample

对一个列表进行随机处理,也就是把列表里面的项目的顺序打乱,得到一个新的列表 ... 这个功能我们可以使用 shuffle 。 在一个列表里面,取出几个样本,可以使用 sample 方法。

(#tracks)

先试一下 shuffle ... 把要随机处理的列表交给这个方法 ...

_.shuffle(tracks)

回车 ... 你会发现,新得到的这个列表里面的项目的顺序,跟以前是不一样的 ... 再用这个方法处理一下 tracks 这个数组 ...

_.shuffle(tracks)

又会得到一个新的顺序的列表 ...

sample

下面,我们再去试一下 smaple 这个方法 ... 同样把 tracks 这个数组交给这个方法去处理一下 ...

_.sample(tracks)

这个方法,会随机从指定的列表里面取出一个样本 ... 再执行一次 ..

_.sample(tracks)

又会随机取出一个样本 ... 我们也可以去指定每一次取出的样本的数量 ...

_.sample(tracks, 2)

这次会从 tracks 里面,随机取出两个样本 ...

来自  https://ninghao.net/video/1493


数组

15)数组 - 从数组里挑选项目 - first, last, initial, rest, without

在这一章里,我们学一下 Undersocre 里面跟数组相关的函数。

现在,这里有一个叫 tracks 的数组 ... 如果想得到这个数组里面的头一个,或者头几个项目,可以使用 frist 这个方法。

_.first(tracks)

返回的就是第一个项目,长城 ... 想得到头两个项目,可以给 first 这个方法传递一个数字参数 ... 得到头两个项目,就把这个参数设置成 2 ...

_.first(tracks, 2)

得到的就是,tracks 这个数组里面的头两个项目 ...

last

想得到最后一个或者最后几个项目,用的是 last 这个方法 ...

_.last(tracks)

返回的就是 tracks 数组里面的最后后一个项目,早班火车 .. 要返回后几个项目,可以给这个方法传递一个数字参数 ... 比如要返回后两个项目,就把这个参数的值设置成 2 ..

_.last(tracks, 2)

initial

initial 这个方法返回的是数组里面除了最后一个项目的其它的项目。

_.initial(tracks)

得到的数组里面,去掉了 tracks 这个数组的最后一个项目 ... 也可以给它传递一个数字参数 .. 比如要去掉数组里面的后两个项目,可以把这个数字参数设置成 2

_.initial(tracks, 2)

返回的结果里面,不会包含 tracks 的最后两个项目,早班火车,还有无语问苍天 ..

rest

如果你想要的是,除了数组里面的第一个项目以外的所有的项目 .. 要以使用 rest ..

_.rest(tracks)

返回的结果里面,去掉了 tracks 数组里的第一个项目,长城 ...

我们还可以给这个方法传递一个数字参数 ... 比如要去掉数组里面的头两个项目 ...

_.rest(tracks, 2)

得到的数组里面,就去掉了 tracks 数组里的头两个项目,长城,还有农民。

without

我们也可以手工去指定要从数组里面去掉的项目 ... 可以使用 without 这个方法 ... 比如要从 tracks 这个数组里面,去掉 遥望 这个值的项目 ...

_.without(tracks, '遥望')

返回的结果里面,就不会包含值是 遥望 的这个数组项目 ... 我们也可以去指定多个值 ... 这里用一个逗号分隔一下 ... 再去添加其它的要去掉的项目 ...

_.without(tracks, '遥望', '长城')

这次给我们返回来的结果里面,去掉了 遥望,还有 长城 这两个项目 ...

来自  https://ninghao.net/video/1494#info

16)把数组分割成两部分 - partition


我们可以按照自己设置的条件,把一个数组分割成两个部分,一部分是满足条件的项目,另一个部分是不符合条件的项目。要用到的方法是 Underscore 的 partition ...

现在这里有一个 albums 数组,里面有几个项目 ... 我们可以把这分割成两个部分,一部分是 releaseYear 是 1992 年的专辑 ... 另一部分就是 releaseYear 不是 1992 年的专辑项目 ...

partition ... 先指定一下要分割的数组 ... 这里就是 albums ... 然后再设置一下条件,可以用一个函数返回这个条件 ... 用 album 表示数组里面的每个项目 ... 然后 return ... 返回的是 album 的 releaseYear 属性的值,等于 1992 ...

_.partition(albums, function(album){ return album.releaseYear === 1992; })

返回的结果就是一个大的数组,里面分成了两小部分,第个部分又是一个数组 ... 第一部分是符合条件的项目 ... 你会看到,这个项目的 releaseYear 属性的值是 1992 ...

另一部分就是不符合条件的项目 ... 这些项目也会放在一个数组里面。

来自  https://ninghao.net/video/1495#info


17)并集,交集,差集 - union, intersection, difference

把几个数组合并到一块儿,去掉重复的项目,得到的就是两个数组的并集。可以使用 union 这个方法。现在,我们这里有两个数组,fruits,还有 foods ... 下面用 union 方法,得到这两个数组的并集 ..

union ... fruits 逗号 ... foods .. 如果有其它的数组,可以继续用逗号分隔一下,再添加进来 ..

_.union(fruits, foods)

得到的结果就是,fruits 和 foods 这两个数组的并集 ... 注意这两个数组里面,都有 apple 这个项目 ... 同并集以后,只会保留一个 apple ...

intersection

如果想要得到的是数组之间的交集 .. 要用的是 intersection 方法 ..

_.intersection(fruits, foods)

返回的结果是 apple ... 因为在这两个数组里面,共有的项目只有 apple ...

difference

要得到一个数组跟其它数组的差集,也就是在这个数组里面有的,在其它数组里没有的项目 ... 可以使用 difference ...

_.difference(fruits, foods)

返回的结果是 orange, banan, 还有 pear 这几个项目 ... 去掉了 fruits 里面的 apple 这个项目 ... 因为在 foods 里面,也包含这个 apple 项目。

来自  https://ninghao.net/video/1496

18)去掉数组里面的重复的项目 - uniq

使用 uniq 这个方法,可以去掉在数组里面重复出现的项目。

我们可以先把这里的 fruits 还有 foods 这两个数组合并到一块儿 ... 先合并之后的结果起个名字 ... 可以叫它 newFoods ...

合并数组可以使用 concat 这个方法 ... 这是 JavaScript 原生自带的一个方法 ...

var newFoods = fruits.concat(foods)

再查看一下这个合并之后的 newFoods ...

这个数组里面,会出现一个重复的项目,也就是这个 apple ... 下面再用一个 Underscore 的 uniq 方法,去掉数组里面的重复的项目。

_.uniq(newFoods)

在返回的结果里面,去掉了重复出现的 apple ,只有保留其中的一个。

来自  https://ninghao.net/video/1497

19)找出指定值的项目在数组中的位置 - indexOf, lastIndexOf


想要得到在一个数组里面,某个值的项目第一次出现的位置,用的是 indexOf 这个方法。找出某个值在数组里面最后一次出现的位置,可以使用 lastIndexOf ...

这里我们有一个叫 newFoods 的数组 ... 下面,我们用 indexOf 去找出,apple 这个值的项目第一次出现在这个数组里面的位置号。

indexOf ... 先给它指定一个数组参数 .... newFoods ... 它的第二个参数是要判断位置的项目的值 .. 这里就是 apple ...

_.indexOf(newFoods, 'apple')

返回的结果是 2 .... 在 newFoods 这个数组里面,第一次出现 apple 这个项目的位置是 2 ... 这个位置号或者叫索引号是从 0 开始的 ...

如果要找的值不在这个数组里面,这个方法会返回 -1 ...

lastIndexOf

想找出最后一次出现 apple 这个项目的位置号 .. 可以使用 lastIndexOf ...

_.lastIndexOf(newFoods, 'apple')

返回的结果是 4 ,表示在 newFoods 这个数组里, apple 这个项目最后一次出现的位置是 4 ...

跟 indexOf 方法一样,如果在数组里面没有找到指定的这个值的项目,lastIndexOf 方法会返回 -1 。

来自  https://ninghao.net/video/1498#info

20)测试即将插入到数组里的项目的位置 - sortedIndex

sortedIndex 这个方法,可以返回即将要插入到数组里面的项目,在这个数组里面的位置是什么,也就是它的索引号是什么。

foods 是一个数组,我们用 sortBy 对它进行了排序,并且把排序之后的结果交给 sortedFoods 这个变量 ...

下面我们可以用 sortedIndex ,去测试一个要插入到这个数组里的项目的位置。

_.sortedIndex ... 第一个参数是要测试的数组 ... 这里就是 sortedFoods ... 第二个参数是要测试的值 ... 我们可以试试 cookie 这个项目如果插入到 sortedFoods 里面它的位置会是什么 ...

_.sortedIndex(sortedFoods, 'cookie')

返回的结果是 2 ,意思就是,如果把 cookie 作为 sortedFoods 里面的一个项目的话,如果保存排序的方式不变,它的位置是 2 ,也就是它会排在 bread 的后面 ...

下面, 我们可以验证一下 ...

先把 cookie push 到 foods 这个数组里面 ...

foods.push('cookie')

然后再对 foods 重新排下顺序 ..

_.sortBy(foods)

你会看到, cookie 的位置,也就是它的索引号,就会是 2 ... 可以使用 indexOf 得到它的位置号 ...

_.indexOf(_.sortBy(foods), 'cookie')

返回的结果是 2 ...

iterator

这个方法还可以再给它一个 迭代器 作为它的参数 ,可以测试指定的排序条件下,项目在列表中的位置。这个迭代器也可以是属性的名字 ... 下面,我们去试一下 ... (#albums)

可以测试一下要往 #albums 里面添加的项目的位置 ... 列表是 albums ... 要测试的值是一个对象 .. 添加一个 artist 属性 .. title 属性,还有 releaseYear 属性 ... 最后再指定一下排序的条件 ... 这里我们设置成 releaeYear ...

它的意思就是,如果按照 releaseYear 这个属性的值进行排序的话,这个即将插入到 albums 里面的项目的位置是多少 ...

_.sortedIndex(albums, {artist: 'Coldplay', title: 'Yellow', releaseYear: 2003}, 'releaseYear')

返回的结果是 ..

2

来自  https://ninghao.net/video/1499

函数

21)函数 - 为函数绑定对象 - bind

在这一章里,我们去学几个 Underscore 里面的跟函数相关的方法。

bind

先来看一下 bind 这个方法。bind 这个词有绑定的意思,你可能在其它的地方也看到过这个词,比如在 jQuery 里面,它的意思就是去绑定事件。不过在 Underscore 这个上下文里面,bind 的意思是为函数绑定一个对象。这样在调用这个函数的时候,在函数的内部,this 这个关键词表示的就是这个对象。

下面,我们去试一下这个方法 ... 先定义一个函数 ... 可以使用函数表达式的方法去定义这个函数 .. (#编辑器) ..

给这个函数起个名字 .. 叫做 nowPlaying ... function ... 在这个函数里面,让它 返回 this 这个关键词 ...

var nowPlaying = function() {
return this;
}

在下面,我们再去调用这个函数 ...

nowPlaying()

你会发现,这个函数会返回 Window 这个对象 .. 也就是,在这个函数里面,this 这个关键词表示的是 Window 这个对象 ...

我们这里有一个 album 对象 ... (#album)

下面用 bind 方法,把它跟 nowPlaying 这个函数绑到一块儿 ..

nowPlaying 等于 _.bind ... 第一个参数是绑定的函数 ... 这里就是 nowPlaying 这个函数 .. 第二个参数是要绑定到这个函数上的对象 ... 输入 album 这个对象 ...

nowPlaying = _.bind(nowPlaying, album)

然后再去调用一下这个 nowPlaying ... nowPlaying()

现在,这个函数返回的就是用 bind 方法跟它绑定到一块儿的那个 album 对象了。

这样我们再去改造一下 nowPlaying 这个函数 .. 可以让它授受一个参数 ... username ...

然后在用 bind 方法绑定的时候,它的第三个参数,就可以是传递给这个函数的参数的值 ... 这里我们可以设置成 'wanghao'

在这个 nowPlaying 的函数里,让它返回一些东西 ..

return username + ',您正在收听的专辑是:' + this.title + ',来自:' + this.artist;

保存 ... 再到控制台上试试 ... 刷新 ..

然后调用 nowPlaying() 这个函数 ...

会返回一串文字 ... wanghao 是 nowPlaying 的参数值,在用 bind 方法绑定的时候传递进来的 ... 继续革命 是来自 album 对象的 title 属性 .. Beyond 是 album 里面的 artist 属性的值 ...

来自  https://ninghao.net/video/1500#info

22)把对象里的一些方法绑定到对象上 - bindAll

bindAll 这个方法可以把一些方法绑定到一个对象上面,这样在执行这个方法的时候, 在这些方法里面,this 这个关键词表示的就是跟这些方法绑定到一块儿的那个对象。下面,我们再通过一个演示来理解一下 bindAll 的作用。

先打开 index.html 这个文档 ...

div.container>h1.page-header{bindAll}+button.btn.btn-default.btn-lg*2

在这上面添加两个按钮 .. 一个是播放按钮 ... 上面可以添加一个 id 叫做 play ... 再添加一些其它的 css 类,这些类的样式是在 bootstrap 这个框架里面定义的。

再添加一个 暂停 按钮 .. 上面添加一个叫 pause 的 id ...

<div class="container">
<h1 class="page-header">bindAll</h1>
<button id='play' class="btn btn-default btn-lg">播放</button>
<button id='pause' class="btn btn-default btn-lg">暂停</button>
</div>

回到 data.js .. 然后我们可以在 album 这个对象里面,添加两个方法 .. 一个叫 play ... 让它做的事暂时只是返回 this 这个关键词表示的东西 ...

play: function() { console.log('正在播放:' + this.title); },

再添加一个叫 pause 的方法 ... 它作的事儿也是返回 this 这个关键词代表的内容 ...

pause: function() { console.log('暂停播放:' + this.title); }

然后我们可以去监听播放还有暂停这两个按钮的点击事件 ... 当有人点击它们的时候,去执行 album 里的 play 这个方法,还有 pause 这个方法 ...

用 jQuery 去绑定事件 .. 先找到页面上的 id 是 play 的元素,也就是播放按钮元素 ... 用 on 这个方法,添加的事件是 click ... 这个事件发生的时候,去执行 album 对象里面的 play 这个方法 ...

再用类似的方法,去给 pause 按钮绑定一个 click 事件 ... 事件发生的时候,去执行 album 对象的 pause 方法 ...

jQuery('#play').on('click', album.play);
jQuery('#pause').on('click', album.pause);

演示

下面我们到控制台上试一下 ... 点击页面上的 播放 按钮 ... 这会触发 click 事件,这个事件发生的时候去执行的是 album 对象里面的 play 方法 ... 这个方法要做的事就是返回 this 这个关键词表示的东西 ...

你会发现,返回的是发生点击事件的这个按钮元素 ...

再点一下 暂停 ... 返回的是停止这个按钮元素 .. 这其实并不是我们想要的,我们想要的让方法里面的 this 表示的方法所属的那个对象 ... 这样我们就可以使用 this 关键词得到这个对象里面的某些属性的值 ...

bindAll

实现这个功能,我们就可以去使用 Underscore 里面的 bindAll 这个方法 ... 回到 app.js ...

输入 bindAll ... 先指定一下绑定的对象 .. 这里就是 album 这个对象 ... 它的后面就是绑定到这个对象上的方法的列表 ... 方法之间可以使用逗号分隔开 ... 在这里我们要绑定的是 play 还有 stop 这两个方法 ...

_.bindAll(album, 'play', 'pause');

这样在 play 还有 pause 这两个方法里面,this 表示的就是 album 这个对象了 ... 再回到上面,去修改一下这两个方法在控制台上输出的东西 ..

play: function() { console.log('正在播放:' + this.title); },
pause: function() { console.log('暂停播放:' + this.title); }

这里的 this.title ,输出的就应该是 album 这个对象里的 title 属性的值 .. 应该就是 继续革命 这几个字儿 ..

保存 ... 再回到浏览器 ...

刷新 ... 然后点击 播放 .. 在控制台上,会显示 正在播放:继续革命 ...

然后再点一下 暂停 ...

会输出,暂停播放:继续革命 ..

来自  https://ninghao.net/video/1501#info


23)延时执行函数 - delay

通常调用函数以后,会立即执行这个函数。使用 Underscore 的 delay 方法, 我们可以延时去执行函数 ...

先在控制台上,直接定义一个函数 .. 可以叫它 fireIntheHole ... 这个函数能做的事,就是在控制台上输出 Bommmm

var fireIntheHole = function() {
console.log('Boommm...');
};

先去执行一下这个函数 ... 输入 fireIntheHole ...

会在控制台上立即输出 Bommmm ... 人还没跑远呢手雷就炸了 ...

下面,我们可以使用 delay 去延时执行这个函数 ...

_.delay ... 它的第一个参数是要延时执行的函数 ... 这里就是 fireIntheHole ... 然后第二个参数要延迟的时间 ... 单位是毫秒 .. 1000 毫秒就是 1 秒 .. 这里我们设置成 3000 ,表示三秒以后去执行这个函数 ..

另外,还可以给这个方法添加第三个参数,也就是要传递给函数的参数的值 ...

_.delay(fireIntheHole, 3000)

现在,如果回车执行这行代码以后,会在 3 秒钟以后执行 fireIntheHole 这个函数 ...

回车 ...

3 ... 2... 1... 捂上耳朵 ...

Bommmm


来自  https://ninghao.net/video/1502#info


24)你继续,我先等会儿 - defer

程序里面可能会有一些函数要做比较复杂的运算,我们可以让这些运算都完成以后再去执行这个函数。这样,不会影响到程序继续去执行其它的代码。

比如这里我们有一个函数 fireIntheHole ... 它做的事就是在控制台上输出 bomm.. bomm.. boom...

在这个函数的下面, 还有一个叫 run 的函数 ... 它会在控制台上输出 捂上耳朵,狂奔!这几个字儿 ...

在下面,我们先去执行一下 fireIntheHole .... 接着去执行 run 这个函数 ...

回到浏览器 .... 刷新一下 ..

你会看到,控制台上先会显示在 fireIntheHole 里面的 boom ... bomm... bomm ...

然后才会执行 run ,捂上耳朵,狂奔!

这时候人已经完了,对吧。

下面,我们去改造一下 ...

用 defer 方法,去处理这个 fireIntheHole 这个函数 ...

_.defer(fireIntheHole);

保存 ... 再回到浏览器 ... 刷新一下 ...

这次,你会看到,先会去执行 run 函数 ... 这时候人已经跑远了 ...

然后才会显示 fireIntheHole 里面的 bomm........

因为这里我们用 defer 方法处理了一下 fireIntheHole ... 意思就是,你们先跑吧,等会儿我处理好了再炸 ...

来自  https://ninghao.net/video/1503#info


25)防止滥用- throttle

一个函数在执行以后,再次执行它的时候,你可以设置一下两次执行间隔的时间。这就是 Undersocre 的 throttle 的功能。

比如这里我们有一个函数 ... fireIntheHole ... 我们打算让程序在每次执行这个函数的时候,必须间隔一段时间才行 ... 可以使用 throttle 去处理一下它 ...

可以给这个防止滥用版本的函数起个新名字 ... 叫它 fire ... 然后用 throttle ... 第一个参数是要处理的函数的名字 ... 这里就是 fireIntheHole ... 第二个参数是等待的时间,也就是两次执行这个函数中间隔的时间 ... 单位是毫秒 ... 这里设置成 3000 毫秒 ...

var fire = _.throttle(fireIntheHole, 3000);

这样执行 fire 之后 ... 才次再想去执行 fire ,只有等待 3 秒以后才行。

保存 ...

到浏览器上试试 ...

先执行一次 fire ... 然后再继续去执行它 ... 你会发现,第一次执行以后,再次执行它的时候,只有等待一会儿,才会起作用。

来自  https://ninghao.net/video/1504

26)只能执行一次的函数 - once

有时候,我们可能需要有的函数只能被执行一次,比如用这样的函数去初始化一些东西。

创建这样的函数,可以使用 Underscore 的 once 这个方法。

现在我们这里有一个函数叫 fireIntheHole ... 想让它只能被执行一回 ... 用 once 去处理一下 ... 可以给这个一次性的版本的函数起个新名字 .. 叫它 fire ...

var fire = _.once(fireIntheHole)

保存 ... 回到浏览器 .

在控制台上,先执行一次 fire ...

会显示 bomm ...

然后再执行一下它 ...

会返回 undefined ... 再执行 ... 还是会返回 undefined ...

因为 fire 是一次性的函数,才能被执行一回 ...

来自  https://ninghao.net/video/1505

其它

27)对象 - keys, values, pick, omit

下面我们学习几个在 Underscore 里面跟对象相关的一些方法。

keys 这方法可以得到一个对象里面的所有的属性的名字 ... 比如要得到 album 这个对象里的属性名称列表 .. 可以把这个对象作为 keys 方法的一个参数 ...

_.keys(album)

返回的结果是一个数组,数组里面的项目就是在 album 这个对象里面的每个属性的名字 ...

values

如果你想要的是对象里面的所有的属性的值,可以使用 values 方法 ...

_.values(album)

它也会返回一个数组,数组里面的项目就是在 albums 这个对象里面的属性的值 ...

pick

如果你只想要对象里面的指定的几个属性 ... 可以使用 pick 方法去挑选一下 ... 要挑选的是 album 这个对象 ... 然后再去指定一个想要的属性的列表 .. 这里我们只想要的是 title ,还有 artist 这两个属性 ..

_.pick(album, 'title', 'artist')

返回的结果是一个对象,在这个对象里面,会包含我们挑选的几个属性 ...

omit

pick 有点像是去设置一个属性的白名单,在这个名单里面的属性会被选中。另外,还有一个 omit 方法,它的功能有点像是设置一个属性的黑名单 ... 凡是在这个名单里面的属性都不会被选中。

omit 就是忽略掉的意思 ...

_.omit(album, 'location')

回车 ... 返回的对象里面,不会包含 location 这个属性 ... 剩下的其它的属性都会在这个对象里面。

来自  https://ninghao.net/video/1506#info


28)链式调用 - chain

使用 Underscore 的 chain 方法,可以返回一个包装的对象,这个对象里面包含着 Underscore 里面的所有的方法,也就是我们可以直接调用这些方法,完成以后,还会返回这个包装后的对象,这样我们可以继续去调用 Underscore 的方法,这就是链式调用。

现在这里有一个 albums 数组 ... 下面,我们先用 chain 方法去包装一下它 ...

_.chain(albums)

给我们返回来一个包装后的对象 ... 在 _wrapped 属性里面,就是包装的原来的数据 ... __proto__ 里面就是 Underscore 的方法 ...

下面,我们先调用一个 sortBy 这个方法,去给包装后的数据排下顺序,排序的条件,可以使用 releaseYear 这个属性的值 ...

_.chain(albums).sortBy('releaseYear')

回车 .. 返回的还是一个包装后的数据 ... 我们可以继续去调用方法处理它 ...

这里,再用一个 first() 方法,得到排序后的第一个结果 ...

现在,_wrapped 里面,就只剩下 继续革命 这个专辑项目了 ... 因为它是排序之后的第一个结果 ...

最后,得到处理好的结果的值,可以调用 value() 方法 ...

返回的就是处理之后得到的这个对象 ...

来自  https://ninghao.net/video/1507#info
普通分类: