mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-07 01:57:50 +00:00
Further optimize _.template by controlling how the with-statement is inserted.
Former-commit-id: a62331e771302f9390d5aca04f878b839fe11b69
This commit is contained in:
11
build.js
11
build.js
@@ -718,19 +718,20 @@
|
|||||||
|
|
||||||
// remove unnecessary code
|
// remove unnecessary code
|
||||||
code = code
|
code = code
|
||||||
.replace(/, *__t, *__j *= *Array.prototype.join|function print[^}]+}/g, '')
|
.replace(/, *__t,[^;]+|function print[^}]+}/g, '')
|
||||||
.replace(/'(?:\\n|\s)+'/g, "''")
|
.replace(/'(?:\\n|\s)+'/g, "''")
|
||||||
.replace(/__p *\+= *' *';/g, '')
|
.replace(/__p *\+= *' *';/g, '')
|
||||||
.replace(/(__p *\+= *)' *' *\+/g, '$1')
|
.replace(/(__p *\+= *)' *' *\+/g, '$1')
|
||||||
.replace(/\+\s*' *';/g, ';')
|
|
||||||
.replace(/(\{) *;|; *(\})/g, '$1$2')
|
.replace(/(\{) *;|; *(\})/g, '$1$2')
|
||||||
.replace(/\(\(__t *= *\( *([^)]+) *\)\) *== *null *\? *'' *: *__t\)/g, '$1');
|
.replace(/\(\(__w?t *= *\( *([^)]+) *\)\) *== *null *\? *'' *: *__w?t\)/g, '$1');
|
||||||
|
|
||||||
// remove the with-statement
|
// remove the with-statement
|
||||||
code = code.replace(/ *with *\([^)]+\) *{/, '\n').replace(/\s*}(\s*)return /, '$1return ');
|
code = code.replace(/ *with *\([^)]+\) *{/, '\n').replace(/}}\s*;?\s*}/, '}}\n');
|
||||||
|
|
||||||
// minor cleanup
|
// minor cleanup
|
||||||
code = code.replace(/var __p;\s*__p/, 'var __p');
|
code = code
|
||||||
|
.replace(/obj *\|\| *\(obj *= *\{}\);/, '')
|
||||||
|
.replace(/var __p;\s*__p/, 'var __p');
|
||||||
|
|
||||||
// remove comments, including sourceURLs
|
// remove comments, including sourceURLs
|
||||||
code = code.replace(/\s*\/\/.*(?:\n|$)/g, '');
|
code = code.replace(/\s*\/\/.*(?:\n|$)/g, '');
|
||||||
|
|||||||
@@ -224,12 +224,17 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// remove whitespace from `_.template` related regexpes
|
// remove whitespace from `_.template` related regexpes
|
||||||
source = source.replace(/(?:reEmptyString\w+|reInsertVariable) *=.+/g, function(match) {
|
source = source.replace(/(?:reDelimiterCode\w+|reEmptyString\w+|reInsertVariable) *=.+/g, function(match) {
|
||||||
return match.replace(/ /g, '');
|
return match.replace(/ |\\n/g, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove newline from double-quoted string in `_.template`
|
// remove newline from double-quoted strings in `_.template`
|
||||||
source = source.replace('"\';\\n"', '"\';"');
|
source = source
|
||||||
|
.replace('"\';\\n__with ("', '"\';__with("')
|
||||||
|
.replace('"\\n}__\\n__p += \'"', '"}____p+=\'"')
|
||||||
|
.replace('"__p = \'"', '"__p=\'"')
|
||||||
|
.replace('"\';\\n"', '"\';"')
|
||||||
|
.replace("') {\\n'", "'){'")
|
||||||
|
|
||||||
// remove `useSourceURL` variable
|
// remove `useSourceURL` variable
|
||||||
source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *try *\{(?:\s*\/\/.*\n)* *var useSourceURL[\s\S]+?catch[^}]+}\n/, '');
|
source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *try *\{(?:\s*\/\/.*\n)* *var useSourceURL[\s\S]+?catch[^}]+}\n/, '');
|
||||||
|
|||||||
150
lodash.js
150
lodash.js
@@ -9,11 +9,29 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to match potentially incorrect data object references, like `obj.obj`,
|
* Used to cache the last `_.templateSettings.evaluate` delimiter to avoid
|
||||||
* in compiled templates. The variables are assigned in `_.template`.
|
* unnecessarily assigning `reEvaluateDelimiter` a new generated regexp.
|
||||||
|
* Assigned in `_.template`.
|
||||||
*/
|
*/
|
||||||
var lastVariable,
|
var lastEvaluateDelimiter;
|
||||||
reDoubleVariable;
|
|
||||||
|
/**
|
||||||
|
* Used to cache the last template `options.variable` to avoid unnecessarily
|
||||||
|
* assigning `reDoubleVariable` a new generated regexp. Assigned in `_.template`.
|
||||||
|
*/
|
||||||
|
var lastVariable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to match potentially incorrect data object references, like `obj.obj`,
|
||||||
|
* in compiled templates. Assigned in `_.template`.
|
||||||
|
*/
|
||||||
|
var reDoubleVariable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to match "evaluate" delimiters, including internal delimiters,
|
||||||
|
* in template text. Assigned in `_.template`.
|
||||||
|
*/
|
||||||
|
var reEvaluateDelimiter;
|
||||||
|
|
||||||
/** Detect free variable `exports` */
|
/** Detect free variable `exports` */
|
||||||
var freeExports = typeof exports == 'object' && exports &&
|
var freeExports = typeof exports == 'object' && exports &&
|
||||||
@@ -36,13 +54,21 @@
|
|||||||
/** Used to restore the original `_` reference in `noConflict` */
|
/** Used to restore the original `_` reference in `noConflict` */
|
||||||
var oldDash = window._;
|
var oldDash = window._;
|
||||||
|
|
||||||
/** Used to match empty strings in compiled templates */
|
/** Used to detect delimiter values that should be processed by `tokenizeEvaluate` */
|
||||||
|
var reComplexDelimiter = /[-)}={(+]|\[\D/;
|
||||||
|
|
||||||
|
/** Used to match code generated in place of template delimiters */
|
||||||
|
var reDelimiterCodeLeading = /^';\n/,
|
||||||
|
reDelimiterCodeMiddle = /^' \+\n/,
|
||||||
|
reDelimiterCodeTrailing = /(?:__p \+= '|\+\n')$/;
|
||||||
|
|
||||||
|
/** Used to match empty string literals in compiled template source */
|
||||||
var reEmptyStringLeading = /\b__p \+= '';/g,
|
var reEmptyStringLeading = /\b__p \+= '';/g,
|
||||||
reEmptyStringMiddle = /\b(__p \+?=) '' \+/g,
|
reEmptyStringMiddle = /\b(__p \+?=) '' \+/g,
|
||||||
reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
|
reEmptyStringTrailing = /(__w?e\(.*?\)|\b__w?t\)) \+\n'';/g;
|
||||||
|
|
||||||
/** Used to insert the data object variable into compiled templates */
|
/** Used to insert the data object variable into compiled template source */
|
||||||
var reInsertVariable = /(?:__e|__t = )\(\s*(?![\s"']|this\.)/g;
|
var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g;
|
||||||
|
|
||||||
/** Used to detect if a method is native */
|
/** Used to detect if a method is native */
|
||||||
var reNative = RegExp('^' +
|
var reNative = RegExp('^' +
|
||||||
@@ -54,7 +80,7 @@
|
|||||||
/** Used to match tokens in template text */
|
/** Used to match tokens in template text */
|
||||||
var reToken = /__token__(\d+)/g;
|
var reToken = /__token__(\d+)/g;
|
||||||
|
|
||||||
/** Used to match unescaped characters in HTML */
|
/** Used to match unescaped characters in strings for inclusion in HTML */
|
||||||
var reUnescapedHtml = /[&<"']/g;
|
var reUnescapedHtml = /[&<"']/g;
|
||||||
|
|
||||||
/** Used to match unescaped characters in compiled string literals */
|
/** Used to match unescaped characters in compiled string literals */
|
||||||
@@ -88,7 +114,7 @@
|
|||||||
nativeIsFinite = window.isFinite,
|
nativeIsFinite = window.isFinite,
|
||||||
nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys;
|
nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys;
|
||||||
|
|
||||||
/** Object#toString result shortcuts */
|
/** `Object#toString` result shortcuts */
|
||||||
var arrayClass = '[object Array]',
|
var arrayClass = '[object Array]',
|
||||||
boolClass = '[object Boolean]',
|
boolClass = '[object Boolean]',
|
||||||
dateClass = '[object Date]',
|
dateClass = '[object Date]',
|
||||||
@@ -480,7 +506,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by `sortBy()` to compare transformed values of `collection`, sorting
|
* Used by `sortBy` to compare transformed values of `collection`, sorting
|
||||||
* them in ascending order.
|
* them in ascending order.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
@@ -502,7 +528,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by `template()` to replace tokens with their corresponding code snippets.
|
* Used by `template` to replace tokens with their corresponding code snippets.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {String} match The matched token.
|
* @param {String} match The matched token.
|
||||||
@@ -514,7 +540,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by `template()` to escape characters for inclusion in compiled
|
* Used by `template` to escape characters for inclusion in compiled
|
||||||
* string literals.
|
* string literals.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
@@ -526,7 +552,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by `escape()` to escape characters for inclusion in HTML.
|
* Used by `escape` to escape characters for inclusion in HTML.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {String} match The matched character to escape.
|
* @param {String} match The matched character to escape.
|
||||||
@@ -576,7 +602,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by `template()` to replace "escape" template delimiters with tokens.
|
* Used by `template` to replace "escape" template delimiters with tokens.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {String} match The matched template delimiter.
|
* @param {String} match The matched template delimiter.
|
||||||
@@ -584,27 +610,41 @@
|
|||||||
* @returns {String} Returns a token.
|
* @returns {String} Returns a token.
|
||||||
*/
|
*/
|
||||||
function tokenizeEscape(match, value) {
|
function tokenizeEscape(match, value) {
|
||||||
|
if (reComplexDelimiter.test(value)) {
|
||||||
|
return '<e%-' + value + '%>';
|
||||||
|
}
|
||||||
var index = tokenized.length;
|
var index = tokenized.length;
|
||||||
tokenized[index] = "' +\n__e(" + value + ") +\n'";
|
tokenized[index] = "' +\n__e(" + value + ") +\n'";
|
||||||
return token + index;
|
return token + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by `template()` to replace "evaluate" template delimiters with tokens.
|
* Used by `template` to replace "evaluate" template delimiters, or complex
|
||||||
|
* "escape" and "interpolate" delimiters, with tokens.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {String} match The matched template delimiter.
|
* @param {String} match The matched template delimiter.
|
||||||
* @param {String} value The delimiter value.
|
* @param {String} value The delimiter value.
|
||||||
|
* @param {String} escapeValue The "escape" delimiter value.
|
||||||
|
* @param {String} interpolateValue The "interpolate" delimiter value.
|
||||||
* @returns {String} Returns a token.
|
* @returns {String} Returns a token.
|
||||||
*/
|
*/
|
||||||
function tokenizeEvaluate(match, value) {
|
function tokenizeEvaluate(match, value, escapeValue, interpolateValue) {
|
||||||
var index = tokenized.length;
|
var index = tokenized.length;
|
||||||
tokenized[index] = "';\n" + value + ";\n__p += '";
|
if (value) {
|
||||||
|
tokenized[index] = "';\n" + value + ";\n__p += '"
|
||||||
|
}
|
||||||
|
else if (escapeValue) {
|
||||||
|
tokenized[index] = "' +\n__we(" + escapeValue + ") +\n'";
|
||||||
|
}
|
||||||
|
else if (interpolateValue) {
|
||||||
|
tokenized[index] = "' +\n((__wt = (" + interpolateValue + ")) == null ? '' : __wt) +\n'";
|
||||||
|
}
|
||||||
return token + index;
|
return token + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by `template()` to replace "interpolate" template delimiters with tokens.
|
* Used by `template` to replace "interpolate" template delimiters with tokens.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {String} match The matched template delimiter.
|
* @param {String} match The matched template delimiter.
|
||||||
@@ -612,6 +652,9 @@
|
|||||||
* @returns {String} Returns a token.
|
* @returns {String} Returns a token.
|
||||||
*/
|
*/
|
||||||
function tokenizeInterpolate(match, value) {
|
function tokenizeInterpolate(match, value) {
|
||||||
|
if (reComplexDelimiter.test(value)) {
|
||||||
|
return '<e%=' + value + '%>';
|
||||||
|
}
|
||||||
var index = tokenized.length;
|
var index = tokenized.length;
|
||||||
tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'";
|
tokenized[index] = "' +\n((__t = (" + value + ")) == null ? '' : __t) +\n'";
|
||||||
return token + index;
|
return token + index;
|
||||||
@@ -3203,9 +3246,7 @@
|
|||||||
options || (options = {});
|
options || (options = {});
|
||||||
|
|
||||||
var endIndex,
|
var endIndex,
|
||||||
isEscaping,
|
|
||||||
isEvaluating,
|
isEvaluating,
|
||||||
isInterpolating,
|
|
||||||
startIndex,
|
startIndex,
|
||||||
result,
|
result,
|
||||||
useWith,
|
useWith,
|
||||||
@@ -3225,18 +3266,25 @@
|
|||||||
if (interpolateDelimiter == null) {
|
if (interpolateDelimiter == null) {
|
||||||
interpolateDelimiter = defaults.interpolate;
|
interpolateDelimiter = defaults.interpolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tokenize delimiters to avoid escaping them
|
// tokenize delimiters to avoid escaping them
|
||||||
if (escapeDelimiter) {
|
if (escapeDelimiter) {
|
||||||
isEscaping = text != (text = text.replace(escapeDelimiter, tokenizeEscape));
|
text = text.replace(escapeDelimiter, tokenizeEscape);
|
||||||
}
|
}
|
||||||
if (interpolateDelimiter) {
|
if (interpolateDelimiter) {
|
||||||
isInterpolating = text != (text = text.replace(interpolateDelimiter, tokenizeInterpolate));
|
text = text.replace(interpolateDelimiter, tokenizeInterpolate);
|
||||||
}
|
}
|
||||||
if (evaluateDelimiter) {
|
if (evaluateDelimiter != lastEvaluateDelimiter) {
|
||||||
startIndex = tokenized.length;
|
lastEvaluateDelimiter = evaluateDelimiter;
|
||||||
isEvaluating = text != (text = text.replace(evaluateDelimiter, tokenizeEvaluate));
|
reEvaluateDelimiter = RegExp(
|
||||||
endIndex = tokenized.length - 1;
|
(evaluateDelimiter ? evaluateDelimiter.source : '($^)') +
|
||||||
|
'|<e%-([\\s\\S]+?)%>|<e%=([\\s\\S]+?)%>'
|
||||||
|
, 'g');
|
||||||
}
|
}
|
||||||
|
startIndex = tokenized.length;
|
||||||
|
text = text.replace(reEvaluateDelimiter, tokenizeEvaluate);
|
||||||
|
endIndex = tokenized.length - 1;
|
||||||
|
isEvaluating = startIndex <= endIndex;
|
||||||
|
|
||||||
// if `options.variable` is not specified and the template contains "evaluate"
|
// if `options.variable` is not specified and the template contains "evaluate"
|
||||||
// delimiters, inject a with-statement around all "evaluate" delimiters to
|
// delimiters, inject a with-statement around all "evaluate" delimiters to
|
||||||
@@ -3246,22 +3294,14 @@
|
|||||||
useWith = isEvaluating;
|
useWith = isEvaluating;
|
||||||
|
|
||||||
if (useWith) {
|
if (useWith) {
|
||||||
tokenized[startIndex] =
|
tokenized[startIndex] = "';\n__with (" + variable + ') {\n' + tokenized[startIndex]
|
||||||
tokenized[startIndex].slice(0, 3) +
|
.replace(reDelimiterCodeLeading, '')
|
||||||
'__with (' + variable + ') {\n' +
|
.replace(reDelimiterCodeMiddle, '__p += ');
|
||||||
tokenized[startIndex].slice(3);
|
|
||||||
|
|
||||||
tokenized[endIndex] =
|
tokenized[endIndex] = tokenized[endIndex]
|
||||||
tokenized[endIndex].slice(0, -8) +
|
.replace(reDelimiterCodeTrailing, '') + "\n}__\n__p += '";
|
||||||
'}__\n' +
|
|
||||||
tokenized[endIndex].slice(-8);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// memoize `reDoubleVariable`
|
|
||||||
if (variable != lastVariable) {
|
|
||||||
lastVariable = variable;
|
|
||||||
reDoubleVariable = RegExp('([(\\s])' + variable + '\\.' + variable + '\\b', 'g');
|
|
||||||
}
|
|
||||||
|
|
||||||
var strInsertVariable = '$&' + variable + '.',
|
var strInsertVariable = '$&' + variable + '.',
|
||||||
strDoubleVariable = '$1__d';
|
strDoubleVariable = '$1__d';
|
||||||
@@ -3278,9 +3318,13 @@
|
|||||||
// find the start and end indexes of the with-statement
|
// find the start and end indexes of the with-statement
|
||||||
if (useWith) {
|
if (useWith) {
|
||||||
startIndex = text.indexOf('__with');
|
startIndex = text.indexOf('__with');
|
||||||
endIndex = text.indexOf('}__', startIndex + 12);
|
endIndex = text.indexOf('}__', startIndex + 10);
|
||||||
|
}
|
||||||
|
// memoize `reDoubleVariable`
|
||||||
|
if (variable != lastVariable) {
|
||||||
|
lastVariable = variable;
|
||||||
|
reDoubleVariable = RegExp('([(\\s])' + variable + '\\.' + variable + '\\b', 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
// inject data object references outside of the with-statement
|
// inject data object references outside of the with-statement
|
||||||
text = (useWith ? text.slice(0, startIndex) : text)
|
text = (useWith ? text.slice(0, startIndex) : text)
|
||||||
.replace(reInsertVariable, strInsertVariable)
|
.replace(reInsertVariable, strInsertVariable)
|
||||||
@@ -3293,27 +3337,17 @@
|
|||||||
: ''
|
: ''
|
||||||
);
|
);
|
||||||
|
|
||||||
// cleanup compiled code by stripping empty strings
|
// cleanup code by stripping empty strings
|
||||||
text = (isEvaluating ? text.replace(reEmptyStringLeading, '') : text)
|
text = (isEvaluating ? text.replace(reEmptyStringLeading, '') : text)
|
||||||
.replace(reEmptyStringMiddle, '$1')
|
.replace(reEmptyStringMiddle, '$1')
|
||||||
.replace(reEmptyStringTrailing, '$1;');
|
.replace(reEmptyStringTrailing, '$1;');
|
||||||
|
|
||||||
// wrap function body
|
// frame code as the function body
|
||||||
text = 'function(' + variable + ') {\n' +
|
text = 'function(' + variable + ') {\n' +
|
||||||
(useWith
|
variable + ' || (' + variable + ' = {});\n' +
|
||||||
? variable + ' || (' + variable + ' = {});\n'
|
'var __p, __t, __wt' +
|
||||||
: ''
|
|
||||||
) +
|
|
||||||
'var __p' +
|
|
||||||
(isInterpolating
|
|
||||||
? ', __t'
|
|
||||||
: ''
|
|
||||||
) +
|
|
||||||
', __d = ' + variable + '.' + variable + ' || ' + variable +
|
', __d = ' + variable + '.' + variable + ' || ' + variable +
|
||||||
(isEscaping
|
', __e = _.escape, __we = __e' +
|
||||||
? ', __e = _.escape'
|
|
||||||
: ''
|
|
||||||
) +
|
|
||||||
(isEvaluating
|
(isEvaluating
|
||||||
? ', __j = Array.prototype.join;\n' +
|
? ', __j = Array.prototype.join;\n' +
|
||||||
'function print() { __p += __j.call(arguments, \'\') }\n'
|
'function print() { __p += __j.call(arguments, \'\') }\n'
|
||||||
@@ -3333,7 +3367,7 @@
|
|||||||
if (data) {
|
if (data) {
|
||||||
return result(data);
|
return result(data);
|
||||||
}
|
}
|
||||||
// provide the compiled function's source via its `toString()` method, in
|
// provide the compiled function's source via its `toString` method, in
|
||||||
// supported environments, or the `source` property as a convenience for
|
// supported environments, or the `source` property as a convenience for
|
||||||
// build time precompilation
|
// build time precompilation
|
||||||
result.source = text;
|
result.source = text;
|
||||||
|
|||||||
@@ -699,7 +699,7 @@
|
|||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
suites.push(
|
suites.push(
|
||||||
Benchmark.Suite('`_.template` without "evaluate" delimiters')
|
Benchmark.Suite('`_.template` without "evaluate" delimiters (slow path)')
|
||||||
.add('Lo-Dash', function() {
|
.add('Lo-Dash', function() {
|
||||||
lodash.template(tpl, tplData);
|
lodash.template(tpl, tplData);
|
||||||
})
|
})
|
||||||
@@ -709,7 +709,7 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
suites.push(
|
suites.push(
|
||||||
Benchmark.Suite('`_.template` with "evaluate" delimiters')
|
Benchmark.Suite('`_.template` with "evaluate" delimiters (slow path)')
|
||||||
.add('Lo-Dash', function() {
|
.add('Lo-Dash', function() {
|
||||||
lodash.template(tplWithEvaluate, tplData);
|
lodash.template(tplWithEvaluate, tplData);
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user