平时在工作中,经常会用到clone
,一般就直接使用现有工具库,如lodash
去实现了,现在呢,打算动手写一个浅克隆方法clone
。
处理的边界
实现一个浅克隆方法很简单,但要实现一个功能完备,兼容性强大的方法,确要考虑很多边界情况,就拿这里的浅克隆为例,我们就要考虑传入的参数是函数,正则,日期,对象,数组,或者基本数据类型。
- 函数
- 正则
- 日期
- 对象
- 数组
- 基本数据类型
准备判断类型的辅助函数
因为需要对传入参数进行类型判断,所以我们可以提前准备一个判断类型的辅助函数getType
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1)
}
写一个架子
先将clone
函数的架子先支起来,后续逐一实现。
function clone(obj) {
const type = getType(obj)
switch(type) {
// 函数
case 'Function':
break
// 正则
case 'RegExp':
break
// 日期
case 'Date':
break
// 对象
case 'Object':
break
// 数组
case 'Array':
break
// 其他类型
default:
}
}
先处理对象跟数组
对象与数组是我们日常考虑最多的两个情况,且实现思路也一样,就一起实现了。
function clone(obj) {
// 定义一个变量来保存结果
let res
// 获取参数类型
const type = getType(obj)
switch(type) {
// 函数
case 'Function':
break
// 正则
case 'RegExp':
break
// 日期
case 'Date':
break
// 对象
case 'Object':
// 数组
case 'Array':
res = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
res[key] = obj[key]
}
}
break
// 其他类型
default:
}
return res
}
处理正则,与日期
正则,与日期,可以通过各自的构造函数RegExp
, Date
来实现。
function clone(obj) {
// 定义一个变量来保存结果
let res
// 获取参数类型
const type = getType(obj)
switch(type) {
// 函数
case 'Function':
break
// 正则
case 'RegExp':
res = new RegExp(obj)
break
// 日期
case 'Date':
res = new Date(obj)
break
// 对象
case 'Object':
// 数组
case 'Array':
res = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
res[key] = obj[key]
}
}
break
// 其他类型
default:
}
return res
}
处理函数
处理函数,是通过Function
来实现,但要稍稍麻烦点,Function
接收的参数需要稍稍处理一下。
function clone(obj) {
// 定义一个变量来保存结果
let res
// 获取参数类型
const type = getType(obj)
switch(type) {
// 函数
case 'Function':
const reg = /function\((.*)\).*\{([^\}]*)\}/
const matches = obj.toString().match(reg)
if (matches) {
let [, args, functionBody] = matches
// 对args处理成数组格式
args = args.split(',').map(param => param.trim())
res = new Function(...args, functionBody)
}
break
// 正则
case 'RegExp':
res = new RegExp(obj)
break
// 日期
case 'Date':
res = new Date(obj)
break
// 对象
case 'Object':
// 数组
case 'Array':
res = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
res[key] = obj[key]
}
}
break
// 其他类型
default:
}
return res
}
处理基本类型
对基本类型的处理,就直接返回即可。
function clone(obj) {
// 定义一个变量来保存结果
let res
// 获取参数类型
const type = getType(obj)
switch(type) {
// 函数
case 'Function':
const reg = /function\((.*)\).*\{([^\}]*)\}/
const matches = obj.toString().match(reg)
if (matches) {
let [, args, functionBody] = matches
// 对args处理成数组格式
args = args.split(',').map(param => param.trim())
res = new Function(...args, functionBody)
}
break
// 正则
case 'RegExp':
res = new RegExp(obj)
break
// 日期
case 'Date':
res = new Date(obj)
break
// 对象
case 'Object':
// 数组
case 'Array':
res = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
res[key] = obj[key]
}
}
break
// 其他类型
default:
res = obj
}
return res
}
简单测试一下:
// 测试Array
let arr = [1, 2, 3, 4]
console.log(clone(arr))
// 测试Object
let obj = {name: '张三', age: 28}
console.log(clone(obj));
// 测试Function
let fn = function(a, b) {
console.log('HaHa')
return a + b
}
console.log(clone(fn)(2, 3));
// 测试RegExp
let reg = /123/
console.log(clone(reg));
// 测试Date
let date = new Date()
console.log(clone(date));
// 测试基本数据类型
console.log(clone(123));
console.log(clone(false));
测试结果如下: