diff --git a/src/Angular.js b/src/Angular.js index 77ab32aa1e9d..e57387336e41 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -881,22 +881,10 @@ function copy(source, destination) { } var needsRecurse = false; - var destination; + var destination = copyType(source); - if (isArray(source)) { - destination = []; - needsRecurse = true; - } else if (isTypedArray(source)) { - destination = new source.constructor(source); - } else if (isDate(source)) { - destination = new Date(source.getTime()); - } else if (isRegExp(source)) { - destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); - destination.lastIndex = source.lastIndex; - } else if (isFunction(source.cloneNode)) { - destination = source.cloneNode(true); - } else { - destination = Object.create(getPrototypeOf(source)); + if (destination === undefined) { + destination = isArray(source) ? [] : Object.create(getPrototypeOf(source)); needsRecurse = true; } @@ -907,6 +895,36 @@ function copy(source, destination) { ? copyRecurse(source, destination) : destination; } + + function copyType(source) { + switch (toString.call(source)) { + case '[object Int8Array]': + case '[object Int16Array]': + case '[object Int32Array]': + case '[object Float32Array]': + case '[object Float64Array]': + case '[object Uint8Array]': + case '[object Uint8ClampedArray]': + case '[object Uint16Array]': + case '[object Uint32Array]': + return new source.constructor(source); + + case '[object Boolean]': + case '[object Number]': + case '[object String]': + case '[object Date]': + return new source.constructor(source.valueOf()); + + case '[object RegExp]': + var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); + re.lastIndex = source.lastIndex; + return re; + } + + if (isFunction(source.cloneNode)) { + return source.cloneNode(true); + } + } } /** diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 22773cf9c7fd..41b6493a73ed 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -471,6 +471,45 @@ describe('angular', function() { expect(dest.c).toBe(3); expect(Object.keys(dest)).toEqual(['a', 'b', 'c']); }); + + it('should copy String() objects', function() { + /*jshint -W053 */ + var obj = new String('foo'); + /*jshint +W053 */ + var dest = copy(obj); + expect(dest).not.toBe(obj); + expect(isObject(dest)).toBe(true); + expect(dest.valueOf()).toBe(obj.valueOf()); + }); + + it('should copy Boolean() objects', function() { + /*jshint -W053 */ + var obj = new Boolean(true); + /*jshint +W053 */ + var dest = copy(obj); + expect(dest).not.toBe(obj); + expect(isObject(dest)).toBe(true); + expect(dest.valueOf()).toBe(obj.valueOf()); + }); + + it('should copy Number() objects', function() { + /*jshint -W053 */ + var obj = new Number(42); + /*jshint +W053 */ + var dest = copy(obj); + expect(dest).not.toBe(obj); + expect(isObject(dest)).toBe(true); + expect(dest.valueOf()).toBe(obj.valueOf()); + }); + + it('should copy falsy String/Boolean/Number objects', function() { + /*jshint -W053 */ + expect(copy(new String('')).valueOf()).toBe(''); + expect(copy(new Boolean(false)).valueOf()).toBe(false); + expect(copy(new Number(0)).valueOf()).toBe(0); + expect(copy(new Number(NaN)).valueOf()).toBeNaN(); + /*jshint +W053 */ + }); }); describe("extend", function() {