mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-11 19:37:49 +00:00
Use native Object.create when available, optimize the creation of lodash instances, and ensure methods like forEach return the existing wrapper object when chaining, instead of creating a new one.
Former-commit-id: fa9ec371ba23ce8c35c15a66dd9b1f09f183b3a9
This commit is contained in:
65
build.js
65
build.js
@@ -381,21 +381,26 @@
|
|||||||
].join('\n' + indent);
|
].join('\n' + indent);
|
||||||
});
|
});
|
||||||
|
|
||||||
// add `__chain__` checks to `_.mixin`
|
// replace `_.mixin`
|
||||||
source = source.replace(matchFunction(source, 'mixin'), function(match) {
|
source = replaceFunction(source, 'mixin', [
|
||||||
return match.replace(/^( *)return new lodash.+/m, function() {
|
'function mixin(object) {',
|
||||||
var indent = arguments[1];
|
' forEach(functions(object), function(methodName) {',
|
||||||
return indent + [
|
' var func = lodash[methodName] = object[methodName];',
|
||||||
'',
|
'',
|
||||||
'var result = func.apply(lodash, args);',
|
' lodash.prototype[methodName] = function() {',
|
||||||
'if (this.__chain__) {',
|
' var args = [this.__wrapped__];',
|
||||||
' result = new lodash(result);',
|
' push.apply(args, arguments);',
|
||||||
' result.__chain__ = true;',
|
'',
|
||||||
'}',
|
' var result = func.apply(lodash, args);',
|
||||||
'return result;'
|
' if (this.__chain__) {',
|
||||||
].join('\n' + indent);
|
' result = createWrapper(result);',
|
||||||
});
|
' result.__chain__ = true;',
|
||||||
});
|
' }',
|
||||||
|
' return result;',
|
||||||
|
' };',
|
||||||
|
' });',
|
||||||
|
'}'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
// replace wrapper `Array` method assignments
|
// replace wrapper `Array` method assignments
|
||||||
source = source.replace(/^(?: *\/\/.*\n)*( *)each\(\['[\s\S]+?\n\1}$/m, function(match, indent) {
|
source = source.replace(/^(?: *\/\/.*\n)*( *)each\(\['[\s\S]+?\n\1}$/m, function(match, indent) {
|
||||||
@@ -1174,7 +1179,7 @@
|
|||||||
|
|
||||||
// remove `noCharByIndex` from `_.toArray`
|
// remove `noCharByIndex` from `_.toArray`
|
||||||
source = source.replace(matchFunction(source, 'toArray'), function(match) {
|
source = source.replace(matchFunction(source, 'toArray'), function(match) {
|
||||||
return match.replace(/noCharByIndex[^:]+:/, '');
|
return match.replace(/(return\b).+?noCharByIndex[^:]+:\s*/, '$1 ');
|
||||||
});
|
});
|
||||||
|
|
||||||
// `noCharByIndex` from `iteratorTemplate`
|
// `noCharByIndex` from `iteratorTemplate`
|
||||||
@@ -1753,7 +1758,7 @@
|
|||||||
if (isModern) {
|
if (isModern) {
|
||||||
// remove `_.isPlainObject` fallback
|
// remove `_.isPlainObject` fallback
|
||||||
source = source.replace(matchFunction(source, 'isPlainObject'), function(match) {
|
source = source.replace(matchFunction(source, 'isPlainObject'), function(match) {
|
||||||
return match.replace(/!getPrototypeOf.+?: */, '');
|
return match.replace(/!getPrototypeOf[^:]+:\s*/, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
@@ -1927,7 +1932,7 @@
|
|||||||
' }',
|
' }',
|
||||||
' var isArr = className == arrayClass;',
|
' var isArr = className == arrayClass;',
|
||||||
' if (!isArr) {',
|
' if (!isArr) {',
|
||||||
' if (a.__wrapped__ || b.__wrapped__) {',
|
' if (a instanceof lodash || b instanceof lodash) {',
|
||||||
' return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, stackA, stackB);',
|
' return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, stackA, stackB);',
|
||||||
' }',
|
' }',
|
||||||
' if (className != objectClass) {',
|
' if (className != objectClass) {',
|
||||||
@@ -1989,6 +1994,19 @@
|
|||||||
'}'
|
'}'
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
|
|
||||||
|
// replace `lodash`
|
||||||
|
source = replaceFunction(source, 'lodash', [
|
||||||
|
'function lodash(value) {',
|
||||||
|
' if (value instanceof lodash) {',
|
||||||
|
' return value;',
|
||||||
|
' }',
|
||||||
|
' if (!(this instanceof lodash)) {',
|
||||||
|
' return new lodash(value);',
|
||||||
|
' }',
|
||||||
|
' this.__wrapped__ = value;',
|
||||||
|
'}'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
// replace `_.omit`
|
// replace `_.omit`
|
||||||
source = replaceFunction(source, 'omit', [
|
source = replaceFunction(source, 'omit', [
|
||||||
'function omit(object) {',
|
'function omit(object) {',
|
||||||
@@ -2189,7 +2207,7 @@
|
|||||||
// remove conditional `charCodeCallback` use from `_.max` and `_.min`
|
// remove conditional `charCodeCallback` use from `_.max` and `_.min`
|
||||||
_.each(['max', 'min'], function(methodName) {
|
_.each(['max', 'min'], function(methodName) {
|
||||||
source = source.replace(matchFunction(source, methodName), function(match) {
|
source = source.replace(matchFunction(source, methodName), function(match) {
|
||||||
return match.replace(/!callback *&& *isString\(collection\)[\s\S]+?: */g, '');
|
return match.replace(/(return\b).+?callback *&& *isString[^:]+:\s*/g, '$1 ');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2469,9 +2487,14 @@
|
|||||||
' lodash[methodName] = func;',
|
' lodash[methodName] = func;',
|
||||||
'',
|
'',
|
||||||
' lodash.prototype[methodName] = function() {',
|
' lodash.prototype[methodName] = function() {',
|
||||||
' var args = [this.__wrapped__];',
|
' var value = this.__wrapped__,',
|
||||||
|
' args = [value];',
|
||||||
|
'',
|
||||||
' push.apply(args, arguments);',
|
' push.apply(args, arguments);',
|
||||||
' return new lodash(func.apply(lodash, args));',
|
' var result = func.apply(lodash, args);',
|
||||||
|
" return (value && typeof value == 'object' && value == result)",
|
||||||
|
' ? this',
|
||||||
|
' : createWrapper(result);',
|
||||||
' };',
|
' };',
|
||||||
'});'
|
'});'
|
||||||
].join('\n' + indent);
|
].join('\n' + indent);
|
||||||
|
|||||||
61
lodash.js
61
lodash.js
@@ -174,6 +174,7 @@
|
|||||||
|
|
||||||
/* Native method shortcuts for methods with the same name as other `lodash` methods */
|
/* Native method shortcuts for methods with the same name as other `lodash` methods */
|
||||||
var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind,
|
var nativeBind = reNative.test(nativeBind = slice.bind) && nativeBind,
|
||||||
|
nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate,
|
||||||
nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
|
nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
|
||||||
nativeIsFinite = context.isFinite,
|
nativeIsFinite = context.isFinite,
|
||||||
nativeIsNaN = context.isNaN,
|
nativeIsNaN = context.isNaN,
|
||||||
@@ -319,7 +320,7 @@
|
|||||||
*/
|
*/
|
||||||
function lodash(value) {
|
function lodash(value) {
|
||||||
// exit early if already wrapped, even if wrapped by a different `lodash` constructor
|
// exit early if already wrapped, even if wrapped by a different `lodash` constructor
|
||||||
if (value && typeof value == 'object' && value.__wrapped__) {
|
if (value && typeof value == 'object' && hasOwnProperty.call(value, '__wrapped__')) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
// allow invoking `lodash` without the `new` operator
|
// allow invoking `lodash` without the `new` operator
|
||||||
@@ -644,9 +645,7 @@
|
|||||||
}
|
}
|
||||||
if (this instanceof bound) {
|
if (this instanceof bound) {
|
||||||
// ensure `new bound` is an instance of `func`
|
// ensure `new bound` is an instance of `func`
|
||||||
noop.prototype = func.prototype;
|
thisBinding = createObject(func.prototype);
|
||||||
thisBinding = new noop;
|
|
||||||
noop.prototype = null;
|
|
||||||
|
|
||||||
// mimic the constructor's `return` behavior
|
// mimic the constructor's `return` behavior
|
||||||
// http://es5.github.com/#x13.2.2
|
// http://es5.github.com/#x13.2.2
|
||||||
@@ -769,6 +768,33 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object that inherits from the given `prototype` object.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Object} prototype The prototype object.
|
||||||
|
* @returns {Object} Returns the new object.
|
||||||
|
*/
|
||||||
|
var createObject = nativeCreate || function(prototype) {
|
||||||
|
noop.prototype = prototype;
|
||||||
|
var result = new noop;
|
||||||
|
noop.prototype = null;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fast path for creating `lodash` wrapper objects.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Mixed} value The value to wrap in a `lodash` instance.
|
||||||
|
* @returns {Object} Returns a `lodash` instance.
|
||||||
|
*/
|
||||||
|
function createWrapper(value) {
|
||||||
|
var result = createObject(lodash.prototype);
|
||||||
|
result.__wrapped__ = value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function compiled to iterate `arguments` objects, arrays, objects, and
|
* A function compiled to iterate `arguments` objects, arrays, objects, and
|
||||||
* strings consistenly across environments, executing the `callback` for each
|
* strings consistenly across environments, executing the `callback` for each
|
||||||
@@ -1562,7 +1588,7 @@
|
|||||||
|
|
||||||
case numberClass:
|
case numberClass:
|
||||||
// treat `NaN` vs. `NaN` as equal
|
// treat `NaN` vs. `NaN` as equal
|
||||||
return a != +a
|
return (a != +a)
|
||||||
? b != +b
|
? b != +b
|
||||||
// but treat `+0` vs. `-0` as not equal
|
// but treat `+0` vs. `-0` as not equal
|
||||||
: (a == 0 ? (1 / a == 1 / b) : a == +b);
|
: (a == 0 ? (1 / a == 1 / b) : a == +b);
|
||||||
@@ -1576,7 +1602,7 @@
|
|||||||
var isArr = className == arrayClass;
|
var isArr = className == arrayClass;
|
||||||
if (!isArr) {
|
if (!isArr) {
|
||||||
// unwrap any `lodash` wrapped values
|
// unwrap any `lodash` wrapped values
|
||||||
if (a.__wrapped__ || b.__wrapped__) {
|
if (hasOwnProperty.call(a, '__wrapped__ ') || hasOwnProperty.call(b, '__wrapped__')) {
|
||||||
return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, callback, thisArg, stackA, stackB);
|
return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, callback, thisArg, stackA, stackB);
|
||||||
}
|
}
|
||||||
// exit for functions and DOM nodes
|
// exit for functions and DOM nodes
|
||||||
@@ -2748,7 +2774,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback = !callback && isString(collection)
|
callback = (!callback && isString(collection))
|
||||||
? charAtCallback
|
? charAtCallback
|
||||||
: createCallback(callback, thisArg);
|
: createCallback(callback, thisArg);
|
||||||
|
|
||||||
@@ -2817,7 +2843,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback = !callback && isString(collection)
|
callback = (!callback && isString(collection))
|
||||||
? charAtCallback
|
? charAtCallback
|
||||||
: createCallback(callback, thisArg);
|
: createCallback(callback, thisArg);
|
||||||
|
|
||||||
@@ -3181,7 +3207,7 @@
|
|||||||
*/
|
*/
|
||||||
function toArray(collection) {
|
function toArray(collection) {
|
||||||
if (collection && typeof collection.length == 'number') {
|
if (collection && typeof collection.length == 'number') {
|
||||||
return noCharByIndex && isString(collection)
|
return (noCharByIndex && isString(collection))
|
||||||
? collection.split('')
|
? collection.split('')
|
||||||
: slice(collection);
|
: slice(collection);
|
||||||
}
|
}
|
||||||
@@ -3848,7 +3874,7 @@
|
|||||||
|
|
||||||
while (low < high) {
|
while (low < high) {
|
||||||
var mid = (low + high) >>> 1;
|
var mid = (low + high) >>> 1;
|
||||||
callback(array[mid]) < value
|
(callback(array[mid]) < value)
|
||||||
? low = mid + 1
|
? low = mid + 1
|
||||||
: high = mid;
|
: high = mid;
|
||||||
}
|
}
|
||||||
@@ -4573,9 +4599,14 @@
|
|||||||
var func = lodash[methodName] = object[methodName];
|
var func = lodash[methodName] = object[methodName];
|
||||||
|
|
||||||
lodash.prototype[methodName] = function() {
|
lodash.prototype[methodName] = function() {
|
||||||
var args = [this.__wrapped__];
|
var value = this.__wrapped__,
|
||||||
|
args = [value];
|
||||||
|
|
||||||
push.apply(args, arguments);
|
push.apply(args, arguments);
|
||||||
return new lodash(func.apply(lodash, args));
|
var result = func.apply(lodash, args);
|
||||||
|
return (value && typeof value == 'object' && value == result)
|
||||||
|
? this
|
||||||
|
: createWrapper(result);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -5130,7 +5161,7 @@
|
|||||||
var result = func(this.__wrapped__, callback, thisArg);
|
var result = func(this.__wrapped__, callback, thisArg);
|
||||||
return callback == null || (thisArg && typeof callback != 'function')
|
return callback == null || (thisArg && typeof callback != 'function')
|
||||||
? result
|
? result
|
||||||
: new lodash(result);
|
: createWrapper(result);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -5172,7 +5203,7 @@
|
|||||||
each(['concat', 'slice', 'splice'], function(methodName) {
|
each(['concat', 'slice', 'splice'], function(methodName) {
|
||||||
var func = arrayRef[methodName];
|
var func = arrayRef[methodName];
|
||||||
lodash.prototype[methodName] = function() {
|
lodash.prototype[methodName] = function() {
|
||||||
return new lodash(func.apply(this.__wrapped__, arguments));
|
return createWrapper(func.apply(this.__wrapped__, arguments));
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -5190,7 +5221,7 @@
|
|||||||
if (value.length === 0) {
|
if (value.length === 0) {
|
||||||
delete value[0];
|
delete value[0];
|
||||||
}
|
}
|
||||||
return isSplice ? new lodash(result) : result;
|
return isSplice ? createWrapper(result) : result;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
17
test/test.js
17
test/test.js
@@ -179,8 +179,8 @@
|
|||||||
ok(_() instanceof _);
|
ok(_() instanceof _);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return passed LoDash instances', function() {
|
test('should return passed `lodash` instances', function() {
|
||||||
var wrapped = _([]);
|
var wrapped = _(false);
|
||||||
equal(_(wrapped), wrapped);
|
equal(_(wrapped), wrapped);
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
@@ -810,11 +810,16 @@
|
|||||||
QUnit.module('lodash.forEach');
|
QUnit.module('lodash.forEach');
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
test('returns the collection', function() {
|
test('should return the collection', function() {
|
||||||
var collection = [1, 2, 3];
|
var collection = [1, 2, 3];
|
||||||
equal(_.forEach(collection, Boolean), collection);
|
equal(_.forEach(collection, Boolean), collection);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should return the existing wrapper when chaining', function() {
|
||||||
|
var wrapper = _([1, 2, 3]);
|
||||||
|
equal(wrapper.forEach(Boolean), wrapper);
|
||||||
|
});
|
||||||
|
|
||||||
_.each({
|
_.each({
|
||||||
'literal': 'abc',
|
'literal': 'abc',
|
||||||
'object': Object('abc')
|
'object': Object('abc')
|
||||||
@@ -901,6 +906,12 @@
|
|||||||
_.each(['assign', 'defaults', 'merge'], function(methodName) {
|
_.each(['assign', 'defaults', 'merge'], function(methodName) {
|
||||||
var func = _[methodName];
|
var func = _[methodName];
|
||||||
|
|
||||||
|
test('should return the existing wrapper when chaining', function() {
|
||||||
|
var wrapper = _({ 'a': 1 });
|
||||||
|
equal(wrapper[methodName]({ 'b': 2 }), wrapper);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('lodash.' + methodName + ' should assign problem JScript properties (test in IE < 9)', function() {
|
test('lodash.' + methodName + ' should assign problem JScript properties (test in IE < 9)', function() {
|
||||||
var object = {
|
var object = {
|
||||||
'constructor': '0',
|
'constructor': '0',
|
||||||
|
|||||||
Reference in New Issue
Block a user