Skip to content

Commit

Permalink
v0.4.0: fixed array cloning, check for own property, etc. (see more i…
Browse files Browse the repository at this point in the history
…n CHANGELOG.md)
  • Loading branch information
unclechu committed May 1, 2015
1 parent bf49140 commit 9ea374c
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 106 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Changelog
=========

v0.4.0
------

Broken backward compatibility with v0.3.x

- Fixed bug with extending arrays instead of cloning;
- Deep cloning for arrays;
- Check for own property;
- Fixed some documentation issues;
- Strict JS mode.
39 changes: 34 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ Recursive object extending.
[![NPM](https://nodei.co/npm-dl/deep-extend.png?height=3)](https://nodei.co/npm/deep-extend/)

Install
-----
-------

npm install deep-extend
```bash
$ npm install deep-extend
```

Usage
-----

```javascript
var deepExtend = require('deep-extend');
var obj1 = {
a: 1,
Expand All @@ -25,7 +28,8 @@ Usage
},
f: 5,
g: 123,
i: 321
i: 321,
j: [1, 2]
};
var obj2 = {
b: 3,
Expand All @@ -38,7 +42,8 @@ Usage
f: [],
g: (void 0),
h: /abc/g,
i: null
i: null,
j: [3, 4]
};

deepExtend(obj1, obj2);
Expand All @@ -56,5 +61,29 @@ Usage
c: 5,
e: { one: 1, two: 2 },
h: /abc/g,
i: null }
i: null,
j: [3, 4] }
*/
```

Unit testing
------------

```bash
$ npm test
```

Changelog
---------

[CHANGELOG.md](./CHANGELOG.md)

Any issues?
-----------

Please, report about issues [here](https://github.com/unclechu/node-deep-extend/issues).

License
-------

[MIT](./LICENSE)
93 changes: 1 addition & 92 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,92 +1 @@
/*!
* @description Recursive object extending
* @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
* @license MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2015 Viacheslav Lotsmanov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

'use strict';

/**
* Extening object that entered in first argument.
* Returns extended object or false if have no target object or incorrect type.
* If you wish to clone object, simply use that:
* deepExtend({}, yourObj_1, [yourObj_N]) - first arg is new empty object
*/
var deepExtend = module.exports = function (/*obj_1, [obj_2], [obj_N]*/) {
if (arguments.length < 1 || typeof arguments[0] !== 'object') {
return false;
}

if (arguments.length < 2) return arguments[0];

var target = arguments[0];

// convert arguments to array and cut off target object
var args = Array.prototype.slice.call(arguments, 1);

var key, val, src, clone, tmpBuf;

args.forEach(function (obj) {
if (typeof obj !== 'object') return;

for (key in obj) {
src = target[key];
val = obj[key];

if (val === target) continue;

if (typeof val !== 'object' || val === null) {
target[key] = val;
continue;
} else if (val instanceof Buffer) {
tmpBuf = new Buffer(val.length);
val.copy(tmpBuf);
target[key] = tmpBuf;
continue;
} else if (val instanceof Date) {
target[key] = new Date(val.getTime());
continue;
} else if (val instanceof RegExp) {
target[key] = new RegExp(val);
continue;
}

if (typeof src !== 'object' || src === null) {
clone = (Array.isArray(val)) ? [] : {};
target[key] = deepExtend(clone, val);
continue;
}

if (Array.isArray(val)) {
clone = (Array.isArray(src)) ? src : [];
} else {
clone = (!Array.isArray(src)) ? src : {};
}

target[key] = deepExtend(clone, val);
}
});

return target;
}
module.exports = require('./lib/deep-extend');
144 changes: 144 additions & 0 deletions lib/deep-extend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*!
* @description Recursive object extending
* @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
* @license MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2015 Viacheslav Lotsmanov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

'use strict';

function isSpecificValue(val) {
return (
val instanceof Buffer
|| val instanceof Date
|| val instanceof RegExp
) ? true : false;
}

function cloneSpecificValue(val) {
if (val instanceof Buffer) {
var x = new Buffer(val.length);
val.copy(x);
return x;
} else if (val instanceof Date) {
return new Date(val.getTime());
} else if (val instanceof RegExp) {
return new RegExp(val);
} else {
throw new Error('Unexpected situation');
}
}

/**
* Recursive cloning array.
*/
function deepCloneArray(arr) {
var clone = [];
arr.forEach(function (item, index) {
if (typeof item === 'object' && item !== null) {
if (Array.isArray(item)) {
clone[index] = deepCloneArray(item);
} else if (isSpecificValue(item)) {
clone[index] = cloneSpecificValue(item);
} else {
clone[index] = deepExtend({}, item);
}
} else {
clone[index] = item;
}
});
return clone;
}

/**
* Extening object that entered in first argument.
*
* Returns extended object or false if have no target object or incorrect type.
*
* If you wish to clone source object (without modify it), just use empty new
* object as first argument, like this:
* deepExtend({}, yourObj_1, [yourObj_N]);
*/
var deepExtend = module.exports = function (/*obj_1, [obj_2], [obj_N]*/) {
if (arguments.length < 1 || typeof arguments[0] !== 'object') {
return false;
}

if (arguments.length < 2) {
return arguments[0];
}

var target = arguments[0];

// convert arguments to array and cut off target object
var args = Array.prototype.slice.call(arguments, 1);

var val, src, clone;

args.forEach(function (obj) {
// skip argument if it is array or isn't object
if (typeof obj !== 'object' || Array.isArray(obj)) {
return;
}

Object.keys(obj).forEach(function (key) {
src = target[key]; // source value
val = obj[key]; // new value

// recursion prevention
if (val === target) {
return;

/**
* if new value isn't object then just overwrite by new value
* instead of extending.
*/
} else if (typeof val !== 'object' || val === null) {
target[key] = val;
return;

// just clone arrays (and recursive clone objects inside)
} else if (Array.isArray(val)) {
target[key] = deepCloneArray(val);
return;

// custom cloning and overwrite for specific objects
} else if (isSpecificValue(val)) {
target[key] = cloneSpecificValue(val);
return;

// overwrite by new value if source isn't object or array
} else if (typeof src !== 'object' || src === null || Array.isArray(src)) {
target[key] = deepExtend({}, val);
return;

// source value and new value is objects both, extending...
} else {
target[key] = deepExtend(src, val);
return;
}
});
});

return target;
}
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "deep-extend",
"description": "Recursive object extending.",
"description": "Recursive object extending",
"license": "MIT",
"version": "0.3.3",
"version": "0.4.0",
"homepage": "https://github.com/unclechu/node-deep-extend",
"keywords": [
"deep-extend",
Expand Down Expand Up @@ -36,10 +36,10 @@
"url": "https://github.com/maxmaximov"
}
],
"main": "index",
"main": "lib/deep-extend.js",
"engines": {
"node": ">=0.4",
"iojs": ">=1"
"node": ">=0.4.0",
"iojs": ">=1.0.0"
},
"scripts": {
"test": "./node_modules/.bin/mocha"
Expand All @@ -49,9 +49,11 @@
"should": "^5.2.0"
},
"directories": {
"lib": "./lib/",
"test": "./test/"
},
"files": [
"lib/deep-extend.js",
"test/index.spec.js",
"test/mocha.opts",
"index.js",
Expand Down
Loading

0 comments on commit 9ea374c

Please sign in to comment.