diff --git a/build.js b/build.js index f890a13e5..9c6d94852 100755 --- a/build.js +++ b/build.js @@ -28,11 +28,11 @@ var aliasToRealMap = { 'all': 'every', 'any': 'some', - 'assign': 'extend', 'collect': 'map', 'detect': 'find', 'drop': 'rest', 'each': 'forEach', + 'extend': 'assign', 'foldl': 'reduce', 'foldr': 'reduceRight', 'head': 'first', @@ -47,9 +47,9 @@ /** Used to associate real names with their aliases */ var realToAliasMap = { + 'assign': ['extend'], 'contains': ['include'], 'every': ['all'], - 'extend': ['assign'], 'filter': ['select'], 'find': ['detect'], 'first': ['head', 'take'], @@ -66,10 +66,11 @@ /** Used to track function dependencies */ var dependencyMap = { 'after': [], + 'assign': ['isArguments'], 'bind': ['isFunction', 'isObject'], 'bindAll': ['bind', 'functions'], 'chain': ['mixin'], - 'clone': ['extend', 'forEach', 'forOwn', 'isArguments', 'isObject', 'isPlainObject'], + 'clone': ['assign', 'forEach', 'forOwn', 'isArguments', 'isObject', 'isPlainObject'], 'compact': [], 'compose': [], 'contains': ['forEach', 'indexOf', 'isString'], @@ -81,7 +82,6 @@ 'difference': ['indexOf'], 'escape': [], 'every': ['forEach', 'isArray'], - 'extend': ['isArguments'], 'filter': ['forEach', 'isArray'], 'find': ['forEach'], 'first': [], @@ -231,7 +231,6 @@ /** List of methods used by Underscore */ var underscoreMethods = _.without.apply(_, [allMethods].concat([ - 'assign', 'forIn', 'forOwn', 'isPlainObject', @@ -346,7 +345,7 @@ ' lodash csp Build supporting default Content Security Policy restrictions', ' lodash legacy Build tailored for older browsers without ES5 support', ' lodash mobile Build with IE < 9 bug fixes & method compilation removed', - ' lodash strict Build with `_.bindAll`, `_.defaults`, & `_.extend` in strict mode', + ' lodash strict Build with `_.assign`, `_.bindAll`, & `_.defaults` in strict mode', ' lodash underscore Build tailored for projects already using Underscore', ' lodash include=... Comma separated method/category names to include in the build', ' lodash minus=... Comma separated method/category names to remove from those included in the build', @@ -892,7 +891,7 @@ // flag used to specify skipping status updates normally logged to the console var isSilent = isStdOut || options.indexOf('-s') > -1 || options.indexOf('--silent') > -1; - // flag used to specify `_.bindAll`, `_.extend`, and `_.defaults` are + // flag used to specify `_.assign`, `_.bindAll`, and `_.defaults` are // constructed using the "use strict" directive var isStrict = options.indexOf('strict') > -1; @@ -933,7 +932,7 @@ return match ? Function('return {' + match[1].replace(/^{|}$/g, '') + '}')() : result; - }, _.extend(_.clone(_.templateSettings), { + }, _.assign(_.clone(_.templateSettings), { 'moduleId': moduleId })); @@ -998,7 +997,7 @@ dependencyMap.template = ['defaults', 'escape']; if (useUnderscoreClone) { - dependencyMap.clone = ['extend', 'isArray']; + dependencyMap.clone = ['assign', 'isArray']; } } // add method names required by Backbone and Underscore builds @@ -1068,7 +1067,7 @@ source = source.replace(/^( *)function clone[\s\S]+?\n\1}/m, [ ' function clone(value) {', ' return value && objectTypes[typeof value]', - ' ? (isArray(value) ? slice.call(value) : extend({}, value))', + ' ? (isArray(value) ? slice.call(value) : assign({}, value))', ' : value', ' }' ].join('\n')); @@ -1108,9 +1107,9 @@ ' }' ].join('\n')); - // replace `_.extend` - source = source.replace(/^( *)var extend *= *createIterator[\s\S]+?\);/m, [ - ' function extend(object) {', + // replace `_.assign` + source = source.replace(/^( *)var assign *= *createIterator[\s\S]+?\);/m, [ + ' function assign(object) {', ' if (!object) {', ' return object;', ' }', @@ -1309,9 +1308,6 @@ } }); - if (!exposeAssign) { - source = removeFunction(source, 'assign'); - } // remove `isArguments` fallback before `isArguments` is transformed by // other parts of the build process if (isRemoved(source, 'isArguments')) { @@ -1379,11 +1375,14 @@ }); if (isUnderscore) { - // remove `_.forIn`, `_.forOwn`, and `_.isPlainObject` assignments + // remove `_.assign`, `_.forIn`, `_.forOwn`, and `_.isPlainObject` assignments (function() { var snippet = getMethodAssignments(source), modified = snippet; + if (!exposeAssign) { + modified = modified.replace(/(?:\n *\/\/.*\s*)* *lodash\.assign *= *.+\n/, ''); + } if (!exposeForIn) { modified = modified.replace(/(?:\n *\/\/.*\s*)* *lodash\.forIn *= *.+\n/, ''); } diff --git a/lodash.js b/lodash.js index 7a4341277..708c39db9 100644 --- a/lodash.js +++ b/lodash.js @@ -417,6 +417,16 @@ 'return result' ); + /** Reusable iterator options for `assign` and `defaults` */ + var assignIteratorOptions = { + 'args': 'object, source, guard', + 'top': + 'for (var argsIndex = 1, argsLength = typeof guard == \'number\' ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' + + ' if ((iteratee = arguments[argsIndex])) {', + 'objectLoop': 'result[index] = value', + 'bottom': ' }\n}' + }; + /** * Reusable iterator options shared by `forEach`, `forIn`, and `forOwn`. */ @@ -427,17 +437,6 @@ 'objectLoop': 'if (callback(value, index, collection) === false) return result' }; - /** Reusable iterator options for `defaults`, and `extend` */ - var extendIteratorOptions = { - 'useHas': false, - 'args': 'object, source, guard', - 'top': - 'for (var argsIndex = 1, argsLength = typeof guard == \'number\' ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' + - ' if ((iteratee = arguments[argsIndex])) {', - 'objectLoop': 'result[index] = value', - 'bottom': ' }\n}' - }; - /** Reusable iterator options for `forIn` and `forOwn` */ var forOwnIteratorOptions = { 'arrayLoop': null @@ -697,6 +696,25 @@ /*--------------------------------------------------------------------------*/ + /** + * Assigns own enumerable properties of source object(s) to the `destination` + * object. Subsequent sources will overwrite propery assignments of previous + * sources. + * + * @static + * @memberOf _ + * @alias extend + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. + * @example + * + * _.assign({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } + */ + var assign = createIterator(assignIteratorOptions); + /** * Checks if `value` is an `arguments` object. * @@ -756,7 +774,7 @@ }); /** - * Iterates over `object`'s own enumerable properties, executing the `callback` + * Iterates over an object's own enumerable properties, executing the `callback` * for each property. The `callback` is bound to `thisArg` and invoked with three * arguments; (value, key, object). Callbacks may exit iteration early by explicitly * returning `false`. @@ -915,7 +933,7 @@ if (!isObj || !deep) { // don't clone functions return isObj - ? (isArr ? slice.call(value) : extend({}, value)) + ? (isArr ? slice.call(value) : assign({}, value)) : value; } @@ -976,29 +994,11 @@ * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } */ - var defaults = createIterator(extendIteratorOptions, { - 'objectLoop': 'if (result[index] == null) ' + extendIteratorOptions.objectLoop + var defaults = createIterator(assignIteratorOptions, { + 'useHas': false, + 'objectLoop': 'if (result[index] == null) ' + assignIteratorOptions.objectLoop }); - /** - * Assigns enumerable properties of the source object(s) to the `destination` - * object. Subsequent sources will overwrite propery assignments of previous - * sources. - * - * @static - * @memberOf _ - * @alias assign - * @category Objects - * @param {Object} object The destination object. - * @param {Object} [source1, source2, ...] The source objects. - * @returns {Object} Returns the destination object. - * @example - * - * _.extend({ 'name': 'moe' }, { 'age': 40 }); - * // => { 'name': 'moe', 'age': 40 } - */ - var extend = createIterator(extendIteratorOptions); - /** * Creates a sorted array of all enumerable properties, own and inherited, * of `object` that have function values. @@ -4079,6 +4079,7 @@ lodash.VERSION = '0.9.2'; // assign static methods + lodash.assign = assign; lodash.after = after; lodash.bind = bind; lodash.bindAll = bindAll; @@ -4095,7 +4096,6 @@ lodash.difference = difference; lodash.escape = escape; lodash.every = every; - lodash.extend = extend; lodash.filter = filter; lodash.find = find; lodash.first = first; @@ -4177,11 +4177,11 @@ // assign aliases lodash.all = every; lodash.any = some; - lodash.assign = extend; lodash.collect = map; lodash.detect = find; lodash.drop = rest; lodash.each = forEach; + lodash.extend = assign; lodash.foldl = reduce; lodash.foldr = reduceRight; lodash.head = first; diff --git a/test/test-build.js b/test/test-build.js index f72bd3b69..0216667d1 100644 --- a/test/test-build.js +++ b/test/test-build.js @@ -18,11 +18,11 @@ var aliasToRealMap = { 'all': 'every', 'any': 'some', - 'assign': 'extend', 'collect': 'map', 'detect': 'find', 'drop': 'rest', 'each': 'forEach', + 'extend': 'assign', 'foldl': 'reduce', 'foldr': 'reduceRight', 'head': 'first', @@ -37,9 +37,9 @@ /** Used to associate real names with their aliases */ var realToAliasMap = { + 'assign': ['extend'], 'contains': ['include'], 'every': ['all'], - 'extend': ['assign'], 'filter': ['select'], 'find': ['detect'], 'first': ['head', 'take'], @@ -242,7 +242,6 @@ /** List of methods used by Underscore */ var underscoreMethods = _.without.apply(_, [allMethods].concat([ - 'assign', 'forIn', 'forOwn', 'isPlainObject', diff --git a/test/test.js b/test/test.js index e148ad02d..6c54fd70f 100644 --- a/test/test.js +++ b/test/test.js @@ -163,6 +163,42 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.assign'); + + (function() { + test('should not error on `null` or `undefined` sources (test in IE < 9)', function() { + try { + deepEqual(_.assign({}, null, undefined, { 'a': 1 }), { 'a': 1 }); + } catch(e) { + ok(false); + } + }); + + test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() { + function Foo() {} + Foo.prototype.c = 3; + + Foo.a = 1; + Foo.b = 2; + + var expected = { 'a': 1, 'b': 2 }; + deepEqual(_.assign({}, Foo), expected); + + Foo.prototype = { 'c': 3 }; + deepEqual(_.assign({}, Foo), expected); + }); + + test('should work with `_.reduce`', function() { + var actual = { 'a': 1}, + array = [{ 'b': 2 }, { 'c': 3 }]; + + _.reduce(array, _.assign, actual); + deepEqual(actual, { 'a': 1, 'b': 2, 'c': 3}); + }); + }()); + + /*--------------------------------------------------------------------------*/ + QUnit.module('lodash.bind'); (function() { @@ -385,45 +421,9 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.extend'); - - (function() { - test('should not error on `null` or `undefined` sources (test in IE < 9)', function() { - try { - deepEqual(_.extend({}, null, undefined, { 'a': 1 }), { 'a': 1 }); - } catch(e) { - ok(false); - } - }); - - test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() { - function Foo() {} - Foo.prototype.c = 3; - - Foo.a = 1; - Foo.b = 2; - - var expected = { 'a': 1, 'b': 2 }; - deepEqual(_.extend({}, Foo), expected); - - Foo.prototype = { 'c': 3 }; - deepEqual(_.extend({}, Foo), expected); - }); - - test('should work with `_.reduce`', function() { - var actual = { 'a': 1}, - array = [{ 'b': 2 }, { 'c': 3 }]; - - _.reduce(array, _.extend, actual); - deepEqual(actual, { 'a': 1, 'b': 2, 'c': 3}); - }); - }()); - - /*--------------------------------------------------------------------------*/ - QUnit.module('strict mode checks'); - _.each(['bindAll', 'defaults', 'extend'], function(methodName) { + _.each(['assign', 'bindAll', 'defaults'], function(methodName) { var func = _[methodName]; test('lodash.' + methodName + ' should not throw strict mode errors', function() {