Underscore JS 中文文档

Underscore 是一个JavaScript实用库,提供了类似Prototype.js (或 Ruby)的一些功能,但是没有继承任何JavaScript内置对象。它弥补了部分jQuery没有实现的功能,同时又是Backbone.js必不可少的部分。

我主要是用来在mongo中使用其一些常用数组的操作,如果有更好的工具,请友情告之。

Underscore提供了80多个函数,包括常用的: map, select, invoke — 当然还有更多专业的辅助函数,如:函数绑定, JavaScript模板功能, 强类型相等测试, 等等. 在新的浏览器中, 有许多函数如果浏览器本身直接支持,将会采用原生的,如 forEach, map, reduce, filter, every, some 和 indexOf.

下载地址:
http://learning.github.io/underscore/underscore-min.js

集合函数 (数组或对象)

each_.each(list, iterator, [context]) 别名: forEach
对一个 list 的所有元素进行迭代, 对每一个元素执行 iterator 函数. iterator 和 context 对象绑定, 如果传了这个参数. 每次 iterator 的调用将会带有三个参数: (element, index, list). 如果 list 是一个 JavaScript 对象, iterator 的参数将会是 (value, key, list). 如果有原生的 forEach 函数就会用原生的代替.

.each([1, 2, 3], alert);
=> 依次alert每个数字…
.each({one: 1, two: 2, three: 3}, alert);
=> 依此alert每个数字…
注意:集合函数对于数组、对象、以及arguments,NodeList等类似数组的对象都有用。但是对鸭子类型对象也有用,所以请避免传入拥有length属性的对象。

map_.map(list, iterator, [context]) 别名: collect
映射 list 里的每一个值, 通过一个转换函数(iterator)产生一个新的数组. 如果有原生的 map 函数, 将用之代替. 如果 list 是一个 JavaScript 对象, iterator的参数将会是 (value, key, list).

.map([1, 2, 3], function(num){ return num * 3; });
=> [3, 6, 9]
.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
=> [3, 6, 9]
reduce_.reduce(list, iterator, memo, [context]) 别名: inject, foldl
也被称为 inject 和 foldl, reduce 将一个 list 里的所有值归结到一个单独的数值. Memo 是归结的初始值, 而且每一步都由 iterator返回. 迭代器 iterator 会传入四个参数: memo, value 和迭代的索引index (或 key), 最后还有对整个 list 的一个引用.

var sum = .reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
=> 6
reduceRight
.reduceRight(list, iterator, memo, [context]) 别名: foldr
reduce的右结合版本. 如果可能, 将调用 JavaScript 1.8 版本原生的 reduceRight. Foldr 在 JavaScript 中并没那么有用, 人们对它的评价并不好.

var list = [[0, 1], [2, 3], [4, 5]];
var flat = .reduceRight(list, function(a, b) { return a.concat(b); }, []);
=> [4, 5, 2, 3, 0, 1]
find
.find(list, iterator, [context]) 别名: detect
从list里进行逐项查找,返回第一个符合测试(iterator)条件的元素,如果没有的话则返回undefined。 此函数只返回第一个符合条件的元素,并不会遍历整个list。

var even = .find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> 2
filter
.filter(list, iterator, [context]) 别名: select
在 list 里的每一项进行查找, 返回一个符合测试 (iterator) 条件的所有元素的集合. 如果存在原生的 filter 方法, 将采用原生的.

var evens = .filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]
where
.where(list, properties)
遍历 list 里的每一个值, 返回包含 properties 里所有 key-value 组合的对象的数组.

.where(listOfPlays, {author: "Shakespeare", year: 1611});
=> [{title: "Cymbeline", author: "Shakespeare", year: 1611},
{title: "The Tempest", author: "Shakespeare", year: 1611}]
findWhere
.findWhere(list, properties)
从list里进行逐项查找,返回第一个符合properties里键值对的元素,如果没有的话则返回undefined。

如果没有找到符合的元素,或者list为空,将会返回undefined。

