From 11cd924ce133049d54b984a027e1a03861014f8f Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 9 Dec 2012 21:58:31 -0800 Subject: [PATCH] Rework the chaining behavior of `Array` wrapper methods. Former-commit-id: fb8add58a861a19a2df63d6ff377c2a9537a38b6 --- build.js | 92 ++++++++++++++++++++++++++++++++------------ lodash.js | 45 ++++++++++++++-------- lodash.min.js | 4 +- lodash.underscore.js | 23 ++++++----- test/underscore.html | 23 ++++++++--- 5 files changed, 127 insertions(+), 60 deletions(-) diff --git a/build.js b/build.js index 59c515b65..dd877e1d1 100755 --- a/build.js +++ b/build.js @@ -328,25 +328,6 @@ ].join('\n'); }); - // add `__chain__` checks to `_.mixin` and `Array` function wrappers - _.each([ - matchFunction(source, 'mixin'), - /(?:\s*\/\/.*)*\n( *)forEach\(\['[\s\S]+?\n\1}.+/g - ], function(pattern) { - source = source.replace(pattern, function(match) { - return match.replace(/( *)return new lodash\(([^)]+)\).+/, function(submatch, indent, varName) { - return indent + [ - 'if (this.__chain__) {', - ' varName = new lodash(varName);', - ' varName.__chain__ = true;', - '}', - 'return varName;' - ].join('\n' + indent) - .replace(/varName/g, varName); - }); - }); - }); - // add `lodash.chain` assignment source = source.replace(getMethodAssignments(source), function(match) { return match.replace(/^(?: *\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/\n)?( *)lodash\.VERSION *=/m, '$1lodash.chain = chain;\n\n$&'); @@ -372,6 +353,56 @@ ].join('\n'); }); + // add `__chain__` checks to `_.mixin` + source = source.replace(matchFunction(source, 'mixin'), function(match) { + return match.replace(/^( *)return new lodash.+/m, function() { + var indent = arguments[1]; + return indent + [ + 'if (this.__chain__) {', + ' result = new lodash(result);', + ' result.__chain__ = true;', + '}', + 'return result;' + ].join('\n' + indent); + }); + }); + + // replace wrapper `Array` method assignments + source = source.replace(/^(?: *\/\/.*\n)*( *)forEach\(\['[\s\S]+?\n\1}$/m, function() { + return [ + ' // add `Array` mutator functions to the wrapper', + " forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {", + ' var func = arrayRef[methodName];', + ' lodash.prototype[methodName] = function() {', + ' var value = this.__wrapped__;', + ' func.apply(value, arguments);', + '', + ' // avoid array-like object bugs with `Array#shift` and `Array#splice`', + ' // in Firefox < 10 and IE < 9', + ' if (hasObjectSpliceBug && value.length === 0) {', + ' delete value[0];', + ' }', + ' return this;', + ' };', + ' });', + '', + ' // add `Array` accessor functions to the wrapper', + " forEach(['concat', 'join', 'slice'], function(methodName) {", + ' var func = arrayRef[methodName];', + ' lodash.prototype[methodName] = function() {', + ' var value = this.__wrapped__,', + ' result = func.apply(value, arguments);', + '', + ' if (this.__chain__) {', + ' result = new lodash(result);', + ' result.__chain__ = true;', + ' }', + ' return result;', + ' };', + ' });' + ].join('\n'); + }); + return source; } @@ -920,6 +951,19 @@ return source; } + /** + * Removes all `hasObjectSpliceByg` references from `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function removeHasObjectSpliceBug(source) { + return removeVar(source, 'hasObjectSpliceBug') + // remove `hasObjectSpliceBug` fix from the `Array` function mixins + .replace(/(?:\s*\/\/.*)*\n( *)if *\(hasObjectSpliceBug[\s\S]+?(?:{\s*}|\n\1})/, ''); + } + /** * Removes a given variable from `source`. * @@ -1762,9 +1806,7 @@ } else { source = removeIsArgumentsFallback(source); - - // remove `hasObjectSpliceBug` fix from the mutator Array functions mixin - source = source.replace(/(?:\s*\/\/.*)*\n( *)if *\(hasObjectSpliceBug[\s\S]+?\n\1}/, ''); + source = removeHasObjectSpliceBug(source); } // remove `thisArg` from unexposed `forIn` and `forOwn` @@ -1908,7 +1950,7 @@ source = removeIsFunctionFallback(source); } if (isRemoved(source, 'mixin')) { - source = removeVar(source, 'hasObjectSpliceBug'); + source = removeHasObjectSpliceBug(source); // simplify the `lodash` function source = replaceFunction(source, 'lodash', [ @@ -1921,8 +1963,8 @@ source = source .replace(/(?:\s*\/\/.*)*\n( *)forOwn\(lodash, *function\(func, *methodName\)[\s\S]+?\n\1}.+/g, '') .replace(/(?:\s*\/\/.*)*\n( *)forEach\(\['[\s\S]+?\n\1}.+/g, '') - .replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+\n/, '') - .replace(/(?:\s*\/\/.*)*\s*lodash\.prototype.+\n/, ''); + .replace(/(?:\s*\/\/.*)*\s*lodash\.prototype.+\n/g, '') + .replace(/(?:\s*\/\/.*)*\s*mixin\(lodash\).+\n/, ''); } // assign debug source before further modifications that rely on the minifier diff --git a/lodash.js b/lodash.js index 0143819ae..a58208a75 100644 --- a/lodash.js +++ b/lodash.js @@ -4159,7 +4159,6 @@ /*--------------------------------------------------------------------------*/ // add functions that return wrapped values when chaining - lodash.after = after; lodash.assign = assign; lodash.bind = bind; @@ -4323,33 +4322,49 @@ lodash.prototype.value = wrapperValueOf; lodash.prototype.valueOf = wrapperValueOf; - // add mutator `Array` functions to the wrapper - forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { + // add `Array` functions that return unwrapped values + forEach(['join', 'pop', 'shift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { - var value = this.__wrapped__; - func.apply(value, arguments); + return func.apply(this.__wrapped__, arguments); + }; + }); - // avoid array-like object bugs with `Array#shift` and `Array#splice` - // in Firefox < 10 and IE < 9 - if (hasObjectSpliceBug && value.length === 0) { - delete value[0]; - } + // add `Array` functions that return the wrapped value + forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) { + var func = arrayRef[methodName]; + lodash.prototype[methodName] = function() { + func.apply(this.__wrapped__, arguments); return this; }; }); - // add accessor `Array` functions to the wrapper - forEach(['concat', 'join', 'slice'], function(methodName) { + // add `Array` functions that return new wrapped values + forEach(['concat', 'slice', 'splice'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { - var value = this.__wrapped__, - result = func.apply(value, arguments); - + var result = func.apply(this.__wrapped__, arguments); return new lodash(result); }; }); + // avoid array-like object bugs with `Array#shift` and `Array#splice` + // in Firefox < 10 and IE < 9 + if (hasObjectSpliceBug) { + forEach(['shift', 'splice'], function(methodName) { + var func = lodash.prototype[methodName]; + lodash.prototype[methodName] = function() { + var value = this.__wrapped__, + result = func.apply(this, arguments); + + if (value.length === 0) { + delete value[0]; + } + return result; + }; + }); + } + // add pseudo private property to be used and removed during the build process lodash._iteratorTemplate = iteratorTemplate; diff --git a/lodash.min.js b/lodash.min.js index 7f68b32d8..18ee06b26 100644 --- a/lodash.min.js +++ b/lodash.min.js @@ -37,5 +37,5 @@ function(e){return kt.call(e)==Ut},s.isString=A,s.isUndefined=function(e){return {var t=e?e.length:0;return"number"==typeof t?t:bn(e).length},s.some=R,s.sortedIndex=V,s.template=function(e,t,n){e||(e=""),n||(n={});var r,i,u=s.templateSettings,a=0,f=n.interpolate||u.interpolate||mt,l="__p+='",c=n.variable||u.variable,h=c;e.replace(RegExp((n.escape||u.escape||mt).source+"|"+f.source+"|"+(f===vt?dt:mt).source+"|"+(n.evaluate||u.evaluate||mt).source+"|$","g"),function(t,n,i,s,o,u){return i||(i=s),l+=e.slice(a,u).replace(yt,p),n&&(l+="'+__e("+n+")+'"),o&&(l+="';"+o+";__p+='"),i&&( l+="'+((__t=("+i+"))==null?'':__t)+'"),r||(r=o||ot.test(n||i)),a=u+t.length,t}),l+="';\n",h||(c="obj",r?l="with("+c+"){"+l+"}":(n=RegExp("(\\(\\s*)"+c+"\\."+c+"\\b","g"),l=l.replace(ht,"$&"+c+".").replace(n,"$1__d"))),l=(r?l.replace(at,""):l).replace(ft,"$1").replace(lt,"$1;"),l="function("+c+"){"+(h?"":c+"||("+c+"={});")+"var __t,__p='',__e=_.escape"+(r?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":(h?"":",__d="+c+"."+c+"||"+c)+";")+l+"return __p}";try{i=o("_","return "+ l)(s)}catch(d){throw d.source=l,d}return t?i(t):(i.source=l,i)},s.unescape=function(e){return e==r?"":(e+"").replace(ut,y)},s.uniqueId=function(e){return(e==r?"":e+"")+ ++nt},s.all=D,s.any=R,s.detect=H,s.foldl=I,s.foldr=q,s.include=_,s.inject=I,pn(s,function(e,t){s.prototype[t]||(s.prototype[t]=function(){var t=[this.__wrapped__];return Nt.apply(t,arguments),e.apply(s,t)})}),s.first=U,s.last=function(e,t,n){if(e){var i=e.length;return t==r||n?e[i-1]:g(e,Dt(0,i-t))}},s.take=U,s.head=U,pn(s,function( -e,t){s.prototype[t]||(s.prototype[t]=function(t,n){var i=e(this.__wrapped__,t,n);return t==r||n?i:new s(i)})}),s.VERSION="1.0.0-rc.2",s.prototype.toString=function(){return""+this.__wrapped__},s.prototype.value=G,s.prototype.valueOf=G,wn("pop push reverse shift sort splice unshift".split(" "),function(e){var t=et[e];s.prototype[e]=function(){var e=this.__wrapped__;return t.apply(e,arguments),Gt&&e.length===0&&delete e[0],this}}),wn(["concat","join","slice"],function(e){var t=et[e];s.prototype[e]= -function(){var e=t.apply(this.__wrapped__,arguments);return new s(e)}}),typeof define=="function"&&typeof define.amd=="object"&&define.amd?(e._=s,define(function(){return s})):Y?"object"==typeof module&&module&&module.exports==Y?(module.exports=s)._=s:Y._=s:e._=s})(this); \ No newline at end of file +e,t){s.prototype[t]||(s.prototype[t]=function(t,n){var i=e(this.__wrapped__,t,n);return t==r||n?i:new s(i)})}),s.VERSION="1.0.0-rc.2",s.prototype.toString=function(){return""+this.__wrapped__},s.prototype.value=G,s.prototype.valueOf=G,wn(["join","pop","shift"],function(e){var t=et[e];s.prototype[e]=function(){return t.apply(this.__wrapped__,arguments)}}),wn(["push","reverse","sort","unshift"],function(e){var t=et[e];s.prototype[e]=function(){return t.apply(this.__wrapped__,arguments),this}}),wn(["concat" +,"slice","splice"],function(e){var t=et[e];s.prototype[e]=function(){var e=t.apply(this.__wrapped__,arguments);return new s(e)}}),Gt&&wn(["shift","splice"],function(e){var t=s.prototype[e];s.prototype[e]=function(){var e=this.__wrapped__,n=t.apply(this,arguments);return 0===e.length&&delete e[0],n}}),typeof define=="function"&&typeof define.amd=="object"&&define.amd?(e._=s,define(function(){return s})):Y?"object"==typeof module&&module&&module.exports==Y?(module.exports=s)._=s:Y._=s:e._=s})(this); \ No newline at end of file diff --git a/lodash.underscore.js b/lodash.underscore.js index c3e797023..5cf31e6fd 100644 --- a/lodash.underscore.js +++ b/lodash.underscore.js @@ -185,7 +185,7 @@ * Creates a `lodash` object, that wraps the given `value`, to enable * method chaining. * - * The wrapper functions capable of chaining are: + * The chainable wrapper functions are: * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, * `compose`, `countBy`, `debounce`, `defaults`, `defer`, `delay`, `difference`, * `filter`, `flatten`, `forEach`, `forIn`, `forOwn`, `functions`, `groupBy`, @@ -194,16 +194,16 @@ * `range`, `reject`, `rest`, `shuffle`, `sortBy`, `tap`, `throttle`, `times`, * `toArray`, `union`, `uniq`, `values`, `where`, `without`, `wrap`, and `zip` * - * The wrapper functions that do not chain are: - * `clone`, `contains`, `escape`, `every`, `find`, `has`, `identity`, - * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, - * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, - * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `lastIndexOf`, - * `mixin`, `noConflict`, `random`, `reduce`, `reduceRight`, `result`, `size`, - * `some`, `sortedIndex`, `template`, `unescape`, and `uniqueId` + * The non-chainable wrapper functions are: + * `clone`, `contains`, `escape`, `every`, `find`, `has`, `identity`, `indexOf`, + * `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, + * `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, `isObject`, + * `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `lastIndexOf`, `mixin`, + * `noConflict`, `random`, `reduce`, `reduceRight`, `result`, `size`, `some`, + * `sortedIndex`, `template`, `unescape`, and `uniqueId` * * The wrapper functions `first` and `last` return wrapped values when `n` is - * passed, otherwise unwrapped values are returned. + * passed, otherwise return unwrapped values. * * @name _ * @constructor @@ -3656,7 +3656,6 @@ /*--------------------------------------------------------------------------*/ // add functions that return wrapped values when chaining - lodash.after = after; lodash.bind = bind; lodash.bindAll = bindAll; @@ -3794,7 +3793,7 @@ lodash.prototype.chain = wrapperChain; lodash.prototype.value = wrapperValueOf; - // add mutator `Array` functions to the wrapper + // add `Array` mutator functions to the wrapper forEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { @@ -3810,7 +3809,7 @@ }; }); - // add accessor `Array` functions to the wrapper + // add `Array` accessor functions to the wrapper forEach(['concat', 'join', 'slice'], function(methodName) { var func = arrayRef[methodName]; lodash.prototype[methodName] = function() { diff --git a/test/underscore.html b/test/underscore.html index 610522bcc..62c954e4b 100644 --- a/test/underscore.html +++ b/test/underscore.html @@ -33,13 +33,19 @@ document.write('