| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- 'use strict';
- // Method that will return the data type for any structure passed to it
- function getDataType (data) {
- // Use the objects toString method on the data.
- // This will return something like [object String]
- // Then we use .slice to grab the last portion of it (in this case the "string" bit)
- return Object.prototype.toString.call(data).slice(8, -1);
- }
- // Create a method to detect whether an object contains a circular reference
- function isCyclic (data) {
-
- // Create an array that will store the nodes of the array that have already been iterated over
- let seenObjects = [];
- function detect (data) {
- // If the data pass is an object
- if (data && getDataType(data) === "Object") {
-
- // If the data is already in the seen nodes array then we know there is a circular reference
- // Therefore return true
- if (seenObjects.indexOf(data) !== -1) {
- return true;
- }
- // Add the data to the seen objects array
- seenObjects.push(data);
- // Begin iterating through the data passed to the method
- for (var key in data) {
- // Recall this method with the objects key
- if (data.hasOwnProperty(key) === true && detect(data[key])) {
- return true;
- }
- }
- }
- return false;
- }
- // Return the method
- return detect(data);
- }
- const deepClone = function (data) {
- // If the data is null or undefined then we return undefined
- if (data === null || data === undefined) {
- return undefined;
- }
- // Get the data type and store it
- const dataType = getDataType(data);
- // If the data passed is a date object
- if (dataType === "Date") {
- // Create a new date object and set the time to what it was previously
- let clonedDate = new Date();
- clonedDate.setTime(data.getTime());
- return clonedDate;
- }
- // If the data passed is an object
- if (dataType === "Object") {
- // Check for circular references, if there are then we just return the un-cloned data.
- if (isCyclic(data) === true) {
- return data;
- }
- // Create a new object that will store our copied data
- let copiedObject = {};
- // Iterate over the objects keys
- for (let key in data) {
- // Clone the keys of each of the objects so that we can deeply copy and nested data structures
- // For example if an object has a key value that is an array
- // Add this cloned key value to the copiedObject we created earlier
- copiedObject[key] = deepClone(data[key]);
- }
- // Return the deeply copied object
- return copiedObject;
- }
- // If the data is an array
- if (dataType === "Array") {
- // Create a new array that will have no references to the one we want to copy
- let copiedArray = [];
- // Iterate over the arrays elements
- for (var i = 0; i < data.length; i++) {
- // Push the arrays elements to this new array
- // First recall this method with the elements
- // This is so arrays of objects and other nested data structures get correctly cloned.
- copiedArray.push(deepClone(data[i]));
- }
- // Return the cloned array
- return copiedArray;
- }
- // If it's any other data type like a string or number, they don't need cloning so we just return them
- else {
- return data;
- }
- }
- // Export a new instance of the clone constructor
- module.exports = deepClone;
|