.findWhere(publicServicePulitzers, {newsroom: "The New York Times"});
=> {year: 1918, newsroom: "The New York Times",
reason: "For its public service in publishing in full so many official reports,
documents and speeches by European statesmen relating to the progress and
conduct of the war."}
reject
.reject(list, iterator, [context])
返回在 list 不能通过测试 (iterator) 的所有元素的集合. 与 filter 相反.

var odds = .reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [1, 3, 5]
every
.every(list, [iterator], [context]) 别名: all
如果所有在 list 里的元素通过了 iterator 的测试, 返回 true. 如果存在则使用原生的 every 方法.

.every([true, 1, null, ‘yes’], .identity);
=> false
some_.some(list, [iterator], [context]) 别名: any
如果任何 list 里的任何一个元素通过了 iterator 的测试, 将返回 true. 一旦找到了符合条件的元素, 就直接中断对list的遍历. 如果存在, 将会使用原生的 some 方法.

.some([null, 0, ‘yes’, false]);
=> true
contains
.contains(list, value) 别名: include
如果 value 存在与 list 里, 返回 true. 如果 list 是一个数组, 内部会使用 indexOf.

.contains([1, 2, 3], 3);
=> true
invoke
.invoke(list, methodName, [*arguments])
在 list 里的每个元素上调用名为 methodName 的函数. 任何附加的函数传入, invoke 将会转给要调用的函数.

.invoke([[5, 1, 7], [3, 2, 1]], ‘sort’);
=> [[1, 5, 7], [1, 2, 3]]
pluck
.pluck(list, propertyName)
一个 map 通常用法的简便版本: 提取一个集合里指定的属性值.

var stooges = [{name: ‘moe’, age: 40}, {name: ‘larry’, age: 50}, {name: ‘curly’, age: 60}];
.pluck(stooges, ‘name’);
=> ["moe", "larry", "curly"]
max
.max(list, [iterator], [context])
返回 list 里最大的元素. 如果传入了 iterator, 它将用来比较每个值.

var stooges = [{name: ‘moe’, age: 40}, {name: ‘larry’, age: 50}, {name: ‘curly’, age: 60}];
.max(stooges, function(stooge){ return stooge.age; });
=> {name: ‘curly’, age: 60};
min
.min(list, [iterator], [context])
返回 list 里最小的元素. 如果传入了 iterator, 它将用来比较每个值.

var numbers = [10, 5, 100, 2, 1000];
.min(numbers);
=> 2
sortBy
.sortBy(list, iterator, [context])
返回一个经过排序的 list 副本, 用升序排列 iterator 返回的值. 迭代器也可以用字符串的属性来进行比较(如length).

.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
=> [5, 4, 6, 3, 1, 2]
groupBy
.groupBy(list, iterator)
把一个集合分为多个集合, 通过 iterator 返回的结果进行分组. 如果 iterator 是一个字符串而不是函数, 那么将使用 iterator 作为各元素的属性名来对比进行分组.

_.groupBy([1.3, 2.1, 2.4], function(num){ return Math.floor(num); });
=> {1: [1.3], 2: [2.1, 2.4]}

.groupBy([‘one’, ‘two’, ‘three’], ‘length’);
=> {3: ["one", "two"], 5: ["three"]}
indexBy
.indexBy(list, iterator, [context])
Given a list, and an iterator function that returns a key for each element in the list (or a property name), returns an object with an index of each item. Just like groupBy, but for when you know your keys are unique.

var stooges = [{name: ‘moe’, age: 40}, {name: ‘larry’, age: 50}, {name: ‘curly’, age: 60}];
.indexBy(stooges, ‘age’);
=> {
"40": {name: ‘moe’, age: 40},
"50": {name: ‘larry’, age: 50},
"60": {name: ‘curly’, age: 60}
}
countBy
.countBy(list, iterator)
把一个数组分组并返回每一组内对象个数. 与 groupBy 相似, 但不是返回一组值, 而是组内对象的个数.

.countBy([1, 2, 3, 4, 5], function(num) {
return num % 2 == 0 ? ‘even’: ‘odd’;
});
=> {odd: 3, even: 2}
shuffle
.shuffle(list)
返回一个随机乱序的 list 副本, 使用 Fisher-Yates shuffle 来进行随机乱序.

.shuffle([1, 2, 3, 4, 5, 6]);
=> [4, 1, 6, 3, 5, 2]
sample
.sample(list, [n])
从 list 里进行随机取样. 传一个数字 n 来决定返回的样本个数, 否则只返回一个样本.

_.sample([1, 2, 3, 4, 5, 6]);
=> 4

.sample([1, 2, 3, 4, 5, 6], 3);
=> [1, 6, 2]
toArray
.toArray(list)
将一个 list (任何可以被进行迭代的对象)转换成一个数组. 在转换 arguments 对象时非常有用.

(function(){ return .toArray(arguments).slice(1); })(1, 2, 3, 4);
=> [2, 3, 4]
size
.size(list)
返回 list 里所有元素的个数.

_.size({one: 1, two: 2, three: 3});
=> 3

数组函数

注意: 所有数组函数都可以用在 arguments 对象上. 然而, Underscore 函数的设计并不只是针对稀疏数组的.

first_.first(array, [n]) 别名: head, take
返回数组 array 里的第一个元素. 如果传了参数 n 将返回数组里前 n 个元素.

.first([5, 4, 3, 2, 1]);
=> 5
initial
.initial(array, [n])
返回一个数组里除了最后一个元素以外的所有元素. 在arguments对象上特别有用. 传参 n 将排除数组最后的 n 个元素.

.initial([5, 4, 3, 2, 1]);
=> [5, 4, 3, 2]
last
.last(array, [n])
返回数组 array 里的最后一个元素. 传参 n 将返回 数组里的后 n 个元素.

.last([5, 4, 3, 2, 1]);
=> 1
rest
.rest(array, [index]) 别名: tail, drop
返回一个数组里除了第一个以外 剩余的 所有元素. 传参 index 将返回除了第 index 个元素以外剩余的所有元素.

.rest([5, 4, 3, 2, 1]);
=> [4, 3, 2, 1]
compact
.compact(array)
返回一个数组 array 除空(真值检验为false)后的副本. 在JavaScript里, false, null, 0, "", undefined 和 NaN 真值检验的结果都为false.

.compact([0, 1, false, 2, ”, 3]);
=> [1, 2, 3]
flatten
.flatten(array, [shallow])
将一个嵌套多层的数组 array (嵌套可以是任何层数)转换为只有一层的数组. 如果传参 shallow 为true, 数组只转换第一层.

_.flatten([1, [2], [3, [[4]]]]);
=> [1, 2, 3, 4];

.flatten([1, [2], [3, [[4]]]], true);
=> [1, 2, 3, [[4]]];
without
.without(array, [*values])
返回一个除去所有 values 后的 array 副本.

.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
=> [2, 3, 4]
union
.union(*arrays)
返回传入的多个数组 arrays 结合后的数组: 且所有数组元素都是唯一的, 传入的数组可以是一个或多个数组 arrays.

.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2, 3, 101, 10]
intersection
.intersection(*arrays)
返回一个多个数组 arrays 的交集. 即返回的数组里每个元素, 都存在于参数 arrays 每个数组里.

