mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-09 18:37:50 +00:00
Avoid enforcing strict mode in _.defaults, _.extend, and _.bindAll and add benchmarks for _.bindAll and _.functions. [closes #45]
Former-commit-id: 1bb0b5155d3ae46052b4a06cb538dff307e8ec5e
This commit is contained in:
12
build.js
12
build.js
@@ -223,13 +223,13 @@
|
|||||||
'inLoop',
|
'inLoop',
|
||||||
'init',
|
'init',
|
||||||
'isKeysFast',
|
'isKeysFast',
|
||||||
'iteratee',
|
|
||||||
'object',
|
'object',
|
||||||
'objectBranch',
|
'objectBranch',
|
||||||
'noCharByIndex',
|
'noCharByIndex',
|
||||||
'shadowed',
|
'shadowed',
|
||||||
'top',
|
'top',
|
||||||
'useHas'
|
'useHas',
|
||||||
|
'useStrict'
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Collections of method names */
|
/** Collections of method names */
|
||||||
@@ -840,13 +840,7 @@
|
|||||||
// prepend data object references to property names to avoid having to
|
// prepend data object references to property names to avoid having to
|
||||||
// use a with-statement
|
// use a with-statement
|
||||||
iteratorOptions.forEach(function(property) {
|
iteratorOptions.forEach(function(property) {
|
||||||
if (property == 'iteratee') {
|
snippet = snippet.replace(RegExp('([^\\w.])\\b' + property + '\\b', 'g'), '$1obj.' + property);
|
||||||
// use a more fine-grained regexp for the `iteratee` property because
|
|
||||||
// it's a compiled variable as well as a data property
|
|
||||||
snippet = snippet.replace(/(__t *= *\( *)(iteratee)/, '$1obj.$2');
|
|
||||||
} else {
|
|
||||||
snippet = snippet.replace(RegExp('([^\\w.])\\b' + property + '\\b', 'g'), '$1obj.' + property);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove unnecessary code
|
// remove unnecessary code
|
||||||
|
|||||||
@@ -34,9 +34,6 @@
|
|||||||
// move vars exposed by Closure Compiler into the IIFE
|
// move vars exposed by Closure Compiler into the IIFE
|
||||||
source = source.replace(/^([^(\n]+)\s*(\(function[^)]+\){)/, '$2$1');
|
source = source.replace(/^([^(\n]+)\s*(\(function[^)]+\){)/, '$2$1');
|
||||||
|
|
||||||
// use double quotes consistently
|
|
||||||
source = source.replace(/'use strict'/, '"use strict"');
|
|
||||||
|
|
||||||
// unescape properties (i.e. foo["bar"] => foo.bar)
|
// unescape properties (i.e. foo["bar"] => foo.bar)
|
||||||
source = source.replace(/(\w)\["([^."]+)"\]/g, '$1.$2');
|
source = source.replace(/(\w)\["([^."]+)"\]/g, '$1.$2');
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,20 @@
|
|||||||
'accumulator',
|
'accumulator',
|
||||||
'args',
|
'args',
|
||||||
'arrayClass',
|
'arrayClass',
|
||||||
|
'bind',
|
||||||
'callback',
|
'callback',
|
||||||
'className',
|
'className',
|
||||||
'collection',
|
'collection',
|
||||||
'compareAscending',
|
'compareAscending',
|
||||||
'ctor',
|
'ctor',
|
||||||
'funcClass',
|
'funcClass',
|
||||||
|
'funcs',
|
||||||
'hasOwnProperty',
|
'hasOwnProperty',
|
||||||
'identity',
|
'identity',
|
||||||
'index',
|
'index',
|
||||||
'isFunc',
|
'isFunc',
|
||||||
'iteratee',
|
'iteratee',
|
||||||
|
'iterateeIndex',
|
||||||
'iteratorBind',
|
'iteratorBind',
|
||||||
'length',
|
'length',
|
||||||
'methodName',
|
'methodName',
|
||||||
@@ -36,8 +39,6 @@
|
|||||||
'result',
|
'result',
|
||||||
'skipProto',
|
'skipProto',
|
||||||
'slice',
|
'slice',
|
||||||
'source',
|
|
||||||
'sourceIndex',
|
|
||||||
'stringClass',
|
'stringClass',
|
||||||
'target',
|
'target',
|
||||||
'thisArg',
|
'thisArg',
|
||||||
@@ -58,13 +59,13 @@
|
|||||||
'inLoop',
|
'inLoop',
|
||||||
'init',
|
'init',
|
||||||
'isKeysFast',
|
'isKeysFast',
|
||||||
'iteratee',
|
|
||||||
'object',
|
'object',
|
||||||
'objectBranch',
|
'objectBranch',
|
||||||
'noCharByIndex',
|
'noCharByIndex',
|
||||||
'shadowed',
|
'shadowed',
|
||||||
'top',
|
'top',
|
||||||
'useHas'
|
'useHas',
|
||||||
|
'useStrict'
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Used to minify variables and string values to a single character */
|
/** Used to minify variables and string values to a single character */
|
||||||
|
|||||||
76
lodash.js
76
lodash.js
@@ -266,13 +266,17 @@
|
|||||||
* @returns {String} Returns the interpolated text.
|
* @returns {String} Returns the interpolated text.
|
||||||
*/
|
*/
|
||||||
var iteratorTemplate = template(
|
var iteratorTemplate = template(
|
||||||
|
// conditional strict mode
|
||||||
|
'<% if (useStrict) { %>\'use strict\';\n<% } %>' +
|
||||||
|
|
||||||
|
// the `iteratee` may be reassigned by the `top` snippet
|
||||||
|
'var index, iteratee = <%= firstArg %>, ' +
|
||||||
// assign the `result` variable an initial value
|
// assign the `result` variable an initial value
|
||||||
'var result<% if (init) { %> = <%= init %><% } %>;\n' +
|
'result<% if (init) { %> = <%= init %><% } %>;\n' +
|
||||||
// add code to exit early or do so if the first argument is falsey
|
// add code to exit early or do so if the first argument is falsey
|
||||||
'<%= exit %>;\n' +
|
'<%= exit %>;\n' +
|
||||||
// add code after the exit snippet but before the iteration branches
|
// add code after the exit snippet but before the iteration branches
|
||||||
'<%= top %>;\n' +
|
'<%= top %>;\n' +
|
||||||
'var index, iteratee = <%= iteratee %>;\n' +
|
|
||||||
|
|
||||||
// the following branch is for iterating arrays and array-like objects
|
// the following branch is for iterating arrays and array-like objects
|
||||||
'<% if (arrayBranch) { %>' +
|
'<% if (arrayBranch) { %>' +
|
||||||
@@ -389,14 +393,14 @@
|
|||||||
|
|
||||||
/** Reusable iterator options for `defaults` and `extend` */
|
/** Reusable iterator options for `defaults` and `extend` */
|
||||||
var extendIteratorOptions = {
|
var extendIteratorOptions = {
|
||||||
|
'useHas': false,
|
||||||
|
'useStrict': false,
|
||||||
'args': 'object',
|
'args': 'object',
|
||||||
'init': 'object',
|
'init': 'object',
|
||||||
'top':
|
'top':
|
||||||
'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' +
|
'for (var iterateeIndex = 1, length = arguments.length; iterateeIndex < length; iterateeIndex++) {\n' +
|
||||||
' source = arguments[sourceIndex];\n' +
|
' iteratee = arguments[iterateeIndex];\n' +
|
||||||
(hasDontEnumBug ? ' if (source) {' : ''),
|
(hasDontEnumBug ? ' if (iteratee) {' : ''),
|
||||||
'iteratee': 'source',
|
|
||||||
'useHas': false,
|
|
||||||
'inLoop': 'result[index] = iteratee[index]',
|
'inLoop': 'result[index] = iteratee[index]',
|
||||||
'bottom': (hasDontEnumBug ? ' }\n' : '') + '}'
|
'bottom': (hasDontEnumBug ? ' }\n' : '') + '}'
|
||||||
};
|
};
|
||||||
@@ -443,6 +447,12 @@
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} [options1, options2, ...] The compile options objects.
|
* @param {Object} [options1, options2, ...] The compile options objects.
|
||||||
*
|
*
|
||||||
|
* useHas - A boolean to specify whether or not to use `hasOwnProperty` checks
|
||||||
|
* in the object loop.
|
||||||
|
*
|
||||||
|
* useStrict - A boolean to specify whether or not to include the ES5
|
||||||
|
* "use strict" directive.
|
||||||
|
*
|
||||||
* args - A string of comma separated arguments the iteration function will
|
* args - A string of comma separated arguments the iteration function will
|
||||||
* accept.
|
* accept.
|
||||||
*
|
*
|
||||||
@@ -457,12 +467,6 @@
|
|||||||
* beforeLoop - A string or object containing an "array" or "object" property
|
* beforeLoop - A string or object containing an "array" or "object" property
|
||||||
* of code to execute before the array or object loops.
|
* of code to execute before the array or object loops.
|
||||||
*
|
*
|
||||||
* iteratee - A string or object containing an "array" or "object" property
|
|
||||||
* of the variable to be iterated in the loop expression.
|
|
||||||
*
|
|
||||||
* useHas - A boolean to specify whether or not to use `hasOwnProperty` checks
|
|
||||||
* in the object loop.
|
|
||||||
*
|
|
||||||
* inLoop - A string or object containing an "array" or "object" property
|
* inLoop - A string or object containing an "array" or "object" property
|
||||||
* of code to execute in the array or object loops.
|
* of code to execute in the array or object loops.
|
||||||
*
|
*
|
||||||
@@ -506,14 +510,14 @@
|
|||||||
}
|
}
|
||||||
// set additional template `data` values
|
// set additional template `data` values
|
||||||
var args = data.args,
|
var args = data.args,
|
||||||
firstArg = /^[^,]+/.exec(args)[0],
|
firstArg = /^[^,]+/.exec(args)[0];
|
||||||
iteratee = (data.iteratee = data.iteratee || firstArg);
|
|
||||||
|
|
||||||
data.firstArg = firstArg;
|
data.firstArg = firstArg;
|
||||||
data.hasDontEnumBug = hasDontEnumBug;
|
data.hasDontEnumBug = hasDontEnumBug;
|
||||||
data.isKeysFast = isKeysFast;
|
data.isKeysFast = isKeysFast;
|
||||||
data.shadowed = shadowed;
|
data.shadowed = shadowed;
|
||||||
data.useHas = data.useHas !== false;
|
data.useHas = data.useHas !== false;
|
||||||
|
data.useStrict = data.useStrict !== false;
|
||||||
|
|
||||||
if (!('noCharByIndex' in data)) {
|
if (!('noCharByIndex' in data)) {
|
||||||
data.noCharByIndex = noCharByIndex;
|
data.noCharByIndex = noCharByIndex;
|
||||||
@@ -526,14 +530,14 @@
|
|||||||
}
|
}
|
||||||
// create the function factory
|
// create the function factory
|
||||||
var factory = Function(
|
var factory = Function(
|
||||||
'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' +
|
'arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity, ' +
|
||||||
'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' +
|
'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, ' +
|
||||||
'slice, stringClass, toString',
|
'stringClass, toString',
|
||||||
'"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
|
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
|
||||||
);
|
);
|
||||||
// return the compiled function
|
// return the compiled function
|
||||||
return factory(
|
return factory(
|
||||||
arrayClass, compareAscending, funcClass, hasOwnProperty, identity,
|
arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity,
|
||||||
iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice,
|
iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice,
|
||||||
stringClass, toString
|
stringClass, toString
|
||||||
);
|
);
|
||||||
@@ -2058,19 +2062,23 @@
|
|||||||
* jQuery('#lodash_button').on('click', buttonView.onClick);
|
* jQuery('#lodash_button').on('click', buttonView.onClick);
|
||||||
* // => When the button is clicked, `this.label` will have the correct value
|
* // => When the button is clicked, `this.label` will have the correct value
|
||||||
*/
|
*/
|
||||||
function bindAll(object) {
|
var bindAll = createIterator({
|
||||||
var funcs = arguments,
|
'useHas': false,
|
||||||
index = 1;
|
'useStrict': false,
|
||||||
|
'args': 'object',
|
||||||
if (funcs.length == 1) {
|
'init': 'object',
|
||||||
index = 0;
|
'top':
|
||||||
funcs = functions(object);
|
'var funcs = arguments,\n' +
|
||||||
}
|
' length = funcs.length;\n' +
|
||||||
for (var length = funcs.length; index < length; index++) {
|
'if (length > 1) {\n' +
|
||||||
object[funcs[index]] = bind(object[funcs[index]], object);
|
' for (var index = 1; index < length; index++)\n' +
|
||||||
}
|
' result[funcs[index]] = bind(result[funcs[index]], result);\n' +
|
||||||
return object;
|
' return result\n' +
|
||||||
}
|
'}',
|
||||||
|
'inLoop':
|
||||||
|
'if (toString.call(result[index]) == funcClass)' +
|
||||||
|
' result[index] = bind(result[index], result)'
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new function that is the composition of the passed functions,
|
* Creates a new function that is the composition of the passed functions,
|
||||||
@@ -2496,9 +2504,9 @@
|
|||||||
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
|
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
|
||||||
*/
|
*/
|
||||||
var functions = createIterator({
|
var functions = createIterator({
|
||||||
|
'useHas': false,
|
||||||
'args': 'object',
|
'args': 'object',
|
||||||
'init': '[]',
|
'init': '[]',
|
||||||
'useHas': false,
|
|
||||||
'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)',
|
'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)',
|
||||||
'bottom': 'result.sort()'
|
'bottom': 'result.sort()'
|
||||||
});
|
});
|
||||||
|
|||||||
59
perf/perf.js
59
perf/perf.js
@@ -82,12 +82,28 @@
|
|||||||
lodash = window.lodash;
|
lodash = window.lodash;
|
||||||
|
|
||||||
var length = 20,
|
var length = 20,
|
||||||
|
funcNames = lodash.functions(lodash),
|
||||||
numbers = [],
|
numbers = [],
|
||||||
object = {},
|
object = {},
|
||||||
fourNumbers = [5, 25, 10, 30],
|
fourNumbers = [5, 25, 10, 30],
|
||||||
nestedNumbers = [1, [2], [3, [[4]]]],
|
nestedNumbers = [1, [2], [3, [[4]]]],
|
||||||
twoNumbers = [12, 21];
|
twoNumbers = [12, 21];
|
||||||
|
|
||||||
|
var bindAllObjects = [];
|
||||||
|
|
||||||
|
var objects = lodash.map(numbers, function(num) {
|
||||||
|
return { 'num': num };
|
||||||
|
});
|
||||||
|
|
||||||
|
lodash.times(this.count, function(index) {
|
||||||
|
bindAllObjects[index] = lodash.clone(lodash);
|
||||||
|
});
|
||||||
|
|
||||||
|
lodash.times(length, function(index) {
|
||||||
|
numbers[index] = index;
|
||||||
|
object['key' + index] = index;
|
||||||
|
});
|
||||||
|
|
||||||
var ctor = function() { };
|
var ctor = function() { };
|
||||||
|
|
||||||
var func = function(greeting, punctuation) {
|
var func = function(greeting, punctuation) {
|
||||||
@@ -219,15 +235,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
var words = _.keys(wordToNumber).slice(0, length);
|
var words = _.keys(wordToNumber).slice(0, length);
|
||||||
|
|
||||||
for (var index = 0; index < length; index++) {
|
|
||||||
numbers[index] = index;
|
|
||||||
object['key' + index] = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
var objects = lodash.map(numbers, function(num) {
|
|
||||||
return { 'num': num };
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -352,6 +359,28 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
suites.push(
|
||||||
|
Benchmark.Suite('`_.bindAll` iterating arguments')
|
||||||
|
.add('Lo-Dash', function() {
|
||||||
|
lodash.bindAll.apply(lodash, [bindAllObjects.pop()].concat(funcNames));
|
||||||
|
})
|
||||||
|
.add('Underscore', function() {
|
||||||
|
_.bindAll.apply(_, [bindAllObjects.pop()].concat(funcNames));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
suites.push(
|
||||||
|
Benchmark.Suite('`_.bindAll` iterating the `object`')
|
||||||
|
.add('Lo-Dash', function() {
|
||||||
|
lodash.bindAll(bindAllObjects.pop());
|
||||||
|
})
|
||||||
|
.add('Underscore', function() {
|
||||||
|
_.bindAll(bindAllObjects.pop());
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
suites.push(
|
suites.push(
|
||||||
Benchmark.Suite('`_.each` iterating an array')
|
Benchmark.Suite('`_.each` iterating an array')
|
||||||
.add('Lo-Dash', function() {
|
.add('Lo-Dash', function() {
|
||||||
@@ -498,6 +527,18 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
suites.push(
|
||||||
|
Benchmark.Suite('`_.functions`')
|
||||||
|
.add('Lo-Dash', function() {
|
||||||
|
lodash.functions(lodash);
|
||||||
|
})
|
||||||
|
.add('Underscore', function() {
|
||||||
|
_.functions(lodash);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
suites.push(
|
suites.push(
|
||||||
Benchmark.Suite('`_.difference`')
|
Benchmark.Suite('`_.difference`')
|
||||||
.add('Lo-Dash', function() {
|
.add('Lo-Dash', function() {
|
||||||
|
|||||||
41
test/test.js
41
test/test.js
@@ -21,6 +21,9 @@
|
|||||||
_._ || _
|
_._ || _
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** Shortcut used to make object properties immutable */
|
||||||
|
var freeze = Object.freeze;
|
||||||
|
|
||||||
/** Shortcut used to convert array-like objects to arrays */
|
/** Shortcut used to convert array-like objects to arrays */
|
||||||
var slice = [].slice;
|
var slice = [].slice;
|
||||||
|
|
||||||
@@ -52,9 +55,10 @@
|
|||||||
* Skips a given number of tests with a passing result.
|
* Skips a given number of tests with a passing result.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {Number} count The number of tests to skip.
|
* @param {Number} [count=1] The number of tests to skip.
|
||||||
*/
|
*/
|
||||||
function skipTest(count) {
|
function skipTest(count) {
|
||||||
|
count || (count = 1);
|
||||||
while (count--) {
|
while (count--) {
|
||||||
ok(true, 'test skipped');
|
ok(true, 'test skipped');
|
||||||
}
|
}
|
||||||
@@ -71,7 +75,7 @@
|
|||||||
if (window.document && window.require) {
|
if (window.document && window.require) {
|
||||||
equal((lodashModule || {}).moduleName, 'lodash');
|
equal((lodashModule || {}).moduleName, 'lodash');
|
||||||
} else {
|
} else {
|
||||||
skipTest(1)
|
skipTest()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -79,7 +83,7 @@
|
|||||||
if (window.document && window.require) {
|
if (window.document && window.require) {
|
||||||
equal((underscoreModule || {}).moduleName, 'underscore');
|
equal((underscoreModule || {}).moduleName, 'underscore');
|
||||||
} else {
|
} else {
|
||||||
skipTest(1)
|
skipTest()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,7 +91,7 @@
|
|||||||
if (window.document) {
|
if (window.document) {
|
||||||
notDeepEqual(lodashBadShim.keys({ 'a': 1 }), []);
|
notDeepEqual(lodashBadShim.keys({ 'a': 1 }), []);
|
||||||
} else {
|
} else {
|
||||||
skipTest(1);
|
skipTest();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}());
|
}());
|
||||||
@@ -209,6 +213,35 @@
|
|||||||
|
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
_.each(['bindAll', 'defaults', 'extend'], function(methodName) {
|
||||||
|
var func = _[methodName];
|
||||||
|
QUnit.module('lodash.' + methodName + ' strict mode checks');
|
||||||
|
|
||||||
|
test('should not throw strict mode errors', function() {
|
||||||
|
var object = { 'a': null, 'b': function(){} },
|
||||||
|
pass = true;
|
||||||
|
|
||||||
|
if (freeze) {
|
||||||
|
freeze(object);
|
||||||
|
try {
|
||||||
|
if (methodName == 'bindAll') {
|
||||||
|
func(object);
|
||||||
|
} else {
|
||||||
|
func(object, { 'a': 1 });
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
pass = false;
|
||||||
|
}
|
||||||
|
ok(pass);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
skipTest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
QUnit.module('lodash.find');
|
QUnit.module('lodash.find');
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user