From 777c300aa98865e7db8540c67dc112c6a2587044 Mon Sep 17 00:00:00 2001 From: Simon Hailes Date: Fri, 16 Mar 2018 16:30:00 +0000 Subject: [PATCH 1/5] Add use of prototype._clone() if found to clone an object. + test --- clone.js | 85 +++++++++++++++++++++++++++++++------------------------- test.js | 30 ++++++++++++++++++++ 2 files changed, 77 insertions(+), 38 deletions(-) diff --git a/clone.js b/clone.js index 80d0c76..b33199d 100644 --- a/clone.js +++ b/clone.js @@ -78,6 +78,8 @@ function clone(parent, circular, depth, prototype, includeNonEnumerable) { if (depth === 0) return parent; + var dochildren = true; + var child; var proto; if (typeof parent != 'object') { @@ -112,7 +114,12 @@ function clone(parent, circular, depth, prototype, includeNonEnumerable) { } else { if (typeof prototype == 'undefined') { proto = Object.getPrototypeOf(parent); - child = Object.create(proto); + if (proto && proto._clone){ + child = parent._clone(); + dochildren = false; + } else { + child = Object.create(proto); + } } else { child = Object.create(prototype); @@ -144,52 +151,54 @@ function clone(parent, circular, depth, prototype, includeNonEnumerable) { }); } - for (var i in parent) { - var attrs; - if (proto) { - attrs = Object.getOwnPropertyDescriptor(proto, i); - } - - if (attrs && attrs.set == null) { - continue; - } - child[i] = _clone(parent[i], depth - 1); - } + // if we used _clone(), then ignore the rest of this object + if (dochildren){ + for (var i in parent) { + var attrs; + if (proto) { + attrs = Object.getOwnPropertyDescriptor(proto, i); + } - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(parent); - for (var i = 0; i < symbols.length; i++) { - // Don't need to worry about cloning a symbol because it is a primitive, - // like a number or string. - var symbol = symbols[i]; - var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); - if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { + if (attrs && attrs.set == null) { continue; } - child[symbol] = _clone(parent[symbol], depth - 1); - if (!descriptor.enumerable) { - Object.defineProperty(child, symbol, { - enumerable: false - }); + child[i] = _clone(parent[i], depth - 1); + } + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(parent); + for (var i = 0; i < symbols.length; i++) { + // Don't need to worry about cloning a symbol because it is a primitive, + // like a number or string. + var symbol = symbols[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); + if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { + continue; + } + child[symbol] = _clone(parent[symbol], depth - 1); + if (!descriptor.enumerable) { + Object.defineProperty(child, symbol, { + enumerable: false + }); + } } } - } - if (includeNonEnumerable) { - var allPropertyNames = Object.getOwnPropertyNames(parent); - for (var i = 0; i < allPropertyNames.length; i++) { - var propertyName = allPropertyNames[i]; - var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); - if (descriptor && descriptor.enumerable) { - continue; + if (includeNonEnumerable) { + var allPropertyNames = Object.getOwnPropertyNames(parent); + for (var i = 0; i < allPropertyNames.length; i++) { + var propertyName = allPropertyNames[i]; + var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); + if (descriptor && descriptor.enumerable) { + continue; + } + child[propertyName] = _clone(parent[propertyName], depth - 1); + Object.defineProperty(child, propertyName, { + enumerable: false + }); } - child[propertyName] = _clone(parent[propertyName], depth - 1); - Object.defineProperty(child, propertyName, { - enumerable: false - }); } } - return child; } diff --git a/test.js b/test.js index fb4ba67..f4df8e4 100644 --- a/test.js +++ b/test.js @@ -684,3 +684,33 @@ exports["clone should mark the cloned non-enumerable properties as non-enumerabl test.done(); }; + + +exports["clone object using _clone"] = function (test) { + test.expect(3); + + + // create an object with _clone prototype + function myobj( name, id ){ + this.name = name; + this.id = id; + }; + + // note clone function does not copy complete object. + myobj.prototype._clone = function(){ + var newobj = new myobj( this.name, 'isclone'); + return newobj; + } + + var source = new myobj('myname', 'original'); + + + var cloned = clone(source); + + test.equal(cloned.name, source.name); + test.equal(cloned.id, 'isclone'); + test.equal(source.id, 'original'); + + test.done(); +}; + From 3798d51a77afe1afae75d108b1a5de334f88f4a2 Mon Sep 17 00:00:00 2001 From: Simon Hailes Date: Sat, 17 Mar 2018 14:04:22 +0000 Subject: [PATCH 2/5] update to allow a direct _clone() fucntion on an object, rather than only in the prototype. This allows a user to customise a Specific object to behave differently to the prototype method. --- clone.js | 3 ++- test.js | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/clone.js b/clone.js index b33199d..ee689ab 100644 --- a/clone.js +++ b/clone.js @@ -114,7 +114,8 @@ function clone(parent, circular, depth, prototype, includeNonEnumerable) { } else { if (typeof prototype == 'undefined') { proto = Object.getPrototypeOf(parent); - if (proto && proto._clone){ + // if a _clone function in prototype OR direct on object + if (parent._clone){ child = parent._clone(); dochildren = false; } else { diff --git a/test.js b/test.js index f4df8e4..b7d4628 100644 --- a/test.js +++ b/test.js @@ -687,7 +687,7 @@ exports["clone should mark the cloned non-enumerable properties as non-enumerabl exports["clone object using _clone"] = function (test) { - test.expect(3); + test.expect(5); // create an object with _clone prototype @@ -702,14 +702,35 @@ exports["clone object using _clone"] = function (test) { return newobj; } + var source = new myobj('myname', 'original'); + // also test a (different) direct on object _clone() method + source._clone = function(){ + var newobj = new myobj( this.name, 'isclone2'); + return newobj; + } + // this will use the *function* we just added to the source object var cloned = clone(source); + // this will use the prototype we added to the *object defn* + var cloned2 = clone(cloned); + + // ensure that out cloned object still has the prototype _clone() + var fail = false; + if (!cloned._clone){ + fail = true; + } + + var util = require('util'); + console.log(util.inspect(cloned)); + console.log(util.inspect(cloned2)); test.equal(cloned.name, source.name); - test.equal(cloned.id, 'isclone'); + test.equal(cloned.id, 'isclone2'); + test.equal(cloned2.id, 'isclone'); test.equal(source.id, 'original'); + test.equal(fail, false); test.done(); }; From 6134e2e260978ea2fa7af60ffa144168d2076835 Mon Sep 17 00:00:00 2001 From: Simon Hailes Date: Thu, 22 Mar 2018 18:50:25 +0000 Subject: [PATCH 3/5] Add to README.md --- README.md | 10 ++++++++++ package.json | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c21ba16..4e11701 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,16 @@ So, `b.myself` points to `b`, not `a`. Neat! ## Changelog +#### 2018-03-22 + + - Add detection of a custom _clone function on an object. +If present, use this function to clone the object and all children. +This allows for the cloning of objects in any way you wish, +and is especially useful if the object is not clonable in the normal way. +the _clone() fn can be on the object prototype, or directly on the object as a method, +and should return the desired cloned object. See tests for examples. + + ### v2.1.1 #### 2017-03-09 diff --git a/package.json b/package.json index ce8d7e3..2e1741b 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,9 @@ "rictic (https://github.com/rictic)", "Martin JurĨa (https://github.com/jurca)", "Misery Lee (https://github.com/miserylee)", - "Clemens Wolff (https://github.com/c-w)" + "Clemens Wolff (https://github.com/c-w)", + "Simon Hailes (https://github.com/btsimonh)" + ], "license": "MIT", "engines": { From 5a1712aa19b15fd1a61ddc51e4e7eb31c446256b Mon Sep 17 00:00:00 2001 From: Simon Hailes Date: Sat, 24 Mar 2018 10:18:06 +0000 Subject: [PATCH 4/5] resolve conflict in readme? --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 4e11701..9cb9daa 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,12 @@ and is especially useful if the object is not clonable in the normal way. the _clone() fn can be on the object prototype, or directly on the object as a method, and should return the desired cloned object. See tests for examples. +### v2.1.2 +#### 2018-03-21 + + - Use `Buffer.allocUnsafe()` on Node >= 4.5.0 (contributed by @ChALkeR) + ### v2.1.1 #### 2017-03-09 From a1ae553e0962a40e3b7caef9f9bda7075b93b7b3 Mon Sep 17 00:00:00 2001 From: Simon Hailes Date: Sat, 24 Mar 2018 10:25:27 +0000 Subject: [PATCH 5/5] remove a couple of spaces --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cb9daa..e508dbc 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ and should return the desired cloned object. See tests for examples. #### 2018-03-21 - Use `Buffer.allocUnsafe()` on Node >= 4.5.0 (contributed by @ChALkeR) - + ### v2.1.1 #### 2017-03-09