博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript数据类型AND深拷贝和浅拷贝的不归路
阅读量:7236 次
发布时间:2019-06-29

本文共 5617 字,大约阅读时间需要 18 分钟。

本文将向读者介绍在js中数据的拷贝,旨在让读者能在工作中或者面试中遇到相似的问题能够应用起来

首先声明一下,这批文章仅代表我自己对js的一些理解。由于笔者水平有限,如果有出错之出,还望多多谅解,指正。谢谢。当然如果你看的不爽...那么---------你顺着网线过来打我呀O(∩_∩)O哈哈~

正文开始------------------

数据类型

说到js的数据拷贝就不得不提js的数据类型。我们知道的7中数据类型:

Number、String、Boolean、Undefined、Null、Object 和es6新增的Symbol

(null undefined number boolean )都是基本类型,存栈里的数据

Null: 不存在的,没有的数据对象

Undefined: 变量声明了却没有初始化的数据。表示"缺少值",就是此处应该有一个值,但是还没有定义。

number: 顾名思义就是一个数字类型,number是存放在栈里的数据

string:string比较特殊,它其实是存在堆里的,我们拿到的只是一个地址引用。如果对js比较了解的话,那么会知道

js中string是不可变的,我们没有任何一个方法可以改变一个字符串。可以认为string是 行为与基本类型相似的不可变引用类型

Object 是引用类型,存堆里的地址

Objecty下面又有三员大将,它们都是 Object.prototype 下的熟悉

Array: 一个存放数据的集合,js中 数组可以存放任何数据。

Function: 函数,其实也是数据。我们可以把一个函数赋值给另一个变量

object: 对象,没啥好说的。

Symbol的话是一个es6新增对象,用Symbol可以创建唯一的变量名。

这个array有点意思,在有的语言数组就是数组,但是js里它确实对象,数组的key就是它的下标

// instanceof 是判断xxx 是否xxx的实例    Object instanceof Object // true    Array instanceof Object // true    Function instanceof Object // true复制代码

接下来重点来了,请大家看一段代码:

Number.constructor // ƒ Function() { [native code] }    String.constructor // ƒ Function() { [native code] }    Boolean.constructor // ƒ Function() { [native code] }    Object.constructor // ƒ Function() { [native code] }    Symbol.constructor //ƒ Function() { [native code] }复制代码

大家看到这个有陷入深深的沉思吗?为啥他们都有 constructor,为啥他们都有方法可以去调用。而且如果用 刚刚这个 instanceof 去判断会发现,string、boolean、number...都是object 的实例。

那么你知道是为什么吗?

你没猜错。在js中所以数据都是对象。all in of object。这个英文怕不怕。

接下来再看下面的代码:

Number.prototype.six = () => { console.log(666) }    const num = 123    num.six() // 666复制代码

这里能给number原型添加一个方法,并且定义的一个number 可以去掉用这个方法。已经说明了js中number 其实是基于对象。

string、Boolean 等都是如此
顺便解释一下js堆和栈的一点概念
栈(stack)为自动分配的内存空间,它由系统自动释放;而堆(heap)则是动态分配的内存,大小不定也不会自动释放。

拷贝

js的拷贝是有点复杂的,涉及到引用类型的话

let a = 1    let b = '2'    a = b    a = 3    console.log(a, b) // 3 2复制代码

这是js中基本类型的复制,没有任何问题,我们就不多扯了。就是覆盖就好。

我们来看看js中引用类型的拷贝

let obj = {        a: 1,        b: {            b: 1        }    }    let arr = [1, 2, 3]    let arr2 = arr    let obj2 = obj        arr[1] = '666'    obj.a = '777'    console.log(obj.a) // 666    console.log(obj2.a) // 666    console.log(arr[1]) // 777    console.log(arr2[1]) // 777复制代码

这就是引用类型的尿性,有的时候很烦,有的时候却很有用。因为js中引用类型给你访问的是一个地址。这个地址对应着数据的位置,我们像复制普通类型一样复制引用类型,就等于直接把地址给了别人。改动的时候大家改的其实是同一个数据。

那么问题来了,我就是复制一个引用类型的所以数据,但是我不想被我复制的对象收到影响怎么办。

浅拷贝

let obj = {        a: 1,        b: {            b: 1        }    }    let arr = [1, 2, 3]    let obj2 = { ...obj }    let arr2 = [ ...arr ]    obj2.a = '666'    arr2[1] = '777'    console.log(obj.a) // 1    console.log(obj2.a) // 666    console.log(arr[1]) // 1    console.log(arr2[1]) // 777复制代码

