第 5 卷
九、手写浅拷贝函数的代码实现
核心原理
- 浅拷贝:只复制对象的“表层属性”,若属性值是引用类型(对象/数组等),则复制的是引用地
址,原对象和拷贝对象会共享该引用类型数据
- 支持类型:对象(普通对象、数组、类数组)、基本类型(直接返回原值)
- 实现思路:遍历原对象的可枚举属性,将属性值直接赋值到新对象中
实现代码
Code block
1function shallowClone(target) {
| 2 | // 1.处理基本类型和null(基本类型直接返回原值,null无需拷贝) |
|---|---|
| 3 | if (target === null || typeof target !== 'object') { |
| 4 | return target; |
| 5 | } |
6 7// 2. 处理数组:创建新数组,遍历赋值
8if (Array.isArray(target)) { 9return [...target]; // 或 Array.from(target)、target.slice()
10} 11
| 12 | // 3.处理类数组(如arguments、DOM集合):转为数组后返回 |
|---|---|
| 13 | if (target.length !== undefined && typeof target.length === 'number') { |
| 14 | return Array.from(target); |
| 15 | } |
16
| 17 | // 4.处理普通对象:创建新对象,遍历可枚举属性赋值 |
|---|---|
| 18 | const cloneObj = {}; |
| 19 | for (let key in target) { |
| 20 | //只复制自身的可枚举属性(不复制原型链上的属性) |
| 21 | if (target.hasOwnProperty(key)) { |
| 22 | cloneObj[key] = target[key]; |
| 23 | } |
| 24 | } |
| 25 | return cloneObj; |
| 26 | } |
27 28// 测试示例
29// 1. 基本类型测试 30console.log(shallowClone(123)); // 123
31console.log(shallowClone('abc')); // 'abc'
| 37 | arr2[0] = 100; //修改基本类型属性,不影响原数组 |
|---|---|
| 38 | arr2[2].a = 300; //修改引用类型属性,影响原数组 |
| 39 | console.log(arr1); // [1, 2, { a: 300 }] |
| 40 | console.log(arr2); // [100, 2, { a: 300 }] |
41 42// 3. 普通对象测试
| 43 | const obj1 = { name: '浅拷贝', user: { age: 20 } }; |
|---|---|
| 44 | const obj2 = shallowClone(obj1); |
| 45 | obj2.name = '修改后'; //基本类型不共享 |
| 46 | obj2.user.age = 21; //引用类型共享 |
| 47 | console.log(obj1); // { name: '浅拷贝', user: { age: 21 } } |
| 48 | console.log(obj2); // { name: '修改后', user: { age: 21 } } |
49 50// 4. 类数组测试
51function testArgs() {
| 52 | const argsClone = shallowClone(arguments); |
|---|---|
| 53 | console.log(argsClone); // [1, 2, 3](数组类型) |
| 54 | } |
| 55 | testArgs(1, 2, 3); |
56
十、手写深拷贝函数的代码实现
核心原理
- 深拷贝:复制对象的所有层级属性,包括引用类型属性,原对象和拷贝对象完全独立,互不影响
- 解决问题:浅拷贝的引用共享问题、循环引用问题(如obj.a = obj)、特殊类型处理(Date、
RegExp、Function等)
- 实现思路:递归遍历原对象,遇到引用类型时,创建新的对应类型对象,继续递归拷贝其属性;遇
到基本类型直接赋值
实现代码
Code block
1function deepClone(target, map = new WeakMap()) {
| 2 | // 1.处理基本类型和null(直接返回原值) |
|---|---|
| 3 | if (target === null || typeof target !== 'object') { |
| 4 | return target; |
11 12let cloneObj;
13 14// 3. 处理特殊引用类型
15// 3.1 处理Date 16if (target instanceof Date) {
17cloneObj = new Date(target);
18map.set(target, cloneObj);
19return cloneObj;
20} 21
22// 3.2 处理RegExp 23if (target instanceof RegExp) {
| 24 | cloneObj = new RegExp(target.source, target.flags); |
|---|---|
| 25 | map.set(target, cloneObj); |
| 26 | return cloneObj; |
| 27 | } |
28 29// 3.3 处理数组
30if (target instanceof Array) { 31cloneObj = [];
| 32 | map.set(target, cloneObj); //先缓存,避免循环引用 |
|---|---|
| 33 | //递归拷贝数组元素 |
| 34 | target.forEach((item, index) => { |
| 35 | cloneObj[index] = deepClone(item, map); |
| 36 | }); |
| 37 | return cloneObj; |
| 38 | } |
39 40// 3.4 处理普通对象(包括自定义类实例)
41if (target instanceof Object) { 42cloneObj = {};
| 43 | map.set(target, cloneObj); //缓存,避免循环引用 |
|---|---|
| 44 | //遍历对象自身的可枚举属性(包括Symbol属性) |
| 45 | const keys = [...Object.keys(target), |
...Object.getOwnPropertySymbols(target)];
46keys.forEach(key => { 47// 递归拷贝属性值
48cloneObj[key] = deepClone(target[key], map);
49});
50return cloneObj;