.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]
difference
.difference(array, *others)
跟 without 相似, 但是返回的数组是 array 里跟别的数组 other 里不一样的元素.

.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]
uniq
.uniq(array, [isSorted], [iterator]) 别名: unique
返回 array 去重后的副本, 使用 === 做相等测试. 如果您确定 array 已经排序, 给 isSorted 参数传如 true, 此函数将使用更快的算法. 如果要处理对象元素, 传参 iterator 来获取要对比的属性.

.uniq([1, 2, 1, 3, 1, 4]);
=> [1, 2, 3, 4]
zip
.zip(*arrays)
合并 arrays 里每一个数组的每个元素, 并保留对应位置. 在合并分开保存的数据时很有用. 如果你用来处理矩阵嵌套数组时, zip.apply 可以做类似的效果.

_.zip([‘moe’, ‘larry’, ‘curly’], [30, 40, 50], [true, false, false]);
=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]

.zip.apply(, arrayOfRowsOfData);
=> arrayOfColumnsOfData
object_.object(list, [values])
把数组转换成对象. 传一个或多个 [key, value] 形式的数组, 或者一个包含key的数组和一个包含value的数组.

_.object([‘moe’, ‘larry’, ‘curly’], [30, 40, 50]);
=> {moe: 30, larry: 40, curly: 50}

.object([[‘moe’, 30], [‘larry’, 40], [‘curly’, 50]]);
=> {moe: 30, larry: 40, curly: 50}
indexOf
.indexOf(array, value, [isSorted])
返回元素 value 在数组 array 里的索引位置, 如果元素没在数组 array 中, 将返回 -1. 此函数将使用原生的 indexOf 方法, 除非原生的方法无故消失或者被覆盖重写了, 才使用非原生的. 如果您要处理一个大型数组, 而且确定数组已经排序, 参数 isSorted 可以传 true, 函数将使用更快的二分搜索来进行处理… 或者, 传一个数字作为 第三个参数, 以便于在指定索引之后开始寻找对应值.

.indexOf([1, 2, 3], 2);
=> 1
lastIndexOf
.lastIndexOf(array, value, [fromIndex])
返回元素 value 在数组 arrry 里最后一次出现的索引位置, 如果元素没在数组 array 中, 将返回 -1. 如有可能, 此函数将使用原生的 lastIndexOf 方法. 传参 fromIndex 以便从指定索引开始寻找.

.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
=> 4
sortedIndex
.sortedIndex(list, value, [iterator])
为了保持 list 已经排好的顺序, 使用二分搜索来检测 value 应该 插入到 list 里的所在位置的索引. 如果传入了一个 iterator , 它将用来计算每个值的排名, 包括所传的 value 参数.

_.sortedIndex([10, 20, 30, 40, 50], 35);
=> 3

var stooges = [{name: ‘moe’, age: 40}, {name: ‘curly’, age: 60}];
.sortedIndex(stooges, {name: ‘larry’, age: 50}, ‘age’);
=> 1
range
.range([start], stop, [step])
一个灵活创建范围内整数数组的函数, each 和 map 循环整合的简便版本. 如果省略start 参数, 默认为 0; step 默认为 1. 返回一个数组, 包含从 start 到 stop (不包含stop) 范围内, 以 step 递增(减)的整数.

.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []
与函数有关的函数

bind_.bind(function, object, [*arguments])
绑定函数 function 到对象 object 上, 也就是无论何时函数被调用, 函数里的 this 都指向 object. 可选参数 arguments 可以绑定到函数 function , 可以填充函数所需要的参数, 这也被成为 部分应用.

var func = function(greeting){ return greeting + ‘: ‘ + this.name };
func = .bind(func, {name: ‘moe’}, ‘hi’);
func();
=> ‘hi: moe’
bindAll
.bindAll(object, [*methodNames])
绑定 methodNames 指定的方法到 object 上, 当这些方法被执行时将在对象的上下文执行. 绑定函数用作事件处理时非常方便, 否则函数调用时 this 关键字根本没什么用. 如果不传 methodNames 参数, 对象里的所有方法都被绑定.

