1

深拷贝和浅拷贝问题的本质还是不同数据类型的存储方式差异,尤其是引用数据类型的特殊。
图片描述

现分别对赋值、浅拷贝、深拷贝做深入研究:

1.赋值

原理:直接将对象指针直接赋值给另一个变量

代码

let developer = {
    title: 'Frontend',
    basic: {
        html: '5',
        css: '3',
        js: 'es6'
    },
    frameworks: ['React', 'Vue', 'AngularJS'],
    summary: function(){
        console.log('I am FE developer');
    }
};

let newDeveloper = developer;

console.log(newDeveloper);

//基本类型:改变原对象
newDeveloper.title = 'Frontend Leader';
console.log(developer.title); // Frontend Leader

//对象:改变原对象
newDeveloper.basic.http = '2.0';
console.log(developer.basic.http); // 2.0
newDeveloper.basic.js = 'es5';
console.log(developer.basic.js); // es5

//数组:改变原对象
newDeveloper.frameworks.push('Angular');
console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS', 'Angular' ]

//函数:改变原对象
newDeveloper.summary = function () {
    console.log('I like FE development');
};
developer.summary(); // I like FE development

2.浅拷贝

原理:遍历对象的每个属性进行逐个拷贝

实现方式

  • 方式1:遍历并复制
  • 方式2:Object.assign()

代码

let developer = {
    title: 'Frontend',
    basic: {
        html: '5',
        css: '3',
        js: 'es6'
    },
    frameworks: ['React', 'Vue', 'AngularJS'],
    summary: function(){
        console.log('I am FE developer');
    }
};

/*
* 方式1:逐个复制
*
* */
function cloneInShallow(source) {
    let target = {};

    for (prop in source){
        target[prop] = source[prop];
    }

    return target
}
let newDeveloper = cloneInShallow(developer);

/*
* 方式2:Object.assign()
*
* */
// let newDeveloper = Object.assign({}, developer);

console.log(newDeveloper);

//基本类型:不改变原对象
newDeveloper.title = 'Frontend Leader';
console.log(developer.title); // Frontend

// 对象:改变原对象
newDeveloper.basic.http = '2.0';
console.log(developer.basic.http); // 2.0
newDeveloper.basic.js = 'es5';
console.log(developer.basic.js); // es5

//数组:改变原对象
newDeveloper.frameworks.push('Angular');
console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS', 'Angular' ]

//函数:不改变原对象
newDeveloper.summary = function () {
    console.log('I like FE development');
};
developer.summary(); // I am FE developer

3.深拷贝

原理:使用递归,遍历每一个对象属性进行拷贝

实现方式

  • 方式1: 纯手工打造回调函数
  • 方式2: JSON.parse(JSON.stringify(obj))
  • 方式3: 借助jQuery
  • 方式4: 借助lodash

代码

let developer = {
    title: 'Frontend',
    basic: {
        html: '5',
        css: '3',
        js: 'es6'
    },
    frameworks: ['React', 'Vue', 'AngularJS', {node: 'express'}],
    summary: function(){
        console.log('I am FE developer');
    }
};

/*
* 方式1: 纯手工打造
* */
function cloneInDeep(source) {
    if(source && typeof source === 'object'){
        let target = {};
        for (let prop in source){
            let value = source[prop];

            if(Array.isArray(value)){
                let newArray = [];
                value.forEach(function (item, index) {
                    if(Array.isArray(item) || Object.getPrototypeOf(item) === Object.prototype){
                        newArray.push(cloneInDeep(item))
                    }else{
                        newArray.push(item)
                    }
                });

                target[prop] = newArray;
            }else if(Object.getPrototypeOf(value) === Object.prototype){
                target[prop] = cloneInDeep(value);
            }else{
                target[prop] = value;
            }
        }

        return target
    }else{
        throw new Error('source is not object!')
    }
}
let newDeveloper = cloneInDeep(developer);


/*
* 方式2: JSON.parse(JSON.stringify(obj))
* 弊端:会抛弃对象的constructor
* 适用:能够被json直接表示的数据结构,对象中只包含number、string、boolean、array、扁平对象
* 不适用:含有function、regexp
* */
// let newDeveloper = JSON.parse(JSON.stringify(developer));


/*
* 方式3: jQuery
* */
let $ = require('jquery');
// let newDeveloper = $.extend({}, developer);

/*
* 方式4: lodash
* */
let _ = require('lodash');
// let newDeveloper = _.cloneDeep(developer);


console.log(newDeveloper);

//基本类型:不改变原对象
newDeveloper.title = 'Frontend Leader';
console.log(developer.title); // Frontend

// 对象:不改变原对象
newDeveloper.basic.http = '2.0';
console.log(developer.basic.http); // undefined
newDeveloper.basic.js = 'es5';
console.log(developer.basic.js); // es6

//数组:不改变原对象
newDeveloper.frameworks.push('Angular');
console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS' , { node: 'express' } ]
newDeveloper.frameworks[3].node = 'koa';
console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS' , { node: 'express' } ]

//函数:不改变原对象
newDeveloper.summary = function () {
    console.log('I like FE development');
};
developer.summary(); // I am FE developer

涉及的知识点:

  • 数据类型及存储机制
  • for...in...遍历,枚举属性
  • 递归
  • 对象和数组的判断

yuyongyu
921 声望41 粉丝

一个前端