Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: jashkenas/underscore
base: 1.1.5
...
head fork: jashkenas/underscore
compare: 1.1.6
  • 19 commits
  • 8 files changed
  • 19 commit comments
  • 5 contributors
Commits on Mar 22, 2011
Jeremy Ashkenas changing order of operations to force an errror when _.bind -ing null…
… or undefined values.
2498bcc
Jeremy Ashkenas we're reserving nativeBind ... we should be actually using it. 6d4b767
Commits on Mar 25, 2011
Jeremy Ashkenas Documenting zip as transpose. Issue #161 a9ac8b1
Aseem Kishore aseemk Add test case for invoke w/ function reference. de50fc4
Aseem Kishore aseemk Add support for _.invoke() to take function reference. 3f512c2
Commits on Apr 05, 2011
Samuel Clay samuelclay Adding _.count to count truthy values in an iterator. _.count([1, 2, …
…3, 4, 5, 6], function(num){ return num % 2 == 0; }) = 3
c8e3c04
Samuel Clay samuelclay Speeding up a few methods which assign a default identity function fo…
…r missing/optional iterators. Noticeable difference.
5457522
Commits on Apr 06, 2011
Samuel Clay samuelclay Revert "Adding _.count to count truthy values in an iterator. _.count…
…([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }) = 3"

This reverts commit c8e3c04.

Conflicts:

	underscore.js
