Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add instanceClone parameter to lang/clone #2

Merged
merged 4 commits into from Jan 11, 2013
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 13 additions & 1 deletion doc/lang.md
Expand Up @@ -4,10 +4,16 @@ Language Utilities. Easier inheritance, scope handling, type checks.



## clone(val):*
## clone(val, [instanceClone]):*

Deep clone native types like Object, Array, RegExp, Date and primitives.

The `instanceClone` function will be invoked to clone non-native objects if
provided. Non-native objects are defined as objects that have the
`.constructor` property set to a custom function (not `Object`). If
`instanceClone` is not specified, it will copy the object reference without
cloning.

### Example

```js
Expand All @@ -23,6 +29,12 @@ var e = c.concat(); // [1, 2, [3, 4]]
console.log( c[2] === d[2] ); // false
// concat doesn't do a deep clone, arrays are passed by reference
console.log( e[2] === d[2] ); // true

function Custom() { }
function cloneCustom(x) { return new Custom(); }
var f = { test: new Custom() };
var g = clone(Custom, cloneCustom);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo, it should be var g = clone(f, cloneCustom);

g.test === f.test // false, since new Custom instance will be created
```


Expand Down
32 changes: 18 additions & 14 deletions src/lang/clone.js
Expand Up @@ -4,14 +4,14 @@ define(['../object/forOwn', './kindOf'], function (forOwn, kindOf) {
* Clone native types.
* @version 0.1.0 (2012/07/13)
*/
function clone(val){
function clone(val, instanceClone) {
var result;
switch ( kindOf(val) ) {
case 'Object':
result = cloneObject(val);
result = cloneObject(val, instanceClone);
break;
case 'Array':
result = deepCloneArray(val);
result = cloneArray(val, instanceClone);
break;
case 'RegExp':
result = cloneRegExp(val);
Expand All @@ -25,35 +25,39 @@ define(['../object/forOwn', './kindOf'], function (forOwn, kindOf) {
return result;
}

function cloneObject(source) {
var out = {};
forOwn(source, copyProperty, out);
function cloneObject(source, instanceClone) {
if (source.constructor === Object) {
var out = {};
forOwn(source, function(val, key) {
this[key] = clone(val, instanceClone);
}, out);
} else if (instanceClone) {
return instanceClone(source);
} else {
return source;
}
return out;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put return out inside the if (source.constru..., and remove all else.

}

function copyProperty(val, key){
this[key] = clone(val);
}

function cloneRegExp(r){
function cloneRegExp(r) {
var flags = '';
flags += r.multiline? 'm' : '';
flags += r.global? 'g' : '';
flags += r.ignoreCase? 'i' : '';
return new RegExp(r.source, flags);
}

function cloneDate(date){
function cloneDate(date) {
return new Date( date.getTime() );
}

function deepCloneArray(arr){
function cloneArray(arr, instanceClone) {
var out = [],
i = -1,
n = arr.length,
val;
while (++i < n) {
out[i] = clone(arr[i]);
out[i] = clone(arr[i], instanceClone);
}
return out;
}
Expand Down
24 changes: 24 additions & 0 deletions tests/spec/lang/spec-clone.js
Expand Up @@ -76,6 +76,30 @@ define(['mout/lang/clone'], function (clone) {
expect( b.b ).not.toBe( a.b );
});

it('should invoke function to clone instances', function() {
function CustomType() { }

var a = {
test: new CustomType()
};

var result = clone(a, function(x) {
expect(x).toBe(a.test);
return 1;
});

expect(result.test).toEqual(1);
});

it('should copy custom instances by reference by default', function() {
function CustomType() { }
var a = {
test: new CustomType()
};

var result = clone(a);
expect(result.test).toBe(a.test);
});
});

});