lodash: Optimize template. [jddalton]

Former-commit-id: 2cd61549491714e6796308ec437fa8dff8fa9a1b
This commit is contained in:
John-David Dalton
2012-05-03 00:23:57 -04:00
parent a426109c1c
commit 221f70e609
2 changed files with 79 additions and 72 deletions

View File

@@ -84,6 +84,7 @@
'isEqual', 'isEqual',
'isFinite', 'isFinite',
'setTimeout', 'setTimeout',
'source',
'templateSettings', 'templateSettings',
'toArray', 'toArray',
'value', 'value',
@@ -112,7 +113,7 @@
// remove whitespace from string literals // remove whitespace from string literals
source = source.replace(/'(?:(?=(\\?))\1.)*?'/g, function(string) { source = source.replace(/'(?:(?=(\\?))\1.)*?'/g, function(string) {
return string.replace(/\[object |else if|function | in |return\s+[\w']|throw |use strict|var |'\\n'|\\n|\s+/g, function(match) { return string.replace(/\[object |else if|function | in |return\s+[\w']|throw |use strict|var |['|]\\n[|']|\\n|\s+/g, function(match) {
return match == false || match == '\\n' ? '' : match; return match == false || match == '\\n' ? '' : match;
}); });
}); });

148
lodash.js
View File