好像没问题了,似乎很简单的样子啊。

我们接着上面的代码

...    obj2.b.b = '888'    console.log(obj.b.b) // { b: '888' }     console.log(obj2.b.b) // { b: '888' }复制代码

震惊!似乎又出现了刚刚的问题。没错这就是因为 ...是es6的扩展运算符。他只是拷贝了第一层变量。后面的依然还是直接复制地址。

深拷贝

先来一个简单的,百度一搜索一大堆的看看

function clone(params) {    var obj = {};    for(var i in params) {        if (params.hasOwnProperty(i)) {            if (typeof params[i] === 'object') {                obj[i] = clone(params[i]); // 通过判断是否对象而进行递归            } else {                obj[i] = params[i];            }        }    }    return obj;}let obj = {    a: 1,    b: {        b: 1    }}let obj2 = clone(obj)obj2.b.b = '888'console.log(obj.b.b, obj2.b.b) // 1  888复制代码

这就实现了一个简单的深拷贝,但是它有一些问题。主要就是考虑的不够严谨,比如一些数据没有做到兼容。比如set、 map、weakset、weakmap、array... 是不是感觉很麻烦。当然其实我们有个简单的方法。

function clone2(params) {    return JSON.parse(JSON.stringify(params));}复制代码

平时我一般工作中拷贝一些json类型的数据就用这个....简单粗暴。

这个方法其实也有一些问题。就是没法克隆 函数和正则匹配等.当然如果只是简单的数据还是可以的。

这个方法是我在前端早读课中的一篇文章看到的一个方法。这里就厚颜无耻的clone了下来,如果大家有兴趣的话可以去关注前端早读课。看那篇关于js对象拷贝的文章。

function cloneForce(x) {    // =============    const uniqueList = []; // 用来去重    // =============    let root = {};    // 循环数组    const loopList = [        {            parent: root,            key: undefined,            data: x,        }    ];    while(loopList.length) {        // 深度优先        const node = loopList.pop();        const parent = node.parent;        const key = node.key;        const data = node.data;        // 当第一次循环的时候key是undefined,所以靠谱到第一级,后面的都是拷贝到子级        let res = parent;        if (typeof key !== 'undefined') {            res = parent[key] = {};        }        // =============        let uniqueData = find(uniqueList, data);        if (uniqueData) {            parent[key] = uniqueData.target;            continue; // 判断数据是否存在,如果存在就不继续这次循环了        }        // 数据不存在        // 保存源数据,在拷贝数据中对应的引用        uniqueList.push({            source: data,            target: res,        });        // =============        for(let k in data) {            if (data.hasOwnProperty(k)) {                if (typeof data[k] === 'object') {                    // 如果有object 就push禁 loopList,进行下一次转换                    loopList.push({                        parent: res,                        key: k,                        data: data[k],                    });                } else {                    res[k] = data[k];                }            }        }    }    return root;}function find(arr, item) {    for(let i = 0; i < arr.length; i++) {        if (arr[i].source === item) {            return arr[i];        }    }    return null;}复制代码

最后在向大家介绍一种神奇的方法。

let obj = {
a:1, b: {
b:1}}var channel = new MessageChannel(); var port1 = channel.port1; var port2 = channel.port2; port1.onmessage = function(event) { let obj2 = event.data obj2.b.b = '666' console.log(obj.b.b,obj2.b.b) // 1 666 } port2.onmessage = function(event) { console.log("port2收到来自port1的数据:" + event.data); } port2.postMessage(obj);复制代码

是不是很神奇。不过这个也无法解决对象循环引用的问题。并且它是异步的。

转载地址:http://dvgfm.baihongyu.com/

你可能感兴趣的文章
不用判断语句,求两个数的最大值
查看>>
我的友情链接
查看>>
cd命令
查看>>
Spark UDF变长参数的二三事儿
查看>>
python学习笔记06-列表
查看>>
linux桌面与命令行模式切换 ②
查看>>
linux中KS详解
查看>>
正则 表达式
查看>>
TCP/IP基础
查看>>
使用Eclipse远程调试Tomcat
查看>>
ansible安装与部署
查看>>
python-M2Crypto安装方法
查看>>
手机写host 工具
查看>>
Android:time&date、TimePicker、DatePicker
查看>>
初始化weblogic密码
查看>>
Linux Top 命令解析
查看>>
一个C#项目 在引用References中有个引用项上有个黄色三角加感叹号 导致报错
查看>>
PPT无法修改默认打开程序的问题
查看>>
2011年度IT博客大赛50强之 jimmy_lixw
查看>>
WP8开发日志(2):MVC设计模式入门
查看>>