diff --git a/test/arrays.js b/test/arrays.js index dd54c7266..d069d9d62 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -4,6 +4,7 @@ $(document).ready(function() { test("arrays: first", function() { equals(_.first([1,2,3]), 1, 'can pull out the first element of an array'); + equals(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); }); test("arrays: last", function() { @@ -35,6 +36,7 @@ $(document).ready(function() { test("arrays: intersect", function() { var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; equals(_.intersect(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); + equals(_(stooges).intersect(leaders).join(''), 'moe', 'can perform an OO-style intersection'); }); test('arrays: zip', function() { diff --git a/test/collections.js b/test/collections.js index a55d02e88..1a6f5992d 100644 --- a/test/collections.js +++ b/test/collections.js @@ -32,7 +32,7 @@ $(document).ready(function() { answer = null; _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); - ok(answer, 'can reference the original collection from inside the iterator'); + ok(answer, 'can reference the original collection from inside the iterator'); }); test('collections: map', function() { @@ -41,6 +41,9 @@ $(document).ready(function() { var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); equals(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); + + var doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); + equals(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); }); test('collections: reduce', function() { @@ -53,6 +56,9 @@ $(document).ready(function() { sum = _.inject([1, 2, 3], 0, function(sum, num){ return sum + num; }); equals(sum, 6, 'aliased as "inject"'); + + sum = _([1, 2, 3]).reduce(0, function(sum, num){ return sum + num; }); + equals(sum, 6, 'OO-style reduce'); }); test('collections: reduceRight', function() { @@ -100,6 +106,7 @@ $(document).ready(function() { ok(_.include([1,2,3], 2), 'two is in the array'); ok(!_.include([1,3,9], 2), 'two is not in the array'); ok(_.include({moe:1, larry:3, curly:9}, 3), '_.include on objects checks their values'); + ok(_([1,2,3]).include(2), 'OO-style include'); }); test('collections: invoke', function() { diff --git a/test/functions.js b/test/functions.js index 9e401e4c7..80c5cb963 100644 --- a/test/functions.js +++ b/test/functions.js @@ -8,12 +8,15 @@ $(document).ready(function() { var bound = _.bind(func, context); equals(bound(), 'name: moe', 'can bind a function to a context'); - var func = function(salutation, name) { return salutation + ': ' + name; }; + bound = _(func).bind(context); + equals(bound(), 'name: moe', 'can do OO-style binding'); + + func = function(salutation, name) { return salutation + ': ' + name; }; func = _.bind(func, this, 'hello'); equals(func('moe'), 'hello: moe', 'the function was partially applied in advance'); - func = _.bind(func, this, 'curly'); - equals(func(), 'hello: curly', 'the function was completely applied in advance'); + var func = _.bind(func, this, 'curly'); + equals(func(), 'hello: curly', 'the function was completely applied in advance'); }); test("functions: bindAll", function() { diff --git a/test/objects.js b/test/objects.js index eb73245d8..6a20e8d9a 100644 --- a/test/objects.js +++ b/test/objects.js @@ -33,6 +33,7 @@ $(document).ready(function() { var clone = {name : 'moe', lucky : [13, 27, 34]}; ok(moe != clone, 'basic equality between objects is false'); ok(_.isEqual(moe, clone), 'deep equality is true'); + ok(_(moe).isEqual(clone), 'OO-style deep equality works'); }); test("objects: isElement", function() { diff --git a/test/speed.js b/test/speed.js index 5258fb58f..ce4f878d6 100644 --- a/test/speed.js +++ b/test/speed.js @@ -11,6 +11,12 @@ return timesTwo; }); + JSLitmus.test('_(list).each()', function() { + var timesTwo = []; + _(numbers).each(function(num){ timesTwo.push(num * 2); }); + return timesTwo; + }); + JSLitmus.test('_.map()', function() { return _.map(objects, function(obj){ return obj.num; }); }); diff --git a/underscore.js b/underscore.js index e198f6a4b..8f045b9ab 100644 --- a/underscore.js +++ b/underscore.js @@ -16,15 +16,20 @@ // Save the previous value of the "_" variable. var previousUnderscore = root._; - // Create a safe reference to the Underscore object for the functions below. - var _ = root._ = {}; + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. + var wrapper = function(obj) { this.wrapped = obj; }; + + // Create a safe reference to the Underscore object for reference below. + var _ = root._ = function(obj) { return new wrapper(obj); }; // Export the Underscore object for CommonJS. if (typeof exports !== 'undefined') _ = exports; // Current version. - _.VERSION = '0.3.3'; - + _.VERSION = '0.3.3'; + /*------------------------ Collection Functions: ---------------------------*/ // The cornerstone, an each implementation. @@ -447,6 +452,13 @@ return prefix ? prefix + id : id; }; + // Return a sorted list of the function names available in Underscore. + _.functions = function() { + var functions = []; + for (var key in _) if (Object.prototype.hasOwnProperty.call(_, key)) functions.push(key); + return _.without(functions, 'VERSION', 'prototype', 'noConflict'); + }; + // JavaScript templating a-la ERB, pilfered from John Resig's // "Secrets of the JavaScript Ninja", page 83. _.template = function(str, data) { @@ -472,6 +484,16 @@ _.foldr = _.reduceRight; _.filter = _.select; _.every = _.all; - _.some = _.any; + _.some = _.any; + _.methods = _.functions; + + /*------------------- Add all Functions to the Wrapper: --------------------*/ + + _.each(_.functions(), function(name) { + wrapper.prototype[name] = function() { + Array.prototype.unshift.call(arguments, this.wrapped); + return _[name].apply(_, arguments); + }; + }); })();