From b55d7b6691c83e7ad751452b9e04aadfe718c702 Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Wed, 7 Oct 2015 09:30:34 +0200 Subject: [PATCH] * support more property target types (Symbols, Numbers, ...) * unify symbol/string key handling --- index.js | 30 +++++++++++----------- test.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index ca333d0..858461c 100644 --- a/index.js +++ b/index.js @@ -11,11 +11,21 @@ function toObject(val) { return Object(val); } -function isFunc(val) { - return typeof val === 'function'; +function assignKey(to, from, key) { + var val = from[key]; + + if (typeof val === 'undefined' || val === null) { + return; + } + + if (typeof to[key] === 'undefined' || !isObj(val)) { + to[key] = val; + } else { + to[key] = assign(Object(to[key]), from[key]); + } } -function base(to, from) { +function assign(to, from) { if (to === from) { return to; } @@ -24,15 +34,7 @@ function base(to, from) { for (var key in from) { if (hasOwnProperty.call(from, key)) { - var val = from[key]; - - if (Array.isArray(val)) { - to[key] = val.slice(); - } else if (isObj(val) && !isFunc(val)) { - to[key] = base(to[key] || {}, val); - } else if (val !== undefined) { - to[key] = val; - } + assignKey(to, from, key); } } @@ -41,7 +43,7 @@ function base(to, from) { for (var i = 0; i < symbols.length; i++) { if (propIsEnumerable.call(from, symbols[i])) { - to[symbols[i]] = from[symbols[i]]; + assignKey(to, from, symbols[i]); } } } @@ -53,7 +55,7 @@ module.exports = function deepAssign(target) { target = toObject(target); for (var s = 1; s < arguments.length; s++) { - base(target, arguments[s]); + assign(target, arguments[s]); } return target; diff --git a/test.js b/test.js index f241360..10ef1fd 100644 --- a/test.js +++ b/test.js @@ -8,6 +8,76 @@ test('assign own enumerable propreties from source to target object', t => { t.end(); }); +test('do not assign null values', t => { + t.same(fn({}, {foo: null}), {}); + t.end(); +}); + +test('assign values to null targets', t => { + t.same(fn({foo: null}, {foo: {}}), {foo: {}}); + t.end(); +}); + +test('do not assign undefined values', t => { + t.same(fn({}, {foo: undefined}), {}); + t.end(); +}); + +test('assign values to undefined targets', t => { + t.same(fn({foo: undefined}, {foo: {}}), {foo: {}}); + t.end(); +}); + +test('support numbers as targets', t => { + var target = fn({answer: 42}, {answer: {rainbows: 'many'}}); + t.is(target.answer / 7, 6); + t.is(target.answer.constructor, Number); + t.is(target.answer.rainbows, 'many'); + t.end(); +}); + +test('support boolean as targets', t => { + var target = fn({foo: true}, {foo: {rainbows: 'many'}}); + t.is(target.foo.toString(), 'true'); + t.is(target.foo.constructor, Boolean); + t.is(target.foo.rainbows, 'many'); + t.end(); +}); + +test('support strings as targets', t => { + var target = fn({rainbows: 'many'}, {rainbows: {answer: 42}}); + t.is('' + target.rainbows, 'many'); + t.is(target.rainbows.constructor, String); + t.is(target.rainbows.answer, 42); + t.end(); +}); + +test('support arrays as targets', t => { + var target = {a: ['many']}; + var source = {a: []}; + source.a[2] = 'unicorns'; + fn(target, source, {a: {answer: 42}}); + t.is(target.a[0], 'many'); + t.is(target.a[1], undefined); + t.is(target.a[2], 'unicorns'); + t.is(target.a.constructor, Array); + t.is(target.a.answer, 42); + t.end(); +}); + +test('support functions', t => { + var oracle42 = () => 42; + var oracle666 = () => 666; + oracle42.foo = true; + oracle42.bar = true; + oracle666.bar = false; + var target = fn({}, {oracle: oracle42}, {oracle: oracle666}); + t.is(target.oracle(), 42); + t.is(target.oracle.foo, true); + t.is(target.oracle.bar, false); + t.end(); +}); + test('support multiple sources', t => { t.same(fn({foo: 0}, {bar: 1}, {bar: 2}), {foo: 0, bar: 2}); t.same(fn({}, {}, {foo: 1}), {foo: 1}); @@ -93,6 +163,13 @@ if (typeof Symbol !== 'undefined') { t.end(); }); + test('support symbols as targets', t => { + var target = fn({sym: Symbol('foo')}, {sym: {rainbows: 'many'}}); + t.is(target.sym.constructor, Symbol); + t.is(target.sym.rainbows, 'many'); + t.end(); + }); + test('only copy enumerable symbols', t => { var target = {}; var source = {};