1fc7d4b
Commits on Apr 13, 2011
Sam Gentle sgentle _.barrier + tests 217a6a6
Commits on Apr 15, 2011
Jeremy Ashkenas from now on, to avoid confusion, underscore-min will only be in gh-pa…
…ges.
5e051fb
Jeremy Ashkenas Merge branch 'master' of github.com:documentcloud/underscore b6c39fe
Jeremy Ashkenas merging in barrier ... soon to be after c7b47ed
Jeremy Ashkenas _.barrier -> _.after, switch the order of arguments ... fix test form…
…atting.
8f67aa3
Jeremy Ashkenas tweaking invoke implementation 3df562f
Jeremy Ashkenas stricter ECMA5 compliance. _.every now requires an iterator to be pas…
…sed. #160
bf214d2
Jeremy Ashkenas Issue #123. _.extend shouldn't copy keys for undefined values. ea44179
Jeremy Ashkenas michaelficarra's proposed tweak to _.after 6f25cca
Jeremy Ashkenas removing obsolete _.after test. 13d4e62
Commits on Apr 18, 2011
Jeremy Ashkenas Underscore.js 1.1.6 b617615
106 docs/underscore.html
View
57 additions, 49 deletions not shown
37 index.html
View
@@ -118,11 +118,11 @@
<table>
<tr>
- <td><a href="underscore.js">Development Version (1.1.5)</a></td>
- <td><i>28kb, Uncompressed with Comments</i></td>
+ <td><a href="underscore.js">Development Version (1.1.6)</a></td>
+ <td><i>27kb, Uncompressed with Comments</i></td>
</tr>
<tr>
- <td><a href="underscore-min.js">Production Version (1.1.5)</a></td>
+ <td><a href="underscore-min.js">Production Version (1.1.6)</a></td>
<td><i>3kb, Minified and Gzipped</i></td>
</tr>
</table>
@@ -160,7 +160,7 @@
<span class="methods"><a href="#bind">bind</a>, <a href="#bindAll">bindAll</a>,
<a href="#memoize">memoize</a>, <a href="#delay">delay</a>, <a href="#defer">defer</a>,
<a href="#throttle">throttle</a>, <a href="#debounce">debounce</a>,
- <a href="#once">once</a>, <a href="#wrap">wrap</a>, <a href="#compose">compose</a></span>
+ <a href="#once">once</a>, <a href="#after">after</a>, <a href="#wrap">wrap</a>, <a href="#compose">compose</a></span>
</p>
<p>
@@ -578,6 +578,8 @@ <h2 id="styles">Object-Oriented and Functional Styles</h2>
Merges together the values of each of the <b>arrays</b> with the
values at the corresponding position. Useful when you have separate
data sources that are coordinated through matching array indexes.
+ If you're working with a matrix of nested arrays, <b>zip.apply</b>
+ can transpose the matrix in a similar fashion.
</p>
<pre>
_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
@@ -755,6 +757,22 @@ <h2 id="styles">Object-Oriented and Functional Styles</h2>
// Application is only created once.
</pre>
+ <p id="after">
+ <b class="header">after</b><code>_.after(count, function)</code>
+ <br />
+ Creates a version of the function that will only be run after first
+ being called <b>count</b> times. Useful for grouping asynchronous responses,
+ where you want to be sure that all the async calls have finished, before
+ proceeding.
+ </p>
+ <pre>
+var renderNotes = _.after(notes.length, render);
+_.each(notes, function(note) {
+ note.asyncSave({success: renderNotes});
+});
+// renderNotes is run once, after all notes have saved.
+</pre>
+
<p id="wrap">
<b class="header">wrap</b><code>_.wrap(function, wrapper)</code>
<br />
@@ -1226,6 +1244,17 @@ <h2 id="duck_typing">Duck Typing</h2>
<h2 id="changelog">Change Log</h2>
<p>
+ <b class="header">1.1.6</b> &mdash; <small><i>April 18, 2011</i></small><br />
+ Added <tt>_.after</tt>, which will return a function that only runs after
+ first being called a specified number of times.
+ <tt>_.invoke</tt> can now take a direct function reference.
+ <tt>_.every</tt> now requires an iterator function to be passed, which
+ mirrors the ECMA5 API.
+ <tt>_.extend</tt> no longer copies keys when the value is undefined.
+ <tt>_.bind</tt> now errors when trying to bind an undefined value.
+ </p>
+
+ <p>
<b class="header">1.1.5</b> &mdash; <small><i>Mar 20, 2011</i></small><br />
Added an <tt>_.defaults</tt> function, for use merging together JS objects
representing default options.
2  package.json
View
@@ -8,5 +8,5 @@
"dependencies" : [],
"repository" : {"type": "git", "url": "git://github.com/documentcloud/underscore.git"},
"main" : "underscore.js",
- "version" : "1.1.5"
+ "version" : "1.1.6"
}
15 test/collections.js
View
@@ -123,12 +123,12 @@ $(document).ready(function() {
});
test('collections: all', function() {
- ok(_.all([]), 'the empty set');
- ok(_.all([true, true, true]), 'all true values');
- ok(!_.all([true, false, true]), 'one false value');
+ ok(_.all([], _.identity), 'the empty set');
+ ok(_.all([true, true, true], _.identity), 'all true values');
+ ok(!_.all([true, false, true], _.identity), 'one false value');
ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers');
ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number');
- ok(_.every([true, true, true]), 'aliased as "every"');
+ ok(_.every([true, true, true], _.identity), 'aliased as "every"');
});
test('collections: any', function() {
@@ -154,6 +154,13 @@ $(document).ready(function() {
equals(result[1].join(', '), '1, 2, 3', 'second array sorted');
});
+ test('collections: invoke w/ function reference', function() {
+ var list = [[5, 1, 7], [3, 2, 1]];
+ var result = _.invoke(list, Array.prototype.sort);
+ equals(result[0].join(', '), '1, 5, 7', 'first array sorted');
+ equals(result[1].join(', '), '1, 2, 3', 'second array sorted');
+ });
+
test('collections: pluck', function() {
var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];
equals(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects');
16 test/functions.js
View
@@ -21,7 +21,7 @@ $(document).ready(function() {
var func = _.bind(func, this, 'curly');
equals(func(), 'hello: curly', 'the function was completely applied in advance');
- var func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname };
+ var func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; };
func = _.bind(func, this, 'hello', 'moe', 'curly');
equals(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
@@ -137,4 +137,18 @@ $(document).ready(function() {
equals(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
});
+ test("functions: after", function() {
+ var testAfter = function(afterAmount, timesCalled) {
+ var afterCalled = 0;
+ var after = _.after(afterAmount, function() {
+ afterCalled++;
+ });
+ while (timesCalled--) after();
+ return afterCalled;
+ };
+
+ equals(testAfter(5, 5), 1, "after(N) should fire after being called N times");
+ equals(testAfter(5, 4), 0, "after(N) should not fire unless called N times");
+ });
+
});
2  test/objects.js
View
@@ -33,6 +33,8 @@ $(document).ready(function() {
ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects');
result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'});
ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps');
+ result = _.extend({}, {a: void 0, b: null});
+ equals(_.keys(result).join(''), 'b', 'extend does not copy undefined values');
});
test("objects: defaults", function() {
26 underscore-min.js
View
@@ -1,26 +0,0 @@
-// Underscore.js 1.1.5
-// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
-// Underscore is freely distributable under the MIT license.
-// Portions of Underscore are inspired or borrowed from Prototype,
-// Oliver Steele's Functional, and John Resig's Micro-Templating.
-// For all details and documentation:
-// http://documentcloud.github.com/underscore
-(function(){var q=this,D=q._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,E=k.unshift,F=o.toString,m=o.hasOwnProperty,s=k.forEach,t=k.map,u=k.reduce,v=k.reduceRight,w=k.filter,x=k.every,y=k.some,p=k.indexOf,z=k.lastIndexOf;o=Array.isArray;var G=Object.keys,A=Function.prototype.bind,c=function(a){return new l(a)};if(typeof module!=="undefined"&&module.exports){module.exports=c;c._=c}else q._=c;c.VERSION="1.1.5";var j=c.each=c.forEach=function(a,b,d){if(a!=null)if(s&&a.forEach===s)a.forEach(b,
-d);else if(c.isNumber(a.length))for(var e=0,f=a.length;e<f;e++){if(b.call(d,a[e],e,a)===n)break}else for(e in a)if(m.call(a,e))if(b.call(d,a[e],e,a)===n)break};c.map=function(a,b,d){var e=[];if(a==null)return e;if(t&&a.map===t)return a.map(b,d);j(a,function(f,g,h){e[e.length]=b.call(d,f,g,h)});return e};c.reduce=c.foldl=c.inject=function(a,b,d,e){var f=d!==void 0;if(a==null)a=[];if(u&&a.reduce===u){if(e)b=c.bind(b,e);return f?a.reduce(b,d):a.reduce(b)}j(a,function(g,h,H){if(!f&&h===0){d=g;f=true}else d=
-b.call(e,d,g,h,H)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};c.reduceRight=c.foldr=function(a,b,d,e){if(a==null)a=[];if(v&&a.reduceRight===v){if(e)b=c.bind(b,e);return d!==void 0?a.reduceRight(b,d):a.reduceRight(b)}a=(c.isArray(a)?a.slice():c.toArray(a)).reverse();return c.reduce(a,b,d,e)};c.find=c.detect=function(a,b,d){var e;B(a,function(f,g,h){if(b.call(d,f,g,h)){e=f;return true}});return e};c.filter=c.select=function(a,b,d){var e=[];if(a==null)return e;
-if(w&&a.filter===w)return a.filter(b,d);j(a,function(f,g,h){if(b.call(d,f,g,h))e[e.length]=f});return e};c.reject=function(a,b,d){var e=[];if(a==null)return e;j(a,function(f,g,h){b.call(d,f,g,h)||(e[e.length]=f)});return e};c.every=c.all=function(a,b,d){b=b||c.identity;var e=true;if(a==null)return e;if(x&&a.every===x)return a.every(b,d);j(a,function(f,g,h){if(!(e=e&&b.call(d,f,g,h)))return n});return e};var B=c.some=c.any=function(a,b,d){b=b||c.identity;var e=false;if(a==null)return e;if(y&&a.some===
-y)return a.some(b,d);j(a,function(f,g,h){if(e=b.call(d,f,g,h))return n});return e};c.include=c.contains=function(a,b){var d=false;if(a==null)return d;if(p&&a.indexOf===p)return a.indexOf(b)!=-1;B(a,function(e){if(d=e===b)return true});return d};c.invoke=function(a,b){var d=i.call(arguments,2);return c.map(a,function(e){return(b?e[b]:e).apply(e,d)})};c.pluck=function(a,b){return c.map(a,function(d){return d[b]})};c.max=function(a,b,d){if(!b&&c.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};
-j(a,function(f,g,h){g=b?b.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};c.min=function(a,b,d){if(!b&&c.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};j(a,function(f,g,h){g=b?b.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};c.sortBy=function(a,b,d){return c.pluck(c.map(a,function(e,f,g){return{value:e,criteria:b.call(d,e,f,g)}}).sort(function(e,f){var g=e.criteria,h=f.criteria;return g<h?-1:g>h?1:0}),"value")};c.sortedIndex=
-function(a,b,d){d=d||c.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(b)?e=g+1:f=g}return e};c.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(c.isArray(a))return a;if(c.isArguments(a))return i.call(a);return c.values(a)};c.size=function(a){return c.toArray(a).length};c.first=c.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};c.rest=c.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};c.last=function(a){return a[a.length-1]};c.compact=function(a){return c.filter(a,
-function(b){return!!b})};c.flatten=function(a){return c.reduce(a,function(b,d){if(c.isArray(d))return b.concat(c.flatten(d));b[b.length]=d;return b},[])};c.without=function(a){var b=i.call(arguments,1);return c.filter(a,function(d){return!c.include(b,d)})};c.uniq=c.unique=function(a,b){return c.reduce(a,function(d,e,f){if(0==f||(b===true?c.last(d)!=e:!c.include(d,e)))d[d.length]=e;return d},[])};c.intersect=function(a){var b=i.call(arguments,1);return c.filter(c.uniq(a),function(d){return c.every(b,
-function(e){return c.indexOf(e,d)>=0})})};c.zip=function(){for(var a=i.call(arguments),b=c.max(c.pluck(a,"length")),d=Array(b),e=0;e<b;e++)d[e]=c.pluck(a,""+e);return d};c.indexOf=function(a,b,d){if(a==null)return-1;var e;if(d){d=c.sortedIndex(a,b);return a[d]===b?d:-1}if(p&&a.indexOf===p)return a.indexOf(b);d=0;for(e=a.length;d<e;d++)if(a[d]===b)return d;return-1};c.lastIndexOf=function(a,b){if(a==null)return-1;if(z&&a.lastIndexOf===z)return a.lastIndexOf(b);for(var d=a.length;d--;)if(a[d]===b)return d;
-return-1};c.range=function(a,b,d){if(arguments.length<=1){b=a||0;a=0}d=arguments[2]||1;for(var e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a+=d}return g};c.bind=function(a,b){if(A&&a.bind===A)return a.bind.apply(a,i.call(arguments,1));var d=i.call(arguments,2);return function(){return a.apply(b,d.concat(i.call(arguments)))}};c.bindAll=function(a){var b=i.call(arguments,1);if(b.length==0)b=c.functions(a);j(b,function(d){a[d]=c.bind(a[d],a)});return a};c.memoize=function(a,b){var d=
-{};b=b||c.identity;return function(){var e=b.apply(this,arguments);return m.call(d,e)?d[e]:d[e]=a.apply(this,arguments)}};c.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};c.defer=function(a){return c.delay.apply(c,[a,1].concat(i.call(arguments,1)))};var C=function(a,b,d){var e;return function(){var f=this,g=arguments,h=function(){e=null;a.apply(f,g)};d&&clearTimeout(e);if(d||!e)e=setTimeout(h,b)}};c.throttle=function(a,b){return C(a,b,false)};c.debounce=
-function(a,b){return C(a,b,true)};c.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};c.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments));return b.apply(this,d)}};c.compose=function(){var a=i.call(arguments);return function(){for(var b=i.call(arguments),d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};c.keys=G||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)if(m.call(a,
-d))b[b.length]=d;return b};c.values=function(a){return c.map(a,c.identity)};c.functions=c.methods=function(a){return c.filter(c.keys(a),function(b){return c.isFunction(a[b])}).sort()};c.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};c.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)if(a[d]==null)a[d]=b[d]});return a};c.clone=function(a){return c.isArray(a)?a.slice():c.extend({},a)};c.tap=function(a,b){b(a);return a};c.isEqual=function(a,
-b){if(a===b)return true;var d=typeof a;if(d!=typeof b)return false;if(a==b)return true;if(!a&&b||a&&!b)return false;if(a._chain)a=a._wrapped;if(b._chain)b=b._wrapped;if(a.isEqual)return a.isEqual(b);if(c.isDate(a)&&c.isDate(b))return a.getTime()===b.getTime();if(c.isNaN(a)&&c.isNaN(b))return false;if(c.isRegExp(a)&&c.isRegExp(b))return a.source===b.source&&a.global===b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline;if(d!=="object")return false;if(a.length&&a.length!==b.length)return false;
-d=c.keys(a);var e=c.keys(b);if(d.length!=e.length)return false;for(var f in a)if(!(f in b)||!c.isEqual(a[f],b[f]))return false;return true};c.isEmpty=function(a){if(c.isArray(a)||c.isString(a))return a.length===0;for(var b in a)if(m.call(a,b))return false;return true};c.isElement=function(a){return!!(a&&a.nodeType==1)};c.isArray=o||function(a){return F.call(a)==="[object Array]"};c.isArguments=function(a){return!!(a&&m.call(a,"callee"))};c.isFunction=function(a){return!!(a&&a.constructor&&a.call&&
-a.apply)};c.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};c.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};c.isNaN=function(a){return a!==a};c.isBoolean=function(a){return a===true||a===false};c.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};c.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};c.isNull=function(a){return a===null};c.isUndefined=function(a){return a===void 0};c.noConflict=function(){q._=
-D;return this};c.identity=function(a){return a};c.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};c.mixin=function(a){j(c.functions(a),function(b){I(b,c[b]=a[b])})};var J=0;c.uniqueId=function(a){var b=J++;return a?a+b:b};c.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g};c.template=function(a,b){var d=c.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,
-function(e,f){return"',"+f.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(e,f){return"');"+f.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return b?d(b):d};var l=function(a){this._wrapped=a};c.prototype=l.prototype;var r=function(a,b){return b?c(a).chain():a},I=function(a,b){l.prototype[a]=function(){var d=i.call(arguments);E.call(d,this._wrapped);return r(b.apply(c,
-d),this._chain)}};c.mixin(c);j(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=k[a];l.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];l.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}})();
32 underscore.js
View
@@ -1,4 +1,4 @@
-// Underscore.js 1.1.5
+// Underscore.js 1.1.6
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
@@ -59,7 +59,7 @@
}
// Current version.
- _.VERSION = '1.1.5';
+ _.VERSION = '1.1.6';
// Collection Functions
// --------------------
@@ -168,7 +168,6 @@
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
- iterator = iterator || _.identity;
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
@@ -182,7 +181,7 @@
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
- iterator = iterator || _.identity;
+ iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
@@ -208,7 +207,7 @@
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
- return (method ? value[method] : value).apply(value, args);
+ return (method.call ? method || value : value[method]).apply(value, args);
});
};
@@ -255,7 +254,7 @@
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator) {
- iterator = iterator || _.identity;
+ iterator || (iterator = _.identity);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
@@ -408,8 +407,9 @@
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
+ // We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function(func, obj) {
- if (nativeBind && func.bind === nativeBind) return func.bind.apply(func, slice.call(arguments, 1));
+ if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
var args = slice.call(arguments, 2);
return function() {
return func.apply(obj, args.concat(slice.call(arguments)));
@@ -428,7 +428,7 @@
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
- hasher = hasher || _.identity;
+ hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
@@ -509,6 +509,14 @@
};
};
+ // Returns a function that will only be executed after being called N times.
+ _.after = function(times, func) {
+ return function() {
+ if (--times < 1) { return func.apply(this, arguments); }
+ };
+ };
+
+
// Object Functions
// ----------------
@@ -535,7 +543,9 @@
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
- for (var prop in source) obj[prop] = source[prop];
+ for (var prop in source) {
+ if (source[prop] !== void 0) obj[prop] = source[prop];
+ }
});
return obj;
};
@@ -543,7 +553,9 @@
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
- for (var prop in source) if (obj[prop] == null) obj[prop] = source[prop];
+ for (var prop in source) {
+ if (obj[prop] == null) obj[prop] = source[prop];
+ }
});
return obj;
};

Showing you all comments on commits in this comparison.

Michael Ficarra
Collaborator

If func is null, won't this throw a TypeError anyway? Maybe it should be if(!func) return; if(nativeBind && func.bind === nativeBind) ....

Jeremy Ashkenas
Owner

Yes, it's supposed to throw an error early. Before it was suppressed when nativeBind didn't exist.

Michael Ficarra
Collaborator

Ah, I see. Okay. Is that the error that you would like to throw, though? "Cannot read property 'bind' of null" is not the best error message.

Jeremy Ashkenas
Owner

Yes, this is JavaScript -- you don't statically type all your arguments with expected types.

Satoshi Murakami

Made sense if func.bind.apply were nativeBind.apply.

Jeremy Ashkenas
Owner

Thanks for catching that, satyr. Fixed at 6d4b767.

Michael Ficarra
Collaborator

Really?

_.count = function(obj, iterator, context) { return _.filter(obj, iterator, context).length; };

There's no way this is deserving of its own function. Is it that much less expensive?

Jeremy Ashkenas
Owner

@samuelclay ... FYI. Want to defend this, or revert it?

Samuel Clay

In JSPerf, _.count is 5% faster (467,428 vs. 445,403). But it's only a convenience method. One creates an array of values, and the other counts them. At thousands of items in an array, the memory footprint will be favorable to the counter. I'm not opposed to a revert if you don't like the smell of the method.

Vladimir Dronnikov

by sense it's exactly a _.reduce

Michael Ficarra
Collaborator

Yep.

_.count = function(obj, iterator, context){
  return _.reduce(obj, function(memo, val){
    return memo + iterator.call(context, val) ? 1 : 0;
  }, 0);
};

And that implementation might actually be faster, too.

Vladimir Dronnikov

Shouldn't times be checked on being a natural number?

Jeremy Ashkenas
Owner

No. JavaScript isn't statically typed.

Vladimir Dronnikov

I meant simply (times > 0). Just if initially times < 0 this means the function still be called when times wraps around. Or there's no wrap-arounds in JS? (Sorry for ignorance, if so)

Samuel Clay

Yeah, you're going to want to call _.barrier with a positive integer, otherwise the func will never get called. And if you want to have the barrier run after it has already run, you'll have to setup a new barrier.

Michael Ficarra
Collaborator

I would expect _.after(0,... to work the same as _.after(1,.... Currently, this won't be the case. Changing it to --times < 1 will fix this.

Jeremy Ashkenas
Owner

Nice tweak -- patched at 6f25cca.

Michael Ficarra
Collaborator

Well, there's also consequences. This will invoke the given function every time after the desired number of calls, not just when that number is first hit. I think it's better the way it is now, but its behaviour should be explicit in the documentation (which I don't see...).

edit: Though, I see the modified test case, so you probably understood the consequences and agree it's better now.

Jeremy Ashkenas
Owner

Yes, this is an improvement -- and will be documented when released.

Something went wrong with that request. Please try again.