IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> js 深浅拷贝原理 -> 正文阅读

[JavaScript知识库]js 深浅拷贝原理

1:数据类型:

① 基本数据类型:Number、string、null、undefined、boolean
② 引用数据类型: Object =>{object、Array、Function}

2:栈(stack)、堆(heap):

① 基本数据类型是直接存储在栈中的数据,栈的存取速度比堆要快,存在栈中的数据大小是确定的
问:为什么基本数据类型存储在栈中? 存储结构不一样,引用数据类型一般占空间大
答:(http://www.javashuo.com/article/p-gkhlbedc-gg.html)
问:基本数据类型一定存在栈中么?
答:(https://www.cnblogs.com/linliquan/p/11273932.html)
② 引用数据类型存储的是该对象在栈中引用,真实的数据存放在堆内存里

3:基本数据类型和引用数据类型最大的区别:传值和传址

 
let arr = [1, 2, 3, 4, 5] //原数据(引用数据类型)
let arr1 = arr 
  console.log(arr,'arr')   //[1,2,3,4,5] 'arr'
  console.log(arr1,'arr1') //[1,2,3,4,5] 'arr1'
let arr2 = arr[2] 
  console.log(arr2,'arr2') //3 arr2
arr1[2] = 33
  console.log(arr1,'arr1-1')   //[1,2,33,4,5] 'arr1-1'
	console.log(arr,'arr-1')   //[1,2,33,4,5] 'arr-1'
arr2 = 4
	console.log(arr2,'arr2-1') //4 arr2-1
  console.log(arr,'arr-2') //[1,2,33,4,5] 'arr-2'

4:深浅拷贝:针对引用数据类型

① 浅拷贝:只复制指向某个对象的指针而不复制对象本身,新旧对象还是共享同一块内存。
② 深拷贝:指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。

5:对象的直接赋值和浅拷贝的区别:

① 直接赋值:将一个对象赋值给一个新的对象的时候,赋的其实是该对象在栈中的地址,而不是堆中的数据。
也就是一个对象的改变就会改变另外一个对象。
② 浅拷贝:会创建一个对象,再去遍历原始对象,如果原对象的属性值是基础类型,那么就拷贝基础类型,
如果是引用类型,则拷贝的是指针。

let arr =[11,22,33,44,55]
let arr1 = arr //直接赋值
let arr2 = [...arr] //扩展运算符实现浅拷贝:...arr=>11,22,33,44,55

6:浅拷贝的实现方式:

① Object.assign():可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
注:当object只有一层的时候,是深拷贝

let query = {name:'原数据',content:['前端','学习']}
let query1 = Object.assign({}, query)
console.log(query,'query') //{name:'原数据',content:['前端','学习']} query
console.log(query1,'query1') //{name:'原数据',content:['前端','学习']} query1
query1.name = '浅拷贝'
query1.content[1] = '浅拷贝学习'
console.log(query,'query-1') //{name:'原数据',content:['前端','浅拷贝学习']} query-1
console.log(query1,'query1-1') //{name:'浅拷贝',content:['前端','浅拷贝学习']} query1-1

② Array.prototype.concat()


let query = [1,3,{userName:'浅拷贝'}]
let query1 = query.concat()
query1[1] = 33
query1[2].userName = 'concat'
console.log(query,'query')//[1,3,{userName:'concat'}]
console.log(query1,'query1')//[1,33,{userName:'concat'}]

③ Array.prototype.slice()

let query = [1,3,{userName:'浅拷贝'}]
let query1 = query.slice(0)
query1[1] = 33
query1[2].userName = 'concat'
console.log(query,'query')//[1,3,{userName:'concat'}]
console.log(query1,'query1')//[1,33,{userName:'concat'}]

说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
浅拷贝的方式有很多:for…in 等等

var person = {
	name: "浅拷贝",
	age: 111,
	sex: '保密'
};
var person2 = {};
for (var key in person1) {
	person2[key] = person1[key];
}
console.log(person2) //{name: "浅拷贝", age: 111, sex: '保密'}

7:深拷贝的实现方式:

① JSON.parse(JSON.stringify()): 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象, 一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
说明:JSON.parse(JSON.stringify())自身存在小问题:比如:不能处理函数,
因为 JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个JSON字符串,不能接受函数。
另外还有 将undefined,’ ',empty 都转换为null,

let arr = ['深拷贝',' ','',null,undefined, ,{userName:'深拷贝1'},function(){}]
let arr1 = JSON.parse(JSON.stringify(arr))
console.log(arr1,'arr1')
arr1[6].userName = '拷贝'
console.log(arr,'arr') 
console.log(arr1,'arr1-1')

? 如果对象中存在循环引用的情况也无法正确实现深拷贝。

const a = {
    b: 1,
}
a.c = a;
JSON.parse(JSON.stringify(a));

? 如果 data 里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象。

const a = {
    b: new Date(1536627600000),
}
console.log(a) //{b: Tue Sep 11 2018 09:00:00 GMT+0800 (GMT+08:00)}
console.log(JSON.parse(JSON.stringify(a)))//{b: '2018-09-11T01:00:00.000Z'}

? 如果 data 里有RegExp、Error对象,则序列化的结果将只得到空对象;

const a = {
    b: new RegExp(/\d/),
    c: new Error('错误')
}
console.log(a) //
console.log(JSON.parse(JSON.stringify(a)))
 

? 如果 data 里有NaN、Infinity和-Infinity,则序列化的结果会变成null

const a = {
    b: NaN,
    c: 1.7976931348623157E+10308,
    d: -1.7976931348623157E+10308,
}
console.log(JSON.parse(JSON.stringify(a)))//{b: null, c: null, d: null}

② 手写递归方法:
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

function deepClone(target){
    if(target !== null && typeof target === 'object'){ //判断 target 是不是引用类型。     
       let result = Object.prototype.toString.call(target) === "[object Array]" ? [] : {};
        for (let k in target){
           //hasOwnProperty表示是否有自己的属性。这个方法会查找一个对象是否有某个属性,但是不会去查找它的原型链。 
            if (target.hasOwnProperty(k)) {
                result[k] = deepClone(target[k])
            }
        }
        return result;
    }else{
        return target;
    }
}

? hasOwnProperty(链接跳转至详情:https://blog.csdn.net/i_dont_know_a/article/details/84324051)

/**ruoyi
 * This is just a simple version of deep copy
 * Has a lot of edge cases bug
 * If you want to use a perfect deep copy, use lodash's _.cloneDeep
 */
function deepCopy(source) {
        if (!source && typeof source !== 'object') {  判断source 是不是引用类型。
          throw new Error('error arguments', 'deepClone')
        }
        const targetObj = source.constructor === Array ? [] : {} 
        Object.keys(source).forEach((keys) => {
          if (source[keys] && typeof source[keys] === 'object') {
            targetObj[keys] = deepCopy(source[keys])
          } else {
            targetObj[keys] = source[keys]
          }
        })
        return targetObj
      }
      //   测试
      var obj = {
        //原数据,包含字符串、对象、函数、数组等不同的类型
        name: 'test',
        main: {
          a: 1,
          b: 2,
        },
        fn: function () {},
        friends: [1, 2, 3, [22, 33]],
      }
      let query = deepCopy(obj)
      query.friends[2]=4
console.log(obj)
console.log(query)
function deepClone(target) {
    // 获取数据类型
    function getType(target) {
        return Object.prototype.toString.call(target)
    }
    //判断数据是不是引用类型
    function isObject(target) {
        return target !== null && (typeof target === 'object' || typeof target === 'function');
    }
    //处理不需要遍历的应引用类型数据
    function handleOherData(target) {
        const type = getType(target);
        switch (type) {
            case "[object Date]":
                return new Date(target)
            case "[object RegExp]":
                return cloneReg(target)
            case "[object Function]":
                return cloneFunction(target)

        }
    }
    //拷贝Symbol类型数据
    function cloneSymbol(targe) {
        const a = String(targe); //把Symbol字符串化
        const b = a.substring(7, a.length - 1); //取出Symbol()的参数
        return Symbol(b); //用原先的Symbol()的参数创建一个新的Symbol
    }
    //拷贝正则类型数据
    function cloneReg(target) {
        const reFlags = /\w*$/;
        const result = new target.constructor(target.source, reFlags.exec(target));
        result.lastIndex = target.lastIndex;
        return result;
    }
    //拷贝函数
    function cloneFunction(targe) {
        //匹配函数体的正则
        const bodyReg = /(?<={)(.|\n)+(?=})/m;
        //匹配函数参数的正则
        const paramReg = /(?<=\().+(?=\)\s+{)/;
        const targeString = targe.toString();
        //利用prototype来区分下箭头函数和普通函数,箭头函数是没有prototype的
        if (targe.prototype) { //普通函数
            const param = paramReg.exec(targeString);
            const body = bodyReg.exec(targeString);
            if (body) {
                if (param) {
                    const paramArr = param[0].split(',');
                    //使用 new Function 重新构造一个新的函数
                    return new Function(...paramArr, body[0]);
                } else {
                    return new Function(body[0]);
                }
            } else {
                return null;
            }
        } else { //箭头函数
            //eval和函数字符串来重新生成一个箭头函数
            return eval(targeString);
        }
    }
    /**
     * 遍历数据处理函数
     * @array 要处理的数据
     * @callback 回调函数,接收两个参数 value 每一项的值 index 每一项的下标或者key。
     */
    function handleWhile(array, callback) {
        let index = -1;
        const length = array.length;
        while (++index < length) {
            callback(array[index], index);
        }
    }
    function clone(target, map) {
        if (isObject(target)) {
            let result = null;
            if (getType(target) === "[object Array]") {
                result = []
            } else if (getType(target) === "[object Object]") {
                result = {}
            } else if (getType(target) === "[object Map]") {
                result = new Map();
            } else if (getType(target) === "[object Set]") {
                result = new Set();
            }

            // 解决循环引用
            if (map.has(target)) {
                return map.get(target);
            }
            map.set(target, result);

            if (getType(target) === "[object Map]") {
                target.forEach((value, key) => {
                    result.set(key, clone(value, map));
                });
                return result;
            } else if (getType(target) === "[object Set]") {
                target.forEach(value => {
                    result.add(clone(value, map));
                });
                return result;
            } else if (getType(target) === "[object Object]" || getType(target) === "[object Array]") {
                const keys = getType(target) === "[object Array]" ? undefined : Object.keys(target);

                function callback(value, key) {
                    if (keys) {
                        // 如果keys存在则说明value是一个对象的key,不存在则说明key就是数组的下标。
                        key = value
                    }
                    result[key] = clone(target[key], map)
                }
                handleWhile(keys || target, callback)
            } else {
                result = handleOherData(target)
            }
            return result;
        } else {
            if (getType(target) === "[object Symbol]") {
                return cloneSymbol(target)
            } else {
                return target;
            }
        }
    }
    let map = new WeakMap;
    const result = clone(target, map);
    map = null;
    return result
}

loadsh:_.cloneDeep()
jQuery中extend( )

let obj = {
    childs: ['Jack', 'Tom'],
    age: 45
}
let newObj = $.extend(true, {}, obj)
newObj.childs[0] = 'Jone'
console.log(newObj.childs[0]) //----'Jone'
console.log(obj.childs[0]) //-----'Jack'

8.总结:

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-25 10:29:45  更:2022-01-25 10:30:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:01:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码