var buttonView = {
label : ‘underscore’,
onClick: function(){ alert(‘clicked: ‘ + this.label); },
onHover: function(){ console.log(‘hovering: ‘ + this.label); }
};
_.bindAll(buttonView, ‘onClick’, ‘onHover’);
// 当按钮被点击, this.label将会取到正确的值, 即buttonView里的label而不是按钮的label.
jQuery(‘#underscorebutton’).bind(‘click’, buttonView.onClick);
partial
.partial(function, [*arguments])
Partially apply a function by filling in any number of its arguments, without changing its dynamic this value. A close cousin of bind.

var add = function(a, b) { return a + b; };
add5 = .partial(add, 5);
add5(10);
=> 15
memoize
.memoize(function, [hashFunction])
通过缓存计算结果使函数 function 具有记忆功能. 在优化耗时较长的算时法非常有用. 如果传了可选参数 hashFunction, 将用其返回的值作为key来保存函数的运行结果, 以原始函数的参数为基础. hashFunction 默认使用被缓存函数的第一个参数作为key.

var fibonacci = .memoize(function(n) {
return n < 2 ? n: fibonacci(n – 1) + fibonacci(n – 2);
});
delay
.delay(function, wait, [*arguments])
和 setTimeout 方法很像, 在 wait 毫秒之后调用 function 函数. 如果传了可选参数 arguments, 在函数 function 调用的时候会作为参数传入.

var log = .bind(console.log, console);
.delay(log, 1000, ‘logged later’);
=> ‘logged later’ // 一秒钟后显示.
defer_.defer(function, [*arguments])
延迟调用 function 函数, 直到当前调用栈被清空为止, 跟使用 setTimeout 赋予0毫秒的延时很像. 对执行高消耗算法或大型HTML呈现而不阻碍UI更新线程很有用. 如果传了可选参数 arguments, 在函数 function 调用的时候会作为参数传入.

.defer(function(){ alert(‘deferred’); });
// 将在alert显示之前返回这个function
throttle
.throttle(function, wait)
返回一个类似于节流阀一样的函数, 当高频率的调用函数, 实际上会每隔 wait 毫秒才会调用一次. 对于高到您感觉不到的高频率执行的函数时非常有用.

By default, throttle will execute the function as soon as you call it for the first time, and, if you call it again any number of times during the wait period, as soon as that period is over. If you’d like to disable the leading-edge call, pass {leading: false}, and if you’d like to disable the execution on the trailing-edge, pass
{trailing: false}.

var throttled = .throttle(updatePosition, 100);
$(window).scroll(throttled);
debounce
.debounce(function, wait, [immediate])
返回 function 函数的防反跳版本, 将延迟函数的执行(真正的执行)在函数最后一次调用时刻的 wait 毫秒之后. 对于必须在一些输入(多是一些用户操作)停止到达之后执行的行为有帮助。 例如: 渲染一个Markdown格式的评论预览, 当窗口停止改变大小之后重新计算布局, 等等.

传参 immediate 为 true 会让 debounce 在 wait 间隔之后 触发最后的函数调用而不是最先的函数调用. 在类似不小心点了提交按钮两下而提交了两次的情况下很有用.

var lazyLayout = .debounce(calculateLayout, 300);
$(window).resize(lazyLayout);
once
.once(function)
创建一个只能运行一次的函数. 重复调用此修改过的函数会没有效果, 只会返回第一次执行时返回的结果. 作为初始化函数使用时非常有用, 不用再设一个boolean值来检查是否已经初始化完成.

var initialize = .once(createApplication);
initialize();
initialize();
// Application只会创建一次.
after
.after(count, function)
创建一个函数, 只有在运行了 count 次之后才有效果. 在处理同组异步请求返回结果时, 如果你要确保同组里所有异步请求完成之后才 执行这个函数, 这将非常有用.

var renderNotes = .after(notes.length, render);
.each(notes, function(note) {
note.asyncSave({success: renderNotes});
});
// renderNotes 只会运行一次, 而且是在所有 notes 保存完毕之后才执行.
wrap_.wrap(function, wrapper)
将第一个函数 function 封装到函数 wrapper 里面, 并把函数 function 作为第一个参数传给 wrapper. 这样可以让 wrapper 在 function 运行之前和之后 执行代码, 调整参数然后附有条件地执行.

var hello = function(name) { return "hello: " + name; };
hello = .wrap(hello, function(func) {
return "before, " + func("moe") + ", after";
});
hello();
=> ‘before, hello: moe, after’
compose
.compose(*functions)
返回函数集 functions 组合后的复合函数, 也就是一个函数执行完之后把返回的结果再作为参数赋给下一个函数来执行. 以此类推. 在数学里, 把函数 f(), g(), 和 h() 组合起来可以得到复合函数 f(g(h())).

var greet = function(name){ return "hi: " + name; };
var exclaim = function(statement){ return statement.toUpperCase() + "!"; };
var welcome = _.compose(greet, exclaim);
welcome(‘moe’);
=> ‘hi: MOE!’

对象函数

keys_.keys(object)
获取 object 对象的所有属性名.

.keys({one: 1, two: 2, three: 3});
=> ["one", "two", "three"]
values
.values(object)
获取 object 对象的所有属性值.

.values({one: 1, two: 2, three: 3});
=> [1, 2, 3]
pairs
.pairs(object)
把一个对象转换成一个 [key, value] 形式的数组.

.pairs({one: 1, two: 2, three: 3});
=> [["one", 1], ["two", 2], ["three", 3]]
invert
.invert(object)
返回一个 object 的副本, 并且里面键和值是对调的. 要使之有效, 必须确保object里所有的值都是唯一的且可以序列号成字符串.

.invert({Moe: "Moses", Larry: "Louis", Curly: "Jerome"});
=> {Moses: "Moe", Louis: "Larry", Jerome: "Curly"};
functions
.functions(object) 别名: methods
返回一个对象里所有的方法名, 而且是已经排序的 — 也就是说, 对象里每个方法(属性值是一个函数)的名称.

.functions();
=> ["all", "any", "bind", "bindAll", "clone", "compact", "compose" …
extend_.extend(destination, *sources)
复制 source 对象的所有属性到 destination 对象上, 然后返回 destination 对象. 复制是按顺序的, 所以后面的对象属性会把前面的对象属性覆盖掉(如果有重复).

.extend({name: ‘moe’}, {age: 50});
=> {name: ‘moe’, age: 50}
pick
.pick(object, *keys)
返回一个 object 对象的副本, 过滤掉除了 keys 以外的所有属性(一个或多个).

.pick({name: ‘moe’, age: 50, userid: ‘moe1’}, ‘name’, ‘age’);
=> {name: ‘moe’, age: 50}
omit
.omit(object, *keys)
返回一个 object 对象的副本, 过滤掉了黑名单里的 keys (keys可以是单个key也可以是包含多个key的数组).

.omit({name: ‘moe’, age: 50, userid: ‘moe1’}, ‘userid’);
=> {name: ‘moe’, age: 50}
defaults
.defaults(object, *defaults)
用 defaults 对象里的默认值来填充 object 对象里遗漏的属性值, 并返回 object 对象. 当属性值已被填充遗漏, 再添加属性值就没用了.

var iceCream = {flavor: "chocolate"};
_.defaults(iceCream, {flavor: "vanilla", sprinkles: "lots"});
=> {flavor: "chocolate", sprinkles: "lots"}