Make _.extend and alias of _.assign and make _.assign iterate only own enumerable source props to align with ES6.

Former-commit-id: 37ba7c3066c1ea70210346a9bf598e8587e907db
This commit is contained in:
John-David Dalton
2012-11-11 15:52:43 -08:00
parent ec6a709393
commit 797a87ca26
4 changed files with 91 additions and 93 deletions

View File

@@ -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/, '');
}

View File

@@ -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;

View File

@@ -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',

View File

@@ -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() {