From 02e743cf64358748fca6b488d68322893f49d9a2 Mon Sep 17 00:00:00 2001 From: Jack Viers Date: Fri, 8 Nov 2013 12:59:28 -0600 Subject: [PATCH] fix(Angular.js): fix isArrayLike for extends Add spec for isArrayLike Add Arguments detection Convert NodeList detection to use hasChildren Prepares a single boolean return instead of several if return boolean statements 1. Convert if-return boolean statements to single boolean return. 2. Assign boolean conditionals to break up the long boolean return and avoid repeated calls. 3. Declare variables at the beginning of function to avoid hoisting errors. Break long lines into indented and aligned expressions. --- src/Angular.js | 50 ++++++++++++++++++++++++++++++++++++++++++--- test/AngularSpec.js | 29 ++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/Angular.js b/src/Angular.js index 797e4b811ed5..91ba0331d3de 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -177,9 +177,53 @@ function isArrayLike(obj) { return true; } - return isString(obj) || isArray(obj) || length === 0 || + return isString(obj) || isArray(obj) || (!isObject(obj) && length === 0) || typeof length === 'number' && length > 0 && (length - 1) in obj; } +function isArrayLike(obj) { + // snake case is to avoid shadowing camel-cased globals + var length, objExists, isNodeList, isArguments, isSomeOtherObj, is_array, is_string, is_object; + objExists = isDefined(obj) && obj !== null; + length = objExists ? obj.length : false; + is_array = isArray(obj); + is_string = isString(obj); + is_object = isObject(obj); + isNodeList = objExists && obj.nodeType === 1 && length; + isArguments = objExists && + (Object.prototype.toString.call(obj) === '[object Arguments]' || + (Object.prototype.hasOwnProperty.call(obj, 'length') && + Object.prototype.hasOwnProperty.call(obj, 'callee'))); + + // this only works if it doesn't return 'object' from typeof and isn't another arrayLike + isSomeOtherObj = objExists && + !isNodeList && + !is_array && + !is_string && + !isArguments && + ( + (!is_object && + length === 0) || + ( + isNumber(length) && + length >= 0 && + (length - 1) in obj + ) + ); + + return ( + objExists && + !isWindow(obj) && + ( + ( + isNodeList || + is_string || + is_array || + isArguments + ) || + isSomeOtherObj + ) + ); +} /** * @ngdoc function @@ -210,7 +254,7 @@ function isArrayLike(obj) { */ function forEach(obj, iterator, context) { var key; - if (obj) { + if (isDefined(obj) && obj !== null) { if (isFunction(obj)){ for (key in obj) { if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { @@ -224,7 +268,7 @@ function forEach(obj, iterator, context) { iterator.call(context, obj[key], key); } else { for (key in obj) { - if (obj.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { iterator.call(context, obj[key], key); } } diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 1b08a18e078d..824a49a96ea4 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -159,6 +159,23 @@ describe('angular', function() { expect(hashKey(dst)).not.toEqual(hashKey(src)); }); + it('should copy the properties of the source object onto the destination object', function(){ + var destination, source; + destination = {}; + source = {foo: true}; + destination = extend(destination, source); + expect(isDefined(destination.foo)).toBe(true); + }); + + it('ISSUE #4751 - should copy the length property of an object source to the destination object', function(){ + var destination, source; + destination = {}; + source = {radius: 30, length: 0}; + destination = extend(destination, source); + expect(isDefined(destination.length)).toBe(true); + expect(isDefined(destination.radius)).toBe(true); + }); + it('should retain the previous $$hashKey', function() { var src,dst,h; src = {}; @@ -439,6 +456,18 @@ describe('angular', function() { }); }); + describe('isArrayLike', function() { + it ('should return false if passed a number', function(){ + expect(isArrayLike(10)).toBe(false) + }); + it('should return true if passed an array', function() { + expect(isArrayLike([1,2,3,4])).toBe(true); + }); + it('should return true if passed an object', function(){ + expect(isArrayLike({0:"test", 1:"bob", 2:"tree", length:3})).toBe(true); + }); + }); + describe('forEach', function() { it('should iterate over *own* object properties', function() {