Ensure optimized isPlainObject works with objects from other documents.

Former-commit-id: 2f782b3dfc19e7ea3274132c31cd408ee2387021
This commit is contained in:
John-David Dalton
2012-09-02 02:35:02 -07:00
parent c2117ef4fd
commit f3bec4fc37
4 changed files with 75 additions and 71 deletions

View File

@@ -572,17 +572,6 @@
return source.replace(/(?:\s*\/\/.*)*\n( +)if *\(isFunction\(\/x\/[\s\S]+?};\n\1}/, ''); 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`. * Removes the `Object.keys` object iteration optimization from `source`.
* *
@@ -1003,7 +992,6 @@
if (!isUnderscore) { if (!isUnderscore) {
source = removeIsArgumentsFallback(source); source = removeIsArgumentsFallback(source);
source = removeIsPlainObjectFallback(source);
source = removeNoArgsClass(source); source = removeNoArgsClass(source);
} }
@@ -1131,7 +1119,6 @@
source = removeVar(source, 'reNative'); source = removeVar(source, 'reNative');
} }
if (isRemoved(source, 'createIterator', 'clone', 'merge')) { if (isRemoved(source, 'createIterator', 'clone', 'merge')) {
source = removeIsPlainObjectFallback(source);
source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast;|.+?iteratesOwnLast *=.+/g, ''); source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast;|.+?iteratesOwnLast *=.+/g, '');
} }
if (isRemoved(source, 'createIterator', 'isEqual')) { if (isRemoved(source, 'createIterator', 'isEqual')) {

View File

@@ -966,9 +966,7 @@
} }
/** /**
* Checks if a given `value` is an object created by the `Object` constructor * A fallback implementation of `isPlainObject`.
* assuming objects created by the `Object` constructor have no inherited
* enumerable properties and that there are no `Object.prototype` extensions.
* *
* @private * @private
* @param {Mixed} value The value to check. * @param {Mixed} value The value to check.
@@ -977,14 +975,7 @@
* @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object,
* else `false`. * else `false`.
*/ */
function isPlainObject(value, skipArgsCheck) { function isPlainFallback(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 // avoid non-objects and false positives for `arguments` objects
var result = false; var result = false;
if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) { if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) {
@@ -1015,9 +1006,32 @@
return result === false || hasOwnProperty.call(value, result); return result === false || hasOwnProperty.call(value, result);
} }
return 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
* enumerable properties and that there are no `Object.prototype` extensions.
*
* @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`.
*/
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 * A shim implementation of `Object.keys` that produces an array of the given
* object's own enumerable property names. * object's own enumerable property names.

View File

@@ -33,7 +33,7 @@
delete Object._keys; delete Object._keys;
// set to test `_.noConflict` // set to test `_.noConflict`
_ = 1; _ = {};
// load Lo-Dash again to overwrite the existing `_` value // load Lo-Dash again to overwrite the existing `_` value
document.write('<script src="../' + QUnit.config.lodashFilename + '.js"><\/script>'); document.write('<script src="../' + QUnit.config.lodashFilename + '.js"><\/script>');

View File

@@ -83,6 +83,23 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
// add object from iframe
(function() {
if (!window.document) {
return;
}
var body = document.body,
iframe = document.createElement('iframe');
iframe.frameBorder = iframe.height = iframe.width = 0;
body.appendChild(iframe);
var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc;
idoc.write("<script>parent._._object = { 'a': 1, 'b': 2, 'c': 3 };<\/script>");
idoc.close();
}());
/*--------------------------------------------------------------------------*/
// explicitly call `QUnit.module()` instead of `module()` // explicitly call `QUnit.module()` instead of `module()`
// in case we are in a CLI environment // in case we are in a CLI environment
QUnit.module('lodash'); QUnit.module('lodash');
@@ -191,6 +208,7 @@
'boolean object': Object(false), 'boolean object': Object(false),
'an object': { 'a': 0, 'b': 1, 'c': 3 }, 'an object': { 'a': 0, 'b': 1, 'c': 3 },
'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } }, 'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } },
'an object from another document': _._object || {},
'null': null, 'null': null,
'a number': 3, 'a number': 3,
'a number object': Object(3), 'a number object': Object(3),
@@ -205,12 +223,8 @@
_.forOwn(objects, function(object, key) { _.forOwn(objects, function(object, key) {
test('should deep clone ' + key + ' correctly', function() { test('should deep clone ' + key + ' correctly', function() {
var clone = _.clone(object, true); var clone = _.clone(object, true);
ok(_.isEqual(object, clone));
if (object == null) {
equal(clone, object);
} else {
deepEqual(clone.valueOf(), object.valueOf());
}
if (_.isObject(object)) { if (_.isObject(object)) {
ok(clone !== object); ok(clone !== object);
} else { } else {
@@ -747,21 +761,10 @@
}); });
test('should return `true` for like-objects from different documents', function() { test('should return `true` for like-objects from different documents', function() {
if (window.document) {
var body = document.body,
iframe = document.createElement('iframe'),
object = { 'a': 1, 'b': 2, 'c': 3 };
body.appendChild(iframe);
var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc;
idoc.write("<script>parent._._object = { 'a': 1, 'b': 2, 'c': 3 };<\/script>");
idoc.close();
}
// ensure `_._object` is assigned (unassigned in Opera 10.00) // ensure `_._object` is assigned (unassigned in Opera 10.00)
if (_._object) { if (_._object) {
var object = { 'a': 1, 'b': 2, 'c': 3 };
equal(_.isEqual(object, _._object), true); equal(_.isEqual(object, _._object), true);
body.removeChild(iframe);
delete _._object;
} }
else { else {
skipTest(); skipTest();