手写js之浅克隆

361 阅读3分钟

平时在工作中,经常会用到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));

测试结果如下:

image.png