We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在讲深浅拷贝之前,我们需要了解 JS 中的数据类型。
在 JS 中,数据类型分为两大类,一类是基础类型,一类是引用类型。
截至目前,JS 中基础类型有这7种:
引用类型有1种:
其中,Object又可以分为以下7种:
Object
那么基础类型和引用类型有什么不同呢?
原来,基础类型的变量存储在栈内存中,而引用类型的变量在栈内存中存储的是一个地址,这个地址指向存储在堆内存中的真正的值。(参考JavaScript中变量是如何存储的)
当将一个基础类型的变量赋值给另一个变量的时候,新变量得到的是全新的值,跟原变量没有任何关联,修改新变量的值也不会影响到原变量。但是引用类型就不一样了,当把一个引用类型的变量赋值给另一个变量的时候,新变量得到的只是原变量的地址,它们都指向同一块内存,当对任何一个变量作出更改,另一个变量也会被改变。
明确了基础类型和引用类型的区别,下面我们可以开始深浅拷贝的内容了。
先对浅拷贝下个定义:基于原对象创建一个新对象,复制原对象的每一个属性,如果属性值是基础类型,则复制得到该值;如果属性值是引用类型,则复制得到该值的地址。当新对象或原对象的其中一个被更改,基础类型的属性值不会互相影响,引用类型的属性值会跟着改变。
实际上,JS 原生已经实现了很多浅拷贝方法:
Array:concat(),slice(),Array.from(),展开运算符(...) Object:Object.assign()
通过以下例子了解浅拷贝的特性。
数组浅拷贝例子:
const obj = [ { a: 1 }, { b: 2 } ]; const copyObj = [].concat(obj); copyObj[0] = 'newVal'; copyObj[1].b = 'newB'; console.log(copyObj); // ['newVal', { b: 'newB' }] console.log(obj); // [{ a: 1 }, { b: 'newB' }]
可以看到,在浅拷贝的时候,更改数组第一层的值并不会影响原对象,而更改更深层级的值就影响到了原对象。
再来看下对象(狭义)浅拷贝的例子:
const obj = { a: 1, b: { c: 2 } } const copyObj = Object.assign({}, obj); copyObj.a = 'newA'; copyObj.b.c = 'newC'; console.log(copyObj); // { a: 'newA', b: { c: 'newC' } } console.log(obj); // { a: 1, b: { c: 'newC' } }
通过以上代码可以看到,浅拷贝对象的情况下,更改对象基础类型的属性值,没有影响到另一个对象;但是更改引用类型的属性值,另一个对象也被影响到了。这跟数组浅拷贝表现一致。
OK,我们已经了解浅拷贝的特性了,让我们开始对深拷贝的探索。
照例,我们对深拷贝进行下定义:基于原对象创建一个新对象,复制原对象的每一个属性,不管属性值是基础类型还是引用类型,都完全复制其属性值。对新对象或者原对象的任何更改,都不会影响另一个对象。
那么,了解了什么是深拷贝,是时候开始动手实现了。
键盘敲起来。。。
深拷贝实现思路主要有两种,一种是通过转为字符串,切断与原对象的联系,非常简单直观;另一种思路是通过递归。
下面我们先看第一种思路。
这种方式就是使用JSON.stringify将对象转为字符串,再用JSON.parse将字符串转回对象。
JSON.stringify
JSON.parse
const obj = { a: 1, b: { c: 2 } } const copyObj = JSON.parse(JSON.stringify(obj));
简单粗暴的方式,总是会有诸多缺点:
我们要记住的就是,如果对象中有这些类型的属性值,就不要使用 JSON 序列化这种方式进行深拷贝。
下面我们看下更好一些的方案。
递归实现深拷贝的思路是,遍历对象的每个自身属性,如果该属性值是基本数据类型,则直接复制到新对象;如果该属性值是引用类型,则使用递归重走以上流程。
function deepClone(obj) { let result = Array.isArray(obj) ? [] : {}; for (const k in obj) { if (obj.hasOwnProperty(k)) { if (typeof obj[k] === "object" && obj[k] !== null) { result[k] = deepClone(obj[k]); } else { result[k] = obj[k]; } } } return result; }
但是这样的实现方式也还是存在一些问题:
针对以上问题,我们得到最终版深拷贝方法:
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null); const deepClone = function (obj, hash = new WeakMap()) { if (obj.constructor === Date) { return new Date(obj); // 日期对象直接返回一个新的日期对象 } if (obj.constructor === RegExp) { return new RegExp(obj); //正则对象直接返回一个新的正则对象 } //如果循环引用了就用 weakMap 来解决 if (hash.has(obj)) return hash.get(obj); let allDesc = Object.getOwnPropertyDescriptors(obj); //遍历传入参数所有键的特性 let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc); //继承原型链 hash.set(obj, cloneObj); for (let key of Reflect.ownKeys(obj)) { cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key] } return cloneObj; }
经过这样的完善,绝大部分情况都考虑到了,可以满足各种场景的开发需求了。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在讲深浅拷贝之前,我们需要了解 JS 中的数据类型。
JS 数据类型
在 JS 中,数据类型分为两大类,一类是基础类型,一类是引用类型。
截至目前,JS 中基础类型有这7种:
引用类型有1种:
其中,
Object
又可以分为以下7种:那么基础类型和引用类型有什么不同呢?
基础类型 VS 引用类型
原来,基础类型的变量存储在栈内存中,而引用类型的变量在栈内存中存储的是一个地址,这个地址指向存储在堆内存中的真正的值。(参考JavaScript中变量是如何存储的)
当将一个基础类型的变量赋值给另一个变量的时候,新变量得到的是全新的值,跟原变量没有任何关联,修改新变量的值也不会影响到原变量。但是引用类型就不一样了,当把一个引用类型的变量赋值给另一个变量的时候,新变量得到的只是原变量的地址,它们都指向同一块内存,当对任何一个变量作出更改,另一个变量也会被改变。
明确了基础类型和引用类型的区别,下面我们可以开始深浅拷贝的内容了。
什么是浅拷贝?
先对浅拷贝下个定义:基于原对象创建一个新对象,复制原对象的每一个属性,如果属性值是基础类型,则复制得到该值;如果属性值是引用类型,则复制得到该值的地址。当新对象或原对象的其中一个被更改,基础类型的属性值不会互相影响,引用类型的属性值会跟着改变。
实际上,JS 原生已经实现了很多浅拷贝方法:
Array:concat(),slice(),Array.from(),展开运算符(...)
Object:Object.assign()
通过以下例子了解浅拷贝的特性。
数组浅拷贝例子:
可以看到,在浅拷贝的时候,更改数组第一层的值并不会影响原对象,而更改更深层级的值就影响到了原对象。
再来看下对象(狭义)浅拷贝的例子:
通过以上代码可以看到,浅拷贝对象的情况下,更改对象基础类型的属性值,没有影响到另一个对象;但是更改引用类型的属性值,另一个对象也被影响到了。这跟数组浅拷贝表现一致。
OK,我们已经了解浅拷贝的特性了,让我们开始对深拷贝的探索。
什么是深拷贝?
照例,我们对深拷贝进行下定义:基于原对象创建一个新对象,复制原对象的每一个属性,不管属性值是基础类型还是引用类型,都完全复制其属性值。对新对象或者原对象的任何更改,都不会影响另一个对象。
那么,了解了什么是深拷贝,是时候开始动手实现了。
键盘敲起来。。。
深拷贝如何实现
深拷贝实现思路主要有两种,一种是通过转为字符串,切断与原对象的联系,非常简单直观;另一种思路是通过递归。
下面我们先看第一种思路。
通过 JSON 序列化实现深拷贝
这种方式就是使用
JSON.stringify
将对象转为字符串,再用JSON.parse
将字符串转回对象。简单粗暴的方式,总是会有诸多缺点:
我们要记住的就是,如果对象中有这些类型的属性值,就不要使用 JSON 序列化这种方式进行深拷贝。
下面我们看下更好一些的方案。
递归实现深拷贝
递归实现深拷贝的思路是,遍历对象的每个自身属性,如果该属性值是基本数据类型,则直接复制到新对象;如果该属性值是引用类型,则使用递归重走以上流程。
但是这样的实现方式也还是存在一些问题:
针对以上问题,我们得到最终版深拷贝方法:
经过这样的完善,绝大部分情况都考虑到了,可以满足各种场景的开发需求了。
The text was updated successfully, but these errors were encountered: