diff --git a/build.js b/build.js index c84c9be5b..cf3c2df4d 100755 --- a/build.js +++ b/build.js @@ -1148,6 +1148,27 @@ return source; } + /** + * Removes all `support.enumErrorProps` references from `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function removeSupportEnumErrorProps(source) { + source = removeSupportProp(source, 'enumErrorProps'); + + // remove `support.enumErrorProps` from `iteratorTemplate` + source = source.replace(getIteratorTemplate(source), function(match) { + return match + .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(support\.enumErrorProps *(?:&&|\))(.+?}["']|[\s\S]+?<% *} *(?:%>|["'])).+/g, '') + .replace(/support\.enumErrorProps\s*\|\|\s*/g, ''); + }); + + return source; + } + + /** * Removes all `support.enumPrototypes` references from `source`. * @@ -1168,7 +1189,7 @@ // remove `support.enumPrototypes` from `iteratorTemplate` source = source.replace(getIteratorTemplate(source), function(match) { return match - .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(support\.enumPrototypes *(?:&&|\))[\s\S]+?<% *} *(?:%>|["']).+/g, '') + .replace(/(?: *\/\/.*\n)* *["'] *(?:<% *)?if *\(support\.enumPrototypes *(?:&&|\))(.+?}["']|[\s\S]+?<% *} *(?:%>|["'])).+/g, '') .replace(/support\.enumPrototypes\s*\|\|\s*/g, ''); }); @@ -1249,7 +1270,9 @@ // remove `support.nonEnumShadows` from `iteratorTemplate` source = source.replace(getIteratorTemplate(source), function(match) { - return match.replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(support\.nonEnumShadows[\s\S]+?["']\1<% *} *%>.+/, ''); + return match + .replace(/\s*\|\|\s*support\.nonEnumShadows/, '') + .replace(/(?: *\/\/.*\n)* *["']( *)<% *if *\(support\.nonEnumShadows[\s\S]+?["']\1<% *} *%>.+/, ''); }); return source; @@ -1986,6 +2009,7 @@ source = removeSupportNodeClass(source); if (!isMobile) { + source = removeSupportEnumErrorProps(source); source = removeSupportEnumPrototypes(source); source = removeSupportNonEnumArgs(source); diff --git a/build/pre-compile.js b/build/pre-compile.js index ffe53a78e..cd2b3a8b2 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -13,6 +13,7 @@ 'callback', 'className', 'collection', + 'conditions', 'ctor', 'ctorByClass', 'errorClass', @@ -36,8 +37,8 @@ 'objectTypes', 'ownIndex', 'ownProps', - 'proto', 'result', + 'skipErrorProps', 'skipProto', 'source', 'stringClass', @@ -112,6 +113,7 @@ 'difference', 'drop', 'each', + 'enumErrorProps', 'enumPrototypes', 'environment', 'escape', @@ -290,7 +292,7 @@ string = string.slice(left.length); } // avoids removing the '\n' of the `stringEscapes` object - string = string.replace(/\[object |delete |else (?!{)|function | in |return\s+[\w"']|throw |typeof |use strict|var |@ |(["'])\\n\1|\\\\n|\\n|\s+/g, function(match) { + string = string.replace(/\[object |delete |else (?!{)|function | in | instanceof |return\s+[\w"']|throw |typeof |use strict|var |@ |(["'])\\n\1|\\\\n|\\n|\s+/g, function(match) { return match == false || match == '\\n' ? '' : match; }); // unclip diff --git a/lodash.js b/lodash.js index d55cec4e9..f97cf1d88 100644 --- a/lodash.js +++ b/lodash.js @@ -191,6 +191,7 @@ getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, hasOwnProperty = objectProto.hasOwnProperty, push = arrayProto.push, + propertyIsEnumerable = objectProto.propertyIsEnumerable, setImmediate = context.setImmediate, setTimeout = context.setTimeout, toString = objectProto.toString; @@ -346,6 +347,14 @@ */ support.argsClass = isArguments(arguments); + /** + * Detect if `name` or `message` properties of `Error.prototype` are + * enumerable by default. (IE < 9, Safari < 5.1) + * + * @type Boolean + */ + support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name'); + /** * Detect if `prototype` properties are enumerable by default. * @@ -357,7 +366,7 @@ * @memberOf _.support * @type Boolean */ - support.enumPrototypes = ctor.propertyIsEnumerable('prototype'); + support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype'); /** * Detect if `Function#bind` exists and is inferred to be fast (all but V8). @@ -529,7 +538,7 @@ // iterate over the array-like value ' while (++index < length) {\n' + - ' <%= loop %>\n' + + ' <%= loop %>;\n' + ' }\n' + '}\n' + 'else {' + @@ -541,7 +550,7 @@ ' if (length && isArguments(iterable)) {\n' + ' while (++index < length) {\n' + " index += '';\n" + - ' <%= loop %>\n' + + ' <%= loop %>;\n' + ' }\n' + ' } else {' + ' <% } %>' + @@ -551,29 +560,37 @@ " var skipProto = typeof iterable == 'function';\n" + ' <% } %>' + - // iterate own properties using `Object.keys` if it's fast + // avoid iterating over `Error.prototype` properties in older IE and Safari + ' <% if (support.enumErrorProps || support.nonEnumShadows) { %>\n' + + ' var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n' + + ' <% } %>' + + + // define conditions used in the loop + ' <%' + + ' var conditions = [];' + + ' if (support.enumPrototypes) { conditions.push("!(skipProto && index == \'prototype\')"); }' + + ' if (support.enumErrorProps) { conditions.push("!(skipErrorProps && (index == \'message\' || index == \'name\'))"); }' + + ' %>' + + + // iterate own properties using `Object.keys` ' <% if (useHas && useKeys) { %>\n' + ' var ownIndex = -1,\n' + ' ownProps = objectTypes[typeof iterable] ? keys(iterable) : [],\n' + ' length = ownProps.length;\n\n' + ' while (++ownIndex < length) {\n' + - ' index = ownProps[ownIndex];\n' + - " <% if (support.enumPrototypes) { %>if (!(skipProto && index == 'prototype')) {\n <% } %>" + - ' <%= loop %>\n' + - ' <% if (support.enumPrototypes) { %>}\n<% } %>' + + ' index = ownProps[ownIndex];\n<%' + + " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" + + ' <%= loop %>;' + + ' <% if (conditions.length) { %>\n }<% } %>\n' + ' }' + // else using a for-in loop ' <% } else { %>\n' + - ' for (index in iterable) {<%' + - ' if (support.enumPrototypes || useHas) { %>\n if (<%' + - " if (support.enumPrototypes) { %>!(skipProto && index == 'prototype')<% }" + - ' if (support.enumPrototypes && useHas) { %> && <% }' + - ' if (useHas) { %>hasOwnProperty.call(iterable, index)<% }' + - ' %>) {' + - ' <% } %>\n' + + ' for (index in iterable) {\n<%' + + ' if (useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); }' + + " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" + ' <%= loop %>;' + - ' <% if (support.enumPrototypes || useHas) { %>\n }<% } %>\n' + + ' <% if (conditions.length) { %>\n }<% } %>\n' + ' }' + // Because IE < 9 can't set the `[[Enumerable]]` attribute of an @@ -583,19 +600,15 @@ ' <% if (support.nonEnumShadows) { %>\n\n' + ' if (iterable !== objectProto) {\n' + " var ctor = iterable.constructor,\n" + - ' proto = ctor && ctor.prototype,\n' + - ' isProto = iterable === proto,\n' + - ' nonEnum = nonEnumProps[objectClass];\n\n' + - ' if (isProto) {\n' + - " var className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n" + - ' nonEnum = nonEnumProps[iterable === (ctorByClass[className] && ctorByClass[className].prototype) ? className : objectClass];\n' + - ' }\n' + + ' isProto = iterable === (ctor && ctor.prototype),\n' + + ' className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n' + + ' nonEnum = nonEnumProps[className];\n' + ' <% for (k = 0; k < 7; k++) { %>\n' + " index = '<%= shadowedProps[k] %>';\n" + ' if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))<%' + ' if (!useHas) { %> || (!nonEnum[index] && iterable[index] !== objectProto[index])<% }' + ' %>) {\n' + - ' <%= loop %>\n' + + ' <%= loop %>;\n' + ' }' + ' <% } %>\n' + ' }' +