mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-29 06:27:49 +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',
|
||||
'init',
|
||||
'isKeysFast',
|
||||
'iteratee',
|
||||
'object',
|
||||
'objectBranch',
|
||||
'noCharByIndex',
|
||||
'shadowed',
|
||||
'top',
|
||||
'useHas'
|
||||
'useHas',
|
||||
'useStrict'
|
||||
];
|
||||
|
||||
/** Collections of method names */
|
||||
@@ -840,13 +840,7 @@
|
||||
// prepend data object references to property names to avoid having to
|
||||
// use a with-statement
|
||||
iteratorOptions.forEach(function(property) {
|
||||
if (property == 'iteratee') {
|
||||
// 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);
|
||||
}
|
||||
snippet = snippet.replace(RegExp('([^\\w.])\\b' + property + '\\b', 'g'), '$1obj.' + property);
|
||||
});
|
||||
|
||||
// remove unnecessary code
|
||||
|
||||
@@ -34,9 +34,6 @@
|
||||
// move vars exposed by Closure Compiler into the IIFE
|
||||
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)
|
||||
source = source.replace(/(\w)\["([^."]+)"\]/g, '$1.$2');
|
||||
|
||||
|
||||
@@ -10,17 +10,20 @@
|
||||
'accumulator',
|
||||
'args',
|
||||
'arrayClass',
|
||||
'bind',
|
||||
'callback',
|
||||
'className',
|
||||
'collection',
|
||||
'compareAscending',
|
||||
'ctor',
|
||||
'funcClass',
|
||||
'funcs',
|
||||
'hasOwnProperty',
|
||||
'identity',
|
||||
'index',
|
||||
'isFunc',
|
||||
'iteratee',
|
||||
'iterateeIndex',
|
||||
'iteratorBind',
|
||||
'length',
|
||||
'methodName',
|
||||
@@ -36,8 +39,6 @@
|
||||
'result',
|
||||
'skipProto',
|
||||
'slice',
|
||||
'source',
|
||||
'sourceIndex',
|
||||
'stringClass',
|
||||
'target',
|
||||
'thisArg',
|
||||
@@ -58,13 +59,13 @@
|
||||
'inLoop',
|
||||
'init',
|
||||
'isKeysFast',
|
||||
'iteratee',
|
||||
'object',
|
||||
'objectBranch',
|
||||
'noCharByIndex',
|
||||
'shadowed',
|
||||
'top',
|
||||
'useHas'
|
||||
'useHas',
|
||||
'useStrict'
|
||||
];
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
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
|
||||
'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
|
||||
'<%= exit %>;\n' +
|
||||
// add code after the exit snippet but before the iteration branches
|
||||
'<%= top %>;\n' +
|
||||
'var index, iteratee = <%= iteratee %>;\n' +
|
||||
|
||||
// the following branch is for iterating arrays and array-like objects
|
||||
'<% if (arrayBranch) { %>' +
|
||||
@@ -389,14 +393,14 @@
|
||||
|
||||
/** Reusable iterator options for `defaults` and `extend` */
|
||||
var extendIteratorOptions = {
|
||||
'useHas': false,
|
||||
'useStrict': false,
|
||||
'args': 'object',
|
||||
'init': 'object',
|
||||
'top':
|
||||
'for (var source, sourceIndex = 1, length = arguments.length; sourceIndex < length; sourceIndex++) {\n' +
|
||||
' source = arguments[sourceIndex];\n' +
|
||||
(hasDontEnumBug ? ' if (source) {' : ''),
|
||||
'iteratee': 'source',
|
||||
'useHas': false,
|
||||
'for (var iterateeIndex = 1, length = arguments.length; iterateeIndex < length; iterateeIndex++) {\n' +
|
||||
' iteratee = arguments[iterateeIndex];\n' +
|
||||
(hasDontEnumBug ? ' if (iteratee) {' : ''),
|
||||
'inLoop': 'result[index] = iteratee[index]',
|
||||
'bottom': (hasDontEnumBug ? ' }\n' : '') + '}'
|
||||
};
|
||||
@@ -443,6 +447,12 @@
|
||||
* @private
|
||||
* @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
|
||||
* accept.
|
||||
*
|
||||
@@ -457,12 +467,6 @@
|
||||
* beforeLoop - A string or object containing an "array" or "object" property
|
||||
* 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
|
||||
* of code to execute in the array or object loops.
|
||||
*
|
||||
@@ -506,14 +510,14 @@
|
||||
}
|
||||
// set additional template `data` values
|
||||
var args = data.args,
|
||||
firstArg = /^[^,]+/.exec(args)[0],
|
||||
iteratee = (data.iteratee = data.iteratee || firstArg);
|
||||
firstArg = /^[^,]+/.exec(args)[0];
|
||||
|
||||
data.firstArg = firstArg;
|
||||
data.hasDontEnumBug = hasDontEnumBug;
|
||||
data.isKeysFast = isKeysFast;
|
||||
data.shadowed = shadowed;
|
||||
data.useHas = data.useHas !== false;
|
||||
data.useStrict = data.useStrict !== false;
|
||||
|
||||
if (!('noCharByIndex' in data)) {
|
||||
data.noCharByIndex = noCharByIndex;
|
||||
@@ -526,14 +530,14 @@
|
||||
}
|
||||
// create the function factory
|
||||
var factory = Function(
|
||||
'arrayClass, compareAscending, funcClass, hasOwnProperty, identity, ' +
|
||||
'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, ' +
|
||||
'slice, stringClass, toString',
|
||||
'"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
|
||||
'arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity, ' +
|
||||
'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, ' +
|
||||
'stringClass, toString',
|
||||
'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
|
||||
);
|
||||
// return the compiled function
|
||||
return factory(
|
||||
arrayClass, compareAscending, funcClass, hasOwnProperty, identity,
|
||||
arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity,
|
||||
iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice,
|
||||
stringClass, toString
|
||||
);
|
||||
@@ -2058,19 +2062,23 @@
|
||||
* jQuery('#lodash_button').on('click', buttonView.onClick);
|
||||
* // => When the button is clicked, `this.label` will have the correct value
|
||||
*/
|
||||
function bindAll(object) {
|
||||
var funcs = arguments,
|
||||
index = 1;
|
||||
|
||||
if (funcs.length == 1) {
|
||||
index = 0;
|
||||
funcs = functions(object);
|
||||
}
|
||||
for (var length = funcs.length; index < length; index++) {
|
||||
object[funcs[index]] = bind(object[funcs[index]], object);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
var bindAll = createIterator({
|
||||
'useHas': false,
|
||||
'useStrict': false,
|
||||
'args': 'object',
|
||||
'init': 'object',
|
||||
'top':
|
||||
'var funcs = arguments,\n' +
|
||||
' length = funcs.length;\n' +
|
||||
'if (length > 1) {\n' +
|
||||
' for (var index = 1; index < length; index++)\n' +
|
||||
' result[funcs[index]] = bind(result[funcs[index]], result);\n' +
|
||||
' 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,
|
||||
@@ -2496,9 +2504,9 @@
|
||||
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
|
||||
*/
|
||||
var functions = createIterator({
|
||||
'useHas': false,
|
||||
'args': 'object',
|
||||
'init': '[]',
|
||||
'useHas': false,
|
||||
'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)',
|
||||
'bottom': 'result.sort()'
|
||||
});
|
||||
|
||||
59
perf/perf.js
59
perf/perf.js
@@ -82,12 +82,28 @@
|
||||
lodash = window.lodash;
|
||||
|
||||
var length = 20,
|
||||
funcNames = lodash.functions(lodash),
|
||||
numbers = [],
|
||||
object = {},
|
||||
fourNumbers = [5, 25, 10, 30],
|
||||
nestedNumbers = [1, [2], [3, [[4]]]],
|
||||
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 func = function(greeting, punctuation) {
|
||||
@@ -219,15 +235,6 @@
|
||||
};
|
||||
|
||||
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(
|
||||
Benchmark.Suite('`_.each` iterating an array')
|
||||
.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(
|
||||
Benchmark.Suite('`_.difference`')
|
||||
.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 */
|
||||
var slice = [].slice;
|
||||
|
||||
@@ -52,9 +55,10 @@
|
||||
* Skips a given number of tests with a passing result.
|
||||
*
|
||||
* @private
|
||||
* @param {Number} count The number of tests to skip.
|
||||
* @param {Number} [count=1] The number of tests to skip.
|
||||
*/
|
||||
function skipTest(count) {
|
||||
count || (count = 1);
|
||||
while (count--) {
|
||||
ok(true, 'test skipped');
|
||||
}
|
||||
@@ -71,7 +75,7 @@
|
||||
if (window.document && window.require) {
|
||||
equal((lodashModule || {}).moduleName, 'lodash');
|
||||
} else {
|
||||
skipTest(1)
|
||||
skipTest()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -79,7 +83,7 @@
|
||||
if (window.document && window.require) {
|
||||
equal((underscoreModule || {}).moduleName, 'underscore');
|
||||
} else {
|
||||
skipTest(1)
|
||||
skipTest()
|
||||
}
|
||||
});
|
||||
|
||||
@@ -87,7 +91,7 @@
|
||||
if (window.document) {
|
||||
notDeepEqual(lodashBadShim.keys({ 'a': 1 }), []);
|
||||
} 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');
|
||||
|
||||
(function() {
|
||||
|
||||
Reference in New Issue
Block a user