克隆

/**
 * 浅克隆对象只会被克隆最外部的一层,
 * 至于更深层的对象,则依然是通过引用指向同一块堆内存.
 * @param {*} o
 */
function shallow(o) {
    const obj = {}
    for (const i in o) {
        obj[i] = o[i]
    }
    return obj
}
// 被克隆对象
const oldObj = {
    a: 1,
    b: ['aaa', 'bbbb'],
    old: {
        zwz: {
            name:111
        }
    }
}

const newObj = shallow(oldObj)
console.log(newObj.old.zwz, oldObj.old.zwz)
console.log(newObj.old.zwz === oldObj.old.zwz)
// { name: 111 } { name: 111 }
// true
newObj.old.zwz.name = '卓文智'
console.log(newObj.old.zwz, oldObj.old.zwz)
// { name: '卓文智' } { name: '卓文智' }


// 深度克隆
// 1.JOSN.parse()
// JSON对象parse方法可以将JSON字符串反序列化成JS对象,
// stringify方法可以将JS对象序列化成JSON字符串,
// 这两个方法结合起来就能产生一个便捷的深克隆


const newObjDeep1 = JSON.parse(JSON.stringify(oldObj))


console.log(newObjDeep1.old.zwz, oldObj.old.zwz)
console.log(newObjDeep1.old.zwz === oldObj.old.zwz)
// { name: 111 } { name: 111 }
// false
newObjDeep1.old.zwz.name = '卓文智zzz'
console.log(newObjDeep1.old.zwz, oldObj.old.zwz)
// { name: '卓文智zzz' } { name: '卓文智' }

// JOSN.parse()坑
// 1.他无法实现对函数 、RegExp等特殊对象的克隆;
// 2.会抛弃对象的constructor,所有的构造函数会指向Object;
// 3.对象有循环引用,会报错;

// 构造函数
function person(name) {
    this.name = name
}

const cgl = new person('cgl')

// 函数
function say() {
    console.log('haaa')
}

const oldObj2 = {
    a: say,
    b: new Array(1),
    c: new RegExp('ab+c', 'i'),
    d: cgl
}
console.log('*************JSON.parse***********')

const newObj2 = JSON.parse(JSON.stringify(oldObj2))
console.log(newObj2.a, oldObj2.a);//无法复制函数
// undefined [Function: say]
console.log(newObj2.b[0], oldObj2.b[0]);//稀疏数组 复制错误
// null undefined
console.log(newObj2.c, oldObj2.c);//无法复制正在对象
// {} /ab+c/i
console.log(newObj2.d.constructor, oldObj2.d.constructor);//构造函数指向错误
// [Function: Object] [Function: person]


/**
 * 2.深度克隆函数
 * 由于要面对不同的对象(正则、数组、Date等)要采用不同的处理方式,
 * 我们需要实现一个对象类型判断函数
 */

 const isType = (obj, type) => {
     if (typeof obj !== 'object') return false
    //  判断数据类型
    const typeString = Object.prototype.toString.call(obj)
    let flag
    switch (type) {
        case 'Array':
            flag = typeString === '[object Array]'
            break;
        case 'Date':
            flag = typeString === '[object Date]'
            break;
        case 'RegExp':
            flag = typeString === '[object RegExp]'
            break;
        default:
            flag = false
    }
    return flag
 }

//  我们需要通过正则的扩展了解到flags属性等等,
// 因此我们需要实现一个提取flags的函数
const getRegExp = re => {
    var flags = ''
    if (re.global) flags += 'g'
    if (re.ignoreCase) flags += 'i'
    if (re.multiline) flags += 'm'
    return flags
}

/**
 * deep clone
 * @param {*} parent 
 */
const clone = parent => {
    // 维护两个存储循坏的数组
    const parents = []
    const children = []

    const _clone = parent => {
        if (parent === null ) return null
        if (typeof parent !== 'object') return parent

        let child,proto

        if (isType(parent, 'Array')) {
            // 对数组做特殊处理
            child = []
        } else if (isType(parent, 'RegExp')) {
             // 对正则对象做特殊处理
             child = new RegExp(parent.source, getRegExp(parent))
             if (parent.lastIndex) child.lastIndex = parent.lastIndex
        } else if (isType(parent, 'RegExp')) {
             // 对Date对象做特殊处理
             child = new Date(parent.getTime)
        } else {
            // 处理对象原型
            proto = Object.getPrototypeOf(parent)
            // 利用Object.create切断原型链
            child = Object.create(proto)
        }

        // 处理循环引用
        const index = parents.indexOf(parent)

        if (index != -1) {
            // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
            return children[index]
        }
        parents.push(parent)
        children.push(child)

        for (let i in parent) {
            // 递归
            child[i] = _clone(parent[i])
        }
        return child
    }
    return _clone(parent)
}


console.log('*************clone***********')

const newClone = oldObj2

console.log(newClone.a, oldObj2.a);
// [Function: say] [Function: say]
console.log(newClone.b[0], oldObj2.b[0]);
// undefined undefined
console.log(newClone.c, oldObj2.c);
// /ab+c/i /ab+c/i
console.log(newClone.d.constructor, oldObj2.d.constructor);
// [Function: person] [Function: person]

参考链接


  Reprint please specify: 云深不知处

 Previous
HTTP浏览器缓存机制 HTTP浏览器缓存机制
浏览器缓存浏览器缓存就是把一个以及请求过的Web资源(如html页面,图片,数据,js等等)拷贝一份副本存储在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本
2019-02-18
Next 
闭包 /** * bar()是定义在 foo() 里的内部函数,仅在该函数体内可用。 * bar()内没有自己的局部变量,然而它可以访问到外部函数的变量, * bar() 可以使用父函数 foo() 中声明的变量 name */ f
2019-02-17 卓文智
  TOC