1/** 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16/** 17 * @file: Deep copy 18 */ 19import Method from './Method'; 20 21const mapType = '[object Map]'; 22const setType = '[object Set]'; 23const arrayType = '[object Array]'; 24const objectType = '[object Object]'; 25const argsType = '[object Arguments]'; 26const boolType = '[object Boolean]'; 27const dateType = '[object Date]'; 28const numberType = '[object Number]'; 29const stringType = '[object String]'; 30const symbolType = '[object Symbol]'; 31const errorType = '[object Error]'; 32const regexpType = '[object RegExp]'; 33const funcType = '[object Function]'; 34const deepType = [mapType, setType, arrayType, objectType, argsType]; 35 36export default class Getclone { 37 private static getclone: Getclone = new Getclone(); 38 39 public static getInstance(): Getclone { 40 return this.getclone; 41 } 42 43 /** 44 * forEach 45 * 46 * @param { array } array 47 * 48 * @param { function } iteratee 49 */ 50 private forEach(array, iteratee) { 51 let index = -1; 52 const length = array.length; 53 54 while (++index < length) { 55 iteratee(array[index], index); 56 } 57 return array; 58 } 59 60 /** 61 * is object 62 * 63 * @param { any } target 64 */ 65 private isObject(target) { 66 const type = typeof target; 67 return target !== null && (type === 'object' || type === 'function'); 68 } 69 70 /** 71 * get type 72 * 73 * @param { any } target 74 */ 75 private getType(target) { 76 return Object.prototype.toString.call(target); 77 } 78 79 /** 80 * get init 81 * 82 * @param { any } target 83 */ 84 private getInit(target) { 85 const ctor = target.constructor; 86 return new ctor(); 87 } 88 89 /** 90 * clone symbol 91 * 92 * @param { any } target 93 */ 94 private cloneSymbo(target) { 95 return Object(Symbol.prototype.valueOf.call(target)); 96 } 97 98 /** 99 * clone reg 100 * 101 * @param { any } target 102 */ 103 private cloneRegister(target) { 104 const reFlags = /\w*$/; 105 const result = new target.constructor(target.source, reFlags.exec(target)); 106 result.lastIndex = target.lastIndex; 107 return result; 108 } 109 110 /** 111 * clone function 112 * 113 * @param { function } func 114 */ 115 private cloneFunc(func) { 116 const bodyRegister = /(?<={)(.|\n)+(?=})/m; 117 const paramRegister = /(?<=\().+(?=\)\s+{)/; 118 const funcStr = func.toString(); 119 if (func.prototype) { 120 const param = paramRegister.exec(funcStr); 121 const bodys = bodyRegister.exec(funcStr); 122 if (bodys) { 123 if (param) { 124 const paramArray = param[0].split(','); 125 return new Function(...paramArray, bodys[0]); 126 } else { 127 return new Function(bodys[0]); 128 } 129 } else { 130 return null; 131 } 132 } else { 133 return eval(funcStr); 134 } 135 } 136 137 /** 138 * clone other type 139 * 140 * @param { any } target 141 * 142 * @param { any } type - data type 143 */ 144 private cloneOtherType(target, type) { 145 const ctor = target.constructor; 146 switch (type) { 147 case boolType: 148 case numberType: 149 case stringType: 150 case errorType: 151 case dateType: 152 return new ctor(target); 153 case regexpType: 154 return this.cloneRegister(target); 155 case symbolType: 156 return this.cloneSymbo(target); 157 case funcType: 158 return this.cloneFunc(target); 159 default: 160 return null; 161 } 162 } 163 164 /** 165 * clone 166 * 167 * @param { any } target 168 * 169 * @param { map } map 170 */ 171 public clone(target, map = new WeakMap()) { 172 if (!this.isObject(target)) { 173 return target; 174 } 175 176 const type = this.getType(target); 177 let cloneTargets; 178 179 if (Method.includes(deepType, type)) { 180 cloneTargets = this.getInit(target); 181 } else { 182 return this.cloneOtherType(target, type); 183 } 184 185 if (map.get(target)) { 186 return map.get(target); 187 } 188 map.set(target, cloneTargets); 189 190 if (type === setType) { 191 target.forEach((value) => { 192 cloneTargets.add(this.clone(value, map)); 193 }); 194 return cloneTargets; 195 } 196 197 if (type === mapType) { 198 target.forEach((value, key) => { 199 cloneTargets.set(key, this.clone(value, map)); 200 }); 201 return cloneTargets; 202 } 203 204 const keys = type === arrayType ? undefined : Object.keys(target); 205 this.forEach(keys || target, (value, key) => { 206 if (keys) { 207 key = value; 208 } 209 cloneTargets[key] = this.clone(target[key], map); 210 }); 211 212 return cloneTargets; 213 } 214}