From 11de155432645168883fb92e4253a9cd729edf98 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 4 Jan 2015 15:47:33 -0600 Subject: [PATCH] Chain methods by reference instead of property. --- lodash.js | 25 +++++++++++----------- test/test.js | 58 ++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/lodash.js b/lodash.js index d295cd8b5..7e19aa345 100644 --- a/lodash.js +++ b/lodash.js @@ -2697,11 +2697,10 @@ while (++index < length) { var args = [result], - action = actions[index], - object = action.object; + action = actions[index]; push.apply(args, action.args); - result = object[action.name].apply(object, args); + result = action.func.apply(action.thisArg, args); } return result; } @@ -10209,23 +10208,25 @@ chain = options.chain; } while (++index < length) { - var methodName = methodNames[index]; - object[methodName] = source[methodName]; + var methodName = methodNames[index], + func = source[methodName]; + + object[methodName] = func; if (isFunc) { - object.prototype[methodName] = (function(methodName) { + object.prototype[methodName] = (function(func) { return function() { var chainAll = this.__chain__; if (chain || chainAll) { var result = object(this.__wrapped__); - (result.__actions__ = baseSlice(this.__actions__)).push({ 'args': arguments, 'object': object, 'name': methodName }); + (result.__actions__ = baseSlice(this.__actions__)).push({ 'func': func, 'args': arguments, 'thisArg': object }); result.__chain__ = chainAll; return result; } var args = [this.value()]; push.apply(args, arguments); - return object[methodName].apply(object, args); + return func.apply(object, args); }; - }(methodName)); + }(func)); } } return object; @@ -10678,10 +10679,10 @@ lodash.prototype.sample = function(n) { if (!this.__chain__ && n == null) { - return lodash.sample(this.value()); + return sample(this.value()); } return this.thru(function(value) { - return lodash.sample(value, n); + return sample(value, n); }); }; @@ -10835,7 +10836,7 @@ if (!retUnwrapped && (isHybrid || result.actions)) { var actions = result.actions || (result.actions = []); - actions.push({ 'args': [interceptor], 'object': lodash, 'name': 'thru' }); + actions.push({ 'func': thru, 'args': [interceptor], 'thisArg': lodash }); } return new LodashWrapper(result, chainAll); } diff --git a/test/test.js b/test/test.js index 782e2234d..58e028524 100644 --- a/test/test.js +++ b/test/test.js @@ -294,11 +294,10 @@ while (++index < length) { var args = [result], - action = actions[index], - object = action.object; + action = actions[index]; push.apply(args, action.args); - result = object[action.name].apply(object, args); + result = action.func.apply(action.thisArg, args); } return result; } @@ -806,7 +805,7 @@ if (!isNpm && lodashBizarro) { var actual = _.map(values, function(value) { var wrapped = _(lodashBizarro(value)), - unwrapped = getUnwrappedValue(wrapped); + unwrapped = wrapped.value(); return wrapped instanceof _ && (unwrapped === value || (_.isNaN(unwrapped) && _.isNaN(value))); @@ -9056,15 +9055,15 @@ return getUnwrappedValue(this); }; - var value = ['a'], + var array = ['a'], source = { 'a': function(array) { return array[0]; }, 'b': 'B' }; test('should mixin `source` methods into lodash', 4, function() { if (!isNpm) { _.mixin(source); - strictEqual(_.a(value), 'a'); - strictEqual(getUnwrappedValue(_(value).a()), 'a'); + strictEqual(_.a(array), 'a'); + strictEqual(_(array).a().value(), 'a'); delete _.a; delete _.prototype.a; @@ -9080,11 +9079,27 @@ } }); + test('should mixin chaining methods by reference', 2, function() { + if (!isNpm) { + _.mixin(source); + _.a = _.constant('b'); + + strictEqual(_.a(array), 'b'); + strictEqual(_(array).a().value(), 'a'); + + delete _.a; + delete _.prototype.a; + } + else { + skipTest(2); + } + }); + test('should use `this` as the default `object` value', 3, function() { var object = _.create(_); object.mixin(source); - strictEqual(object.a(value), 'a'); + strictEqual(object.a(array), 'a'); ok(!('a' in _)); ok(!('a' in _.prototype)); @@ -9098,7 +9113,7 @@ test('should accept an `object` argument', 1, function() { var object = {}; _.mixin(object, source); - strictEqual(object.a(value), 'a'); + strictEqual(object.a(array), 'a'); }); test('should return `object`', 2, function() { @@ -9110,7 +9125,7 @@ test('should work with a function for `object`', 2, function() { _.mixin(Wrapper, source); - var wrapped = Wrapper(value), + var wrapped = Wrapper(array), actual = wrapped.a(); strictEqual(actual.value(), 'a'); @@ -9142,7 +9157,7 @@ } else { _.mixin(func, source, options); } - var wrapped = func(value), + var wrapped = func(array), actual = wrapped.a(); if (options === true || (options && options.chain)) { @@ -9222,12 +9237,10 @@ test('should produce methods that work in a lazy chain sequence', 1, function() { if (!isNpm) { - var array = [1, 2, 1, 3], - predicate = function(value) { return value > 1; }; - + var predicate = function(value) { return value > 1; }; _.mixin({ 'a': _.countBy, 'b': _.filter }); - var actual = _(array).a(_.identity).map(String).b(predicate).take().value(); + var actual = _([1, 2, 1, 3]).a(_.identity).map(String).b(predicate).take().value(); deepEqual(actual, ['2']); delete _.a; @@ -11284,6 +11297,21 @@ } }); + test('should use a stored reference to `_.sample` when chaining', 2, function() { + if (!isNpm) { + var sample = _.sample; + _.sample = _.noop; + + var wrapped = _(array); + notStrictEqual(wrapped.sample(), undefined); + notStrictEqual(wrapped.sample(2).value(), undefined); + _.sample = sample; + } + else { + skipTest(2); + } + }); + _.each({ 'literal': 'abc', 'object': Object('abc')