feat(isArray): support Array subclasses in angular.isArray()

Closes #15533
Closes #15541

BREAKING CHANGE:

Previously, `angular.isArray()` was an alias for `Array.isArray()`.
Therefore, objects that prototypally inherit from `Array` where not
considered arrays. Now such objects are considered arrays too.

This change affects several other methods that use `angular.isArray()`
under the hood, such as `angular.copy()`, `angular.equals()`,
`angular.forEach()`, and `angular.merge()`.

This in turn affects how dirty checking treats objects that prototypally
inherit from `Array` (e.g. MobX observable arrays). AngularJS will now
be able to handle these objects better when copying or watching.
This commit is contained in:
Georgii Dolzhykov
2016-12-22 19:17:06 +03:00
committed by George Kalpakas
parent 67f54b6600
commit e3ece2fad9
2 changed files with 36 additions and 4 deletions
+5 -4
View File
@@ -219,8 +219,7 @@ function isArrayLike(obj) {
// NodeList objects (with `item` method) and
// other objects with suitable length characteristics are array-like
return isNumber(length) &&
(length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function');
return isNumber(length) && (length >= 0 && (length - 1) in obj || typeof obj.item === 'function');
}
@@ -635,12 +634,14 @@ function isDate(value) {
* @kind function
*
* @description
* Determines if a reference is an `Array`. Alias of Array.isArray.
* Determines if a reference is an `Array`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Array`.
*/
var isArray = Array.isArray;
function isArray(arr) {
return Array.isArray(arr) || arr instanceof Array;
}
/**
* @description
+31
View File
@@ -1254,6 +1254,37 @@ describe('angular', function() {
});
});
describe('isArray', function() {
it('should return true if passed an `Array`', function() {
expect(isArray([])).toBe(true);
});
it('should return true if passed an `Array` from a different window context', function() {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe); // No `contentWindow` if not attached to the DOM.
var arr = new iframe.contentWindow.Array();
document.body.removeChild(iframe); // Clean up.
expect(arr instanceof Array).toBe(false);
expect(isArray(arr)).toBe(true);
});
it('should return true if passed an object prototypically inherited from `Array`', function() {
function FooArray() {}
FooArray.prototype = [];
expect(isArray(new FooArray())).toBe(true);
});
it('should return false if passed non-array objects', function() {
expect(isArray(document.body.childNodes)).toBe(false);
expect(isArray({length: 0})).toBe(false);
expect(isArray({length: 2, 0: 'one', 1: 'two'})).toBe(false);
});
});
describe('isArrayLike', function() {
it('should return false if passed a number', function() {