index.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. 'use strict';
  2. // Method that will return the data type for any structure passed to it
  3. function getDataType (data) {
  4. // Use the objects toString method on the data.
  5. // This will return something like [object String]
  6. // Then we use .slice to grab the last portion of it (in this case the "string" bit)
  7. return Object.prototype.toString.call(data).slice(8, -1);
  8. }
  9. // Create a method to detect whether an object contains a circular reference
  10. function isCyclic (data) {
  11. // Create an array that will store the nodes of the array that have already been iterated over
  12. let seenObjects = [];
  13. function detect (data) {
  14. // If the data pass is an object
  15. if (data && getDataType(data) === "Object") {
  16. // If the data is already in the seen nodes array then we know there is a circular reference
  17. // Therefore return true
  18. if (seenObjects.indexOf(data) !== -1) {
  19. return true;
  20. }
  21. // Add the data to the seen objects array
  22. seenObjects.push(data);
  23. // Begin iterating through the data passed to the method
  24. for (var key in data) {
  25. // Recall this method with the objects key
  26. if (data.hasOwnProperty(key) === true && detect(data[key])) {
  27. return true;
  28. }
  29. }
  30. }
  31. return false;
  32. }
  33. // Return the method
  34. return detect(data);
  35. }
  36. const deepClone = function (data) {
  37. // If the data is null or undefined then we return undefined
  38. if (data === null || data === undefined) {
  39. return undefined;
  40. }
  41. // Get the data type and store it
  42. const dataType = getDataType(data);
  43. // If the data passed is a date object
  44. if (dataType === "Date") {
  45. // Create a new date object and set the time to what it was previously
  46. let clonedDate = new Date();
  47. clonedDate.setTime(data.getTime());
  48. return clonedDate;
  49. }
  50. // If the data passed is an object
  51. if (dataType === "Object") {
  52. // Check for circular references, if there are then we just return the un-cloned data.
  53. if (isCyclic(data) === true) {
  54. return data;
  55. }
  56. // Create a new object that will store our copied data
  57. let copiedObject = {};
  58. // Iterate over the objects keys
  59. for (let key in data) {
  60. // Clone the keys of each of the objects so that we can deeply copy and nested data structures
  61. // For example if an object has a key value that is an array
  62. // Add this cloned key value to the copiedObject we created earlier
  63. copiedObject[key] = deepClone(data[key]);
  64. }
  65. // Return the deeply copied object
  66. return copiedObject;
  67. }
  68. // If the data is an array
  69. if (dataType === "Array") {
  70. // Create a new array that will have no references to the one we want to copy
  71. let copiedArray = [];
  72. // Iterate over the arrays elements
  73. for (var i = 0; i < data.length; i++) {
  74. // Push the arrays elements to this new array
  75. // First recall this method with the elements
  76. // This is so arrays of objects and other nested data structures get correctly cloned.
  77. copiedArray.push(deepClone(data[i]));
  78. }
  79. // Return the cloned array
  80. return copiedArray;
  81. }
  82. // If it's any other data type like a string or number, they don't need cloning so we just return them
  83. else {
  84. return data;
  85. }
  86. }
  87. // Export a new instance of the clone constructor
  88. module.exports = deepClone;