@@ -8,24 +8,17 @@
;(function(window, undefined) { ;(function(window, undefined) {
'use strict'; 'use strict';
/** Used to escape and unescape characters in templates */ /** Used to escape characters in templates */
var escapes = { var escapes = {
'\\': '\\', '\\': '\\',
"'": "'", "'": "'",
'r': '\r', '\n': 'n',
'n': '\n', '\r': 'r',
't': '\t', '\t': 't',
'u2028': '\u2028', '\u2028': 'u2028',
'u2029': '\u2029' '\u2029': 'u2029'
}; };
// assign the result as keys and the keys as values
(function() {
for (var prop in escapes) {
escapes[escapes[prop]] = prop;
}
}());
/** Detect free variable `exports` */ /** Detect free variable `exports` */
var freeExports = typeof exports == 'object' && exports && var freeExports = typeof exports == 'object' && exports &&
(typeof global == 'object' && global && global == global.global && (window = global), exports); (typeof global == 'object' && global && global == global.global && (window = global), exports);
@@ -36,18 +29,11 @@
/** Used to restore the original `_` reference in `noConflict` */ /** Used to restore the original `_` reference in `noConflict` */
var oldDash = window._; var oldDash = window._;
/** Used to replace unescape characters with their escaped counterpart */ /** Used to store unique `reUnescaped` regexps */
var reEscaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; var reCache = {};
/** /** Used to extract a regexp from its `source` property */
* Used for `templateSettings` properties such as `escape`, `evaluate`, var reSource = /^\/([\s\S]+?)\/[gim]+?$/;
* or `interpolate` with explicitly assigned falsey values to ensure no match
* is made.
*/
var reNoMatch = /.^/;
/** Used to replace escaped characters with their unescaped counterpart */
var reUnescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
/** Object#toString result shortcuts */ /** Object#toString result shortcuts */
var arrayClass = '[object Array]', var arrayClass = '[object Array]',
@@ -65,6 +51,7 @@
/** Native method shortcuts */ /** Native method shortcuts */
var concat = ArrayProto.concat, var concat = ArrayProto.concat,
hasOwnProperty = ObjProto.hasOwnProperty, hasOwnProperty = ObjProto.hasOwnProperty,
join = ArrayProto.join,
push = ArrayProto.push, push = ArrayProto.push,
slice = ArrayProto.slice, slice = ArrayProto.slice,
toString = ObjProto.toString; toString = ObjProto.toString;
@@ -225,6 +212,19 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/**
* Used by `String#replace` to escape characters for inclusion in compiled
* string literals while skipping over template delimiters.
*
* @private
* @param {String} character The the character to escape.
* @param {String} [delimiter=''] The delimiter to skip over.
* @returns {String} Returns an escaped character or template delimiter.
*/
function escapeString(character, delimiter) {
return delimiter || '\\' + escapes[character];
}
/** /**
* Compiles iteration functions. * Compiles iteration functions.
* *
@@ -324,21 +324,6 @@
indexOf, Infinity, isArray, isEmpty, Math, slice, stringClass, toString); indexOf, Infinity, isArray, isEmpty, Math, slice, stringClass, toString);
} }
/**
* Unescapes characters, previously escaped for inclusion in compiled string
* literals, so they may compiled into function bodies.
* (Used for template interpolation, evaluation, or escaping)
*
* @private
* @param {String} string The string to unescape.
* @returns {String} Returns the unescaped string.
*/
function unescape(string) {
return string.replace(reUnescaper, function(match, escaped) {
return escapes[escaped];
});
}
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** /**
@@ -2594,45 +2579,66 @@
function template(text, data, options) { function template(text, data, options) {
options = defaults(options || {}, lodash.templateSettings); options = defaults(options || {}, lodash.templateSettings);
// Compile the template source, taking care to escape characters that var result,
// cannot be included in string literals and then unescape them in code reEscapeDelimiter = options.escape,
// blocks. reEvaluateDelimiter = options.evaluate,
var source = "__p+='" + text reInterpolateDelimiter = options.interpolate,
.replace(reEscaper, function(match) { id = reEscapeDelimiter + reEvaluateDelimiter + reInterpolateDelimiter,
return '\\' + escapes[match]; reUnescaped = reCache[id],
}) variable = options.variable;
.replace(options.escape || reNoMatch, function(match, code) {
return "'+\n((__t=(" + unescape(code) + "))==null?'':_.escape(__t))+\n'";
})
.replace(options.interpolate || reNoMatch, function(match, code) {
return "'+\n((__t=(" + unescape(code) + "))==null?'':__t)+\n'";
})
.replace(options.evaluate || reNoMatch, function(match, code) {
return "';\n" + unescape(code) + ";\n__p+='";
}) + "';\n";
// if a variable is not specified, place data values in local scope // create and cache the `reUnescaped` regexp
if (!options.variable) { if (!reUnescaped) {
source = 'with (object || {}) {\n' + source + '\n}\n'; reUnescaped = [];
if (reEscapeDelimiter) {
reUnescaped.push(reSource.exec(reEscapeDelimiter)[1]);
}
if (reEvaluateDelimiter) {
reUnescaped.push(reSource.exec(reEvaluateDelimiter)[1]);
}
if (reInterpolateDelimiter) {
reUnescaped.push(reSource.exec(reInterpolateDelimiter)[1]);
}
reUnescaped = reCache[id] = RegExp('\\\\|\'|\\r|\\n|\\t|\\u2028|\\u2029' +
(reUnescaped.length ? '|((?:' + reUnescaped.join(')|(?:') + '))' : ''), 'g');
} }
source = 'var __t, __j = Array.prototype.join, __p = "";' + // escape characters that cannot be included in string literals
text = text.replace(reUnescaped, escapeString);
if (reEscapeDelimiter) {
text = text.replace(reEscapeDelimiter, "'+\n((__t=($1))==null?'':__e(__t))+\n'")
}
if (reInterpolateDelimiter) {
text = text.replace(reInterpolateDelimiter, "'+\n((__t=($1))==null?'':__t)+\n'");
}
if (reEvaluateDelimiter) {
text = text.replace(reEvaluateDelimiter, "';\n$1;\n__p+='");
}
text = "__p='" + text + "';\n";
// if `options.variable` is not specified, add `data` to the top of the scope chain
if (!variable) {
text = 'with (object || {}) {\n' + text + '\n}\n';
}
text = 'function(' + (variable || 'object') + '){\n' +
'var __p, __t;\n' +
'function print() { __p += __j.call(arguments, "") }\n' + 'function print() { __p += __j.call(arguments, "") }\n' +
source + 'return __p'; text +
'return __p\n}';
result = Function('_,__e,__j', 'return ' + text)(lodash, escape, join);
var render = Function(options.variable || 'object', '_', source);
if (data) { if (data) {
return render(data, lodash); return result(data);
} }
// provide the compiled function's source via its `toString()` method, in
var template = function(data) { // supported environments, or the `source` property as a convenience for
return render.call(this, data, lodash); // build time precompilation
}; result.source = text;
return result;
// provide the compiled function source as a convenience for build time precompilation
template.source = 'function(' + (options.variable || 'object') + '){\n' + source + '\n}';
return template;
} }
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/