From f3bec4fc3792b7fa1ba5edc5262350689a36df0b Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 2 Sep 2012 02:35:02 -0700 Subject: [PATCH] Ensure optimized `isPlainObject` works with objects from other documents. Former-commit-id: 2f782b3dfc19e7ea3274132c31cd408ee2387021 --- build.js | 13 ------- lodash.js | 94 ++++++++++++++++++++++++++++--------------------- test/index.html | 2 +- test/test.js | 37 ++++++++++--------- 4 files changed, 75 insertions(+), 71 deletions(-) diff --git a/build.js b/build.js index 36d54591d..c2b47d9b7 100755 --- a/build.js +++ b/build.js @@ -572,17 +572,6 @@ return source.replace(/(?:\s*\/\/.*)*\n( +)if *\(isFunction\(\/x\/[\s\S]+?};\n\1}/, ''); } - /** - * Removes the `isPlainObject` fallback from `source`. - * - * @private - * @param {String} source The source to process. - * @returns {String} Returns the source with the `isPlainObject` fallback removed. - */ - function removeIsPlainObjectFallback(source) { - return source.replace(/(?:\s*\/\/.*)*\n( +)if *\(!isPlainObject[\s\S]+?};\n\1}/, ''); - } - /** * Removes the `Object.keys` object iteration optimization from `source`. * @@ -1003,7 +992,6 @@ if (!isUnderscore) { source = removeIsArgumentsFallback(source); - source = removeIsPlainObjectFallback(source); source = removeNoArgsClass(source); } @@ -1131,7 +1119,6 @@ source = removeVar(source, 'reNative'); } if (isRemoved(source, 'createIterator', 'clone', 'merge')) { - source = removeIsPlainObjectFallback(source); source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast;|.+?iteratesOwnLast *=.+/g, ''); } if (isRemoved(source, 'createIterator', 'isEqual')) { diff --git a/lodash.js b/lodash.js index 45e920d36..f42e4234f 100644 --- a/lodash.js +++ b/lodash.js @@ -965,6 +965,49 @@ }; } + /** + * A fallback implementation of `isPlainObject`. + * + * @private + * @param {Mixed} value The value to check. + * @param {Boolean} [skipArgsCheck=false] Internally used to skip checks for + * `arguments` objects. + * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, + * else `false`. + */ + function isPlainFallback(value, skipArgsCheck) { + // avoid non-objects and false positives for `arguments` objects + var result = false; + if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) { + return result; + } + // IE < 9 presents DOM nodes as `Object` objects except they have `toString` + // methods that are `typeof` "string" and still can coerce nodes to strings. + // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) + var ctor = value.constructor; + if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && + (!isFunction(ctor) || ctor instanceof ctor)) { + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + if (iteratesOwnLast) { + forIn(value, function(objValue, objKey) { + result = !hasOwnProperty.call(value, objKey); + return false; + }); + return result === false; + } + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + forIn(value, function(objValue, objKey) { + result = objKey; + }); + return result === false || hasOwnProperty.call(value, result); + } + return result; + } + /** * Checks if a given `value` is an object created by the `Object` constructor * assuming objects created by the `Object` constructor have no inherited @@ -977,46 +1020,17 @@ * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, * else `false`. */ - function isPlainObject(value, skipArgsCheck) { - return value - ? value == ObjectProto || (value.__proto__ == ObjectProto && (skipArgsCheck || !isArguments(value))) - : false; - } - // fallback for IE - if (!isPlainObject(objectTypes)) { - isPlainObject = function(value, skipArgsCheck) { - // avoid non-objects and false positives for `arguments` objects - var result = false; - if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) { - return result; - } - // IE < 9 presents DOM nodes as `Object` objects except they have `toString` - // methods that are `typeof` "string" and still can coerce nodes to strings. - // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) - var ctor = value.constructor; - if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && - (!isFunction(ctor) || ctor instanceof ctor)) { - // IE < 9 iterates inherited properties before own properties. If the first - // iterated property is an object's own property then there are no inherited - // enumerable properties. - if (iteratesOwnLast) { - forIn(value, function(objValue, objKey) { - result = !hasOwnProperty.call(value, objKey); - return false; - }); - return result === false; - } - // In most environments an object's own properties are iterated before - // its inherited properties. If the last iterated property is an object's - // own property then there are no inherited enumerable properties. - forIn(value, function(objValue, objKey) { - result = objKey; - }); - return result === false || hasOwnProperty.call(value, result); - } - return result; - }; - } + var isPlainObject = objectTypes.__proto__ != ObjectProto ? isPlainFallback : function(value, skipArgsCheck) { + if (!value) { + return false; + } + var valueOf = value.valueOf, + objProto = typeof valueOf == 'function' && valueOf.__proto__.__proto__; + + return objProto + ? value == objProto || (value.__proto__ == objProto && (skipArgsCheck || !isArguments(value))) + : isPlainFallback(value); + }; /** * A shim implementation of `Object.keys` that produces an array of the given diff --git a/test/index.html b/test/index.html index 102e0c700..f0893a766 100644 --- a/test/index.html +++ b/test/index.html @@ -33,7 +33,7 @@ delete Object._keys; // set to test `_.noConflict` - _ = 1; + _ = {}; // load Lo-Dash again to overwrite the existing `_` value document.write('