此版本由Learning翻译, 如有翻译不当或者更好的翻译建议, 欢迎大家到 问题反馈 处提交.

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

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

一个完整的 测试评分 可以给您更详细的对比.

您也可以阅读 带注释的源码.

此项目 在GitHub托管. 您可以在 issues页面报告Bug或者讨论功能, 或者 Freenode 上的 #documentcloud 频道, 或者发推到 @documentcloud.

Underscore 是一个DocumentCloud的开源组件.

下载 (右键单击, 选择 "另存为")

开发版 (1.5.2) 43kb, 未压缩版, 含大量注释
生产版 (1.5.2) 4.9kb, 最简化并用Gzip压缩  (Source Map)
不稳定版 未发布版本, 当前开发中的 master 分支, 如果使用此版本, 风险自负

集合函数 (数组或对象)

each_.each(list, iterator, [context]) 别名: forEach
对一个 list 的所有元素进行迭代, 对每一个元素执行 iterator 函数. iteratorcontext 对象绑定, 如果传了这个参数. 每次 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每个数字...

注意:集合函数对于数组、对象、以及argumentsNodeList等类似数组的对象都有用。但是对鸭子类型对象也有用,所以请避免传入拥有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
也被称为 injectfoldl, 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, "", undefinedNaN 真值检验的结果都为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])
一个灵活创建范围内整数数组的函数, eachmap 循环整合的简便版本. 如果省略start 参数, 默认为 0; step 默认为 1. 返回一个数组, 包含从 startstop (不包含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('#underscore_button').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格式的评论预览, 当窗口停止改变大小之后重新计算布局, 等等.

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

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. 这样可以让 wrapperfunction 运行之前和之后 执行代码, 调整参数然后附有条件地执行.

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"}

clone_.clone(object)
克隆对象 object. 任何嵌套的对象或数组将会被引用, 而不是复制.

_.clone({name: 'moe'});
=> {name: 'moe'};

tap_.tap(object, interceptor)
用对象 object 作为参数来调用函数 interceptor, 然后返回 object 对象. 此方法的主要意图是作为函数链式调用 的一环, 为了对此对象执行操作并返回对象本身.

_.chain([1,2,3,200])
  .filter(function(num) { return num % 2 == 0; })
  .tap(alert)
  .map(function(num) { return num * num })
  .value();
=> // [2, 200] (alerted)
=> [4, 40000]

has_.has(object, key)
判断对象 object 包含指定的属性 key 吗? 和 object.hasOwnProperty(key) 相同, 但是使用了 hasOwnProperty 函数的安全引用, 更多请参考 意外重写.

_.has({a: 1, b: 2, c: 3}, "b");
=> true

isEqual_.isEqual(object, other)
执行优化过的算法来对两个对象进行深度的相等测试, 来判定两个对象是否相等(非全等).

var moe   = {name: 'moe', luckyNumbers: [13, 27, 34]};
var clone = {name: 'moe', luckyNumbers: [13, 27, 34]};
moe == clone;
=> false
_.isEqual(moe, clone);
=> true

isEmpty_.isEmpty(object)
如果 object 里没包含任何东西, 将返回 true.

_.isEmpty([1, 2, 3]);
=> false
_.isEmpty({});
=> true

isElement_.isElement(object)
如果 object 是一个DOM元素, 将返回 true.

_.isElement(jQuery('body')[0]);
=> true

isArray_.isArray(object)
如果 object 是一个数组, 将返回 true.

(function(){ return _.isArray(arguments); })();
=> false
_.isArray([1,2,3]);
=> true

isObject_.isObject(value)
如果 value 是一个对象, 将返回 true. 注意在JavaScript中数组和函数都是对象, 而普通的字符串和数字不是.

_.isObject({});
=> true
_.isObject(1);
=> false

isArguments_.isArguments(object)
如果 object 是参数(Arguments)对象, 将返回 true.

(function(){ return _.isArguments(arguments); })(1, 2, 3);
=> true
_.isArguments([1,2,3]);
=> false

isFunction_.isFunction(object)
如果 object 是函数, 将返回 true.

_.isFunction(alert);
=> true

isString_.isString(object)
如果 object 是字符串, 将返回 true.

_.isString("moe");
=> true

isNumber_.isNumber(object)
如果 object 是一个数字(包括NaN), 将返回 true.

_.isNumber(8.4 * 5);
=> true

isFinite_.isFinite(object)
如果 object 是一个有限的数字, 将返回 true.

_.isFinite(-101);
=> true

_.isFinite(-Infinity);
=> false

isBoolean_.isBoolean(object)
如果 object 是布尔值(truefalse), 将返回 true.

_.isBoolean(null);
=> false

isDate_.isDate(object)
如果 object 是时间对象, 将返回 true.

_.isDate(new Date());
=> true

isRegExp_.isRegExp(object)
如果 object 是正则表达式对象, 将返回 true

_.isRegExp(/moe/);
=> true

isNaN_.isNaN(object)
如果 objectNaN, 将返回 true.
注意: 这与原生的 isNaN 函数不一样, 原生的传参即使是 undefined 也会返回true.

_.isNaN(NaN);
=> true
isNaN(undefined);
=> true
_.isNaN(undefined);
=> false

isNull_.isNull(object)
如果 object 的值是 null, 将返回 true.

_.isNull(null);
=> true
_.isNull(undefined);
=> false

isUndefined_.isUndefined(value)
如果 variableundefined, 将会返回 true .

_.isUndefined(window.missingVariable);
=> true

实用功能

noConflict_.noConflict()
把 "_" 变量的控制权还给它原有的所有者. 返回一个 Underscore 对象的引用.

var underscore = _.noConflict();

identity_.identity(value)
返回与传入参数相等的值. 相当于数学里的: f(x) = x
这个函数看似无用, 但是在Underscore里被用作默认的迭代器iterator.

var moe = {name: 'moe'};
moe === _.identity(moe);
=> true

times_.times(n, iterator, [context])
调用指定的 iterator 函数 n 次. iterator 的每一次调用都带 index 参数.

_(3).times(function(n){ genie.grantWishNumber(n); });

random_.random(min, max)
返回一个介于 minmax 之间(包含)的整数. 如果直传一个参数, 将会返回一个介于 0 和参数之间的整数.

_.random(0, 100);
=> 42

mixin_.mixin(object)
允许您继承 Underscore 并加入您自己的功能函数. 传参 {名称: 函数} 来定义您的函数, 加入到 Underscore 对象中, 就像面向对象的封装.

_.mixin({
  capitalize: function(string) {
    return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
  }
});
_("fabio").capitalize();
=> "Fabio"

uniqueId_.uniqueId([prefix])
给客户端模型或DOM元素生成一个全局的唯一id. 如果传参 prefix , id将以此开头.

_.uniqueId('contact_');
=> 'contact_104'

escape_.escape(string)
转义HTML字符串, 替换 &, <, >, ", ', 和 / 字符.

_.escape('Curly, Larry & Moe');
=> "Curly, Larry &amp; Moe"

unescape_.unescape(string)
escape 函数相反, 反转义 &amp;, &lt;, &gt;, &quot;, &#x27;, and &#x2F;这些字符.

_.unescape('Curly, Larry &amp; Moe');
=> "Curly, Larry & Moe"

result_.result(object, property)
如果对象 object 中的属性 property 是函数, 则调用它, 否则, 返回它.

var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
_.result(object, 'cheese');
=> "crumpets"
_.result(object, 'stuff');
=> "nonsense"

template_.template(templateString, [data], [settings])
将Javascript模板编译为可以用于描绘页面的函数。在从JSON数据源中描绘出一些复杂的HTML时十分有用。模板函数中既可以使用“<%= … %>”插入变量,也可以使用“<% … %>”来执行任意的Javascript代码。如果你想插入一个经过HTML转义处理的值,可以使用“<%- … %>”。当你使用模板函数时,传入一个data对象,这个data对象应当拥有对应于模板中的各个自由变量的属性成员。如果你只是想写一个一次性的代码段,你可以将data对象作为template的第二个参数传入以便立即进行描绘,而非返回一个模板函数。setting参数应当是一个哈希表,包含任何应当被覆盖的_.templateSetting的值。

var compiled = _.template("hello: <%= name %>");
compiled({name: 'moe'});
=> "hello: moe"

var list = "<% _.each(people, function(name) { %> <li><%= name %></li> <% }); %>";
_.template(list, {people: ['moe', 'curly', 'larry']});
=> "<li>moe</li><li>curly</li><li>larry</li>"

var template = _.template("<b><%- value %></b>");
template({value: '<script>'});
=> "<b>&lt;script&gt;</b>"

你也可以在模板的Javascript中使用print。这有时比使用“<%= ... %>”更方便。

var compiled = _.template("<% print('Hello ' + epithet); %>");
compiled({epithet: "stooge"});
=> "Hello stooge"

如果你不喜欢ERB式的分隔符,你可以更改以下Underscore的模板设定来使用不同的符号来标识插入的代码:一个名为interpolate正则表达式来匹配那些应当被作为插入部分的字符,一个名为escape正则表达式来匹配那些应当经过HTML转义处理后被插入的表达式,以及一个名为evaluate正则表达式来匹配那些应当被执行但不插入最终的结果字符串的表达式。你可以定义或忽略这三个正则表达式的任意组合。比如,如果要实施Mustache.js式的模板的话:

_.templateSettings = {
  interpolate: /\{\{(.+?)\}\}/g
};

var template = _.template("Hello {{ name }}!");
template({name: "Mustache"});
=> "Hello Mustache!"

默认情况下,template通过with语句在本地作用域的数据中定位值。不过,你可以用variable设定来设定一个单独的变量名。这可以显著提高一个可以被描绘的模板的速度。

_.template("Using 'with': <%= data.answer %>", {answer: 'no'}, {variable: 'data'});
=> "Using 'with': no"

在对一个你不能再现的错误进行调试时,预编译你的模板非常有帮助。因为预编译模板可以提供行号和调用栈轨迹,这是在客户端进行编译时不可能得到的。在已经编译的模函数上,有一个source属性用来简单地进行预编译。

<script>
  JST.project = <%= _.template(jstText).source %>;
</script>

链式语法

您可以在面向对象或者函数的风格下使用Underscore, 这取决于您的个人偏好. 以下两行代码都可以 把一个数组里的所有数字乘以2.

_.map([1, 2, 3], function(n){ return n * 2; });
_([1, 2, 3]).map(function(n){ return n * 2; });

对一个对象使用 chain 方法, 会把这个对象封装并 让以后每次方法的调用结束后都返回这个封装的对象, 当您完成了计算, 可以使用 value 函数来取得最终的值. 以下是一个同时使用了 map/flatten/reduce 的链式语法例子, 目的是计算一首歌的歌词里每一个单词出现的次数.

var lyrics = [
  {line: 1, words: "I'm a lumberjack and I'm okay"},
  {line: 2, words: "I sleep all night and I work all day"},
  {line: 3, words: "He's a lumberjack and he's okay"},
  {line: 4, words: "He sleeps all night and he works all day"}
];

_.chain(lyrics)
  .map(function(line) { return line.words.split(' '); })
  .flatten()
  .reduce(function(counts, word) {
    counts[word] = (counts[word] || 0) + 1;
    return counts;
  }, {})
  .value();

=> {lumberjack: 2, all: 4, night: 2 ... }

此外, 数组原型方法 也通过代理加入到了链式封装的Underscore对象, 所以您可以 在链式语法中直接使用 reversepush 方法, 然后再接着其他的语句.

chain_.chain(obj)
返回一个封装的对象. 在封装的对象上调用方法会返回封装的对象本身, 直到 value 方法调用为止.

var stooges = [{name: 'curly', age: 25}, {name: 'moe', age: 21}, {name: 'larry', age: 23}];
var youngest = _.chain(stooges)
  .sortBy(function(stooge){ return stooge.age; })
  .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
  .first()
  .value();
=> "moe is 21"

value_(obj).value()
提取封装对象的最终值.

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

Underscore文档也有 简体中文 版(其实您现在所看到的就是).

Underscore.lua, 一个Lua版本的Underscore, 函数都通用. 包含面向对象封装和链式语法. (源码)

Underscore.m, 一个 Objective-C 版本的 Underscore.js, 实现了大部分函数, 它的语法鼓励使用链式语法. (源码)

_.m, 另一个 Objective-C 版本, 这个版本与原始的 Underscore.js API 比较相近. (源码)

Underscore-perl, 一个Perl版本的Underscore, 实现了大部分功能, 主要针对于Perl的哈希表和数组. (源码)

Underscore.cfc, 一个 Coldfusion 版本的 Underscore.js, 实现了大部分函数. (源码)

Underscore.string, 一个Underscore的扩展, 添加了多个字符串操作的函数, 如: trim, startsWith, contains, capitalize, reverse, sprintf, 还有更多.

Ruby的 枚举 模块.

Prototype.js, 提供类似于Ruby枚举方式的JavaScript集合函数.

Oliver Steele的 Functional JavaScript, 包含全面的高阶函数支持以及字符串的匿名函数.

Michael Aufreiter的 Data.js, 一个JavaScript的数据操作和持久化的类库.

Python的 迭代工具(itertools).

更新日志

1.5.22013年9月7日Diff

1.5.12013年7月8日Diff

1.5.02013年7月6日Diff

1.4.42013年1月30日Diff

1.4.32012年12月4Diff

1.4.22012年10月1日比较文件

1.4.12012年10月1日比较文件

1.4.02012年9月27日比较文件

1.3.32012年4月10日

1.3.12012年1月23日

1.3.02012年1月11日

1.2.42012年1月4日

1.2.32011年12月7日

1.2.22011年11月14日

1.2.12011年10月24日

1.2.02011年10月5日

1.1.72011年7月13日
增加 _.groupBy, 它可以将一个集合里的元素进行分组. 增加 _.union_.difference, 用来补充 (重命名过的) _.intersection 函数. 多方面的改进以支持稀疏数组. _.toArray 现在如果直接传数组时, 将会返回此数组的副本. _.functions 现在会返回存在于原型链中的函数名.

1.1.62011年4月18日
增加 _.after 函数, 被它改造过的函数只有在执行指定次数之后才会生效. _.invoke 现在将使用函数的直接引用. _.every 现在必须传如迭代器函数, 为了符合ECMAScript 5标准. _.extend 当值为undefined的时候不再复制键值. _.bind 现在如果试图绑定一个undefined值的时候将报错.

1.1.52011年3月20日
增加 _.defaults 函数, 用来合并JavaScript对象, 一般用来做生成默认值使用. 增加 _.once 函数, 用来把函数改造成只能运行一次的函数. _.bind 函数现在委托原生的ECMAScript 5版本(如可用). _.keys 现在传非对象的值时, 将会抛出一个错误, 就和ECMAScript 5标准里的一样. 修复了 _.keys 函数在传入稀疏数组时的bug.

1.1.42011年1月9日
改进所有数组函数当传值 null 时候的行为, 以符合ECMAScript 5标准. _.wrap 函数现在能正确地 给封装的函数设置 this 关键字了. _.indexOf 函数增加了可选参数isSorted, 寻找索引的时候会将数组作为已排序处理, 将使用更快的二分搜索. 避免使用 .callee, 保证 _.isArray 函数 在ECMAScript 5严格模式下能正常使用.

1.1.32010年12月1日
在CommonJS里, Underscore可以像这样引入:
var _ = require("underscore"). 增加 _.throttle_.debounce 函数. 移除 _.breakLoop 函数, 为了符合ECMAScript 5标准里所说的每一种实现形式都是不能break的 — 这将去掉try/catch块, 现在, 您遇到Underscore迭代器的抛出的异常时, 将会有更完善的堆栈跟踪来检查错误所在之处. 改进 isType 一类函数, 以便更好地兼容Internet Explorer浏览器. _.template 函数现在可以正确的反转义模板中的反斜杠了. 改进 _.reduce 函数以兼容ECMAScript 5标准: 如果您不传初始值, 将使用集合里的第一项作为初始值. _.each 不再返回迭代后的集合, 为了与ECMAScript 5的 forEach 保持一致.

1.1.2
修复 _.contains 指向 _.intersect 函数的错误, 应该是指向 _.include 函数(_.cotains应该是_.include的别名), 增加 _.unique, 作为 _.uniq 函数的别名.

1.1.1
改进 _.template 函数的运行速度, 和处理多行插入值的性能. Ryan Tenney 提供了许多Underscore函数的优化方案. 增加了带注释版本的源代码.

1.1.0
修改了 _.reduce 函数以符合ECMAScript 5规范, 取代了之前Ruby/Prototype.js版本的 _.reduce. 这是一个不向下兼容的修改. _.template 函数现在可以不传参了, 并保留空格. _.contains 是一个 _.include 函数新的别名.

1.0.4
Andri Möll 提供了 _.memoize函数, 以缓存计算结果, 来优化的耗时较长的函数, 使得运行速度变快.

1.0.3
修复了 _.isEqual 函数在对比包含 NaN 的对象时返回 false 的问题. 技术上改良后理论上是正确的, 但是语义上似乎有矛盾, 所以要注意避免对比含有NaN的对象.

1.0.2
修复 _.isArguments 在新版本Opera浏览器里的bug, Opera里会把arguments对象当作数组.

1.0.1
修复了 _.isEqual 函数的bug: 这个bug出现在当对比特定因素两个对象时, 这两个对象有着相同个数的值为undefined的key, 但不同名.

1.0.0
Underscore在这几个月里算是相对稳定了, 所以现在打算出测试版, 版本号为1.0. 从0.6版本开始进行改进, 包括_.isBoolean的改进, 和_.extend允许传多个source对象.

0.6.0
主要版本, 整合了一系列的功能函数, 包括 Mile Frawley写的在保留援用功能的基础上, 对集合函数进行重构, 内部代码更加简洁. 新的 _.mixin 函数, 允许您自己的功能函数继承Underscore对象. 增加 _.times 函数, 跟Ruby或Prototype.js里的times的功能一样. 对ECMAScript 5的 Array.isArray函数提供原生支持, 还有Object.keys.

0.5.8
修复了Underscore的集合函数, 以便可以用于DOM的 节点列表(NodeList)HTML集合(HTMLCollection) 再一次地感谢 Justin Tulloss.

0.5.7
修改 _.isArguments 函数, 使用了更安全的实现方式, 还有 加快了 _.isNumber 的运行速度,
感谢 Jed Schmidt.

0.5.6
增加了 _.template 对自定义分隔符的支持, 由 Noah Sloan提供.

0.5.5
修复了一个在移动版Safari里关于arguments对象的面向对象封装的bug.

0.5.4
修复了_.template函数里多个单引号在模板里造成的错误. 了解更多请阅读: Rick Strahl的博客文章.

0.5.2
几个函数的重写: isArray, isDate, isFunction, isNumber, isRegExp, 和 isString, 感谢Robert Kieffer提供的建议. 取代了 Object#toString 的对比方式, 现在以属性来进行对比, 虽然说安全性有所降低, 但是速度比以前快了有一个数量级. 因此其他大多数的Underscore函数也有小幅度的速度提升. 增加了 _.tap 函数, 由Evgeniy Dolzhenko 提供, 与Ruby 1.9的tap方法相似, 对链式语法里嵌入其他功能(如登录)很方便.

0.5.1
增加了 _.isArguments 函数. 许多小的安全检查和优化由 Noah SloanAndri Möll提供.

0.5.0
[API变更] _.bindAll 现在会将context对象作为第一个参数. 如果不传方法名, context对象的所有方法都会绑定到context, 支持链式语法和简易绑定. _.functions 现在只要一个参数, 然后返回所有的方法名(类型为Function的属性). 调用 _.functions(_) 会列出所有的Underscore函数. 增加 _.isRegExp 函数, isEqual 现在也可以检测两个RegExp对象是否相等了. 所有以"is"开头的函数已经缩减到同一个定义里面, 由Karl Guertin 提供的解决方案.

0.4.7
增加 isDate, isNaN, 和 isNull. 优化 isEqual 函数对比两个数组或两个时间对象时的性能. 优化了 _.keys 函数, 现在的运行速度比以前加快了25%–2倍 (取决于您所使用的浏览器)会加速其所依赖的函数, 如 _.each.

0.4.6
增加 range 函数, Python里同名函数range 的移植版, 用于生成灵活的整型数组. 原始版由Kirill Ishanov提供.

0.4.5
增加 rest 函数, 可以对数组和arguments对象使用, 增加了两个函数的别名, first 的别名为 head, 还有 rest 的别名为 tail, 感谢 Luke Sutton的解决方案. 增加测试文件, 以确保所有Underscore的数组函数都可以在用在 arguments 对象上.

0.4.4
增加 isString, 和 isNumber 函数. 修复了 _.isEqual(NaN, NaN) 会返回 true 的问题.

0.4.3
开始使用原生的 StopIteration 浏览器对象(如果浏览器支持). 修复Underscore在CommonJS环境上的安装.

0.4.2
把解除封装的函数unwrapping改名为value, 更清晰.

0.4.1
链式语法封装的Underscore对象支持函数原型方法的调用, 您可以在封装的数组上连续调用任意函数. 增加 breakLoop 方法, 可以随时在Underscore的迭代中 中断 并跳出迭代. 增加 isEmpty 函数, 在数组和对象上都有用.

0.4.0
现在所有的Underscore函数都可以用面向对象的风格来调用了, 比如: _([1, 2, 3]).map(...);. Marc-André Cournoyer 提供了原始的解决方案. 封装对象可以用链式语法连续调用函数. 添加了 functions 方法, 能以正序方式列出所有的Underscore函数.

0.3.3
增加JavaScript 1.8的函数 reduceRight. 别名为 foldr, 另外 reduce 的别名为 foldl.

0.3.2
可以在 Rhino 上运行了. 只要在编译器里输入: load("underscore.js"). 增加功能函数 identity.

0.3.1
所有迭代器在原始集合里现在都作为第三个参数传入, 和JavaScript 1.6的 forEach 一致. 迭代一个对象现在会以 (value, key, collection) 来调用, 更多详情, 请查看 _.each.

0.3.0
增加 Dmitry Baranovskiy的 综合优化, 合并 Kris Kowal的解决方案让Underscore符合 CommonJS 标准,并和 Narwhal 兼容.

0.2.0
添加 composelastIndexOf, 重命名 injectreduce, 添加 inject, filter, every, some, 和 forEach 的别名.

0.1.1
添加 noConflict, 以便 "Underscore" 对象可以分配给其他变量.

0.1.0
Underscore.js 首次发布.

A DocumentCloud Project