articles
articles copied to clipboard
Javascript深浅拷贝
Javascript有六种基本数据类型(也就是简单数据类型),它们分别是:Undefined,Null,Boolean,Symbol,Number和String。还含有一种复杂数据类型,就是对象
注意Undefined和Null的区别,Undefined类型只有一个值,就是undefined,Null类型也只有一个值,也就是null Undefined其实就是已声明未赋值的变量输出的结果 null其实就是一个不存在的对象的结果
var c;
console.log(c)//undefined
console.log(document.getElementById('wsscat'))//没有id为wsscat的节点,输出null
简单的数据类型和复杂的数据类型有以下重要的区别
对于简单数据类型
它们值在占据了内存中固定大小的空间,并被保存在栈内存中。当一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本,还有就是不能给基本数据类型的值添加属性
var a = 1;
var b = a;
a.attr = 'wsscat';
console.log(a.attr)//undefined
上面代码中a就是简单数据类型(Number),b就是a的副本,它们两者都占有不同位置但相等的内存空间
对于复杂的数据类型
复杂的数据类型即引用类型,它的值是对象,保存在堆内存中,包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针。从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象。
var obj = {
name:'wsscat',
age:0
}
var obj2 = obj;
obj2['c'] = 5;
console.log(obj);//Object {name: "wsscat", age: 0, c: 5}
console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}
我们可以看到obj赋值给obj2后,当我们更改其中一个对象的属性值,两个对象都发生了改变,究其原因局势因为obj和obj2这两个变量都指向同一个指针,赋值只是复制了指针,所以当我们改变其中一个的值就会影响另外一个变量的值
浅拷贝
其实这段代码就是浅拷贝,有时候我们只是想备份数组,但是只是简单让它赋给一个变量,改变其中一个,另外一个就紧跟着改变,但很多时候这不是我们想要的
var obj = {
name:'wsscat',
age:0
}
var obj2 = obj;
obj2['c'] = 5;
console.log(obj);//Object {name: "wsscat", age: 0, c: 5}
console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}
深拷贝
数组
对于数组我们可以使用slice()
和concat()
方法来解决上面的问题
slice
var arr = ['wsscat', 'autumns', 'winds'];
var arrCopy = arr.slice(0);
arrCopy[0] = 'tacssw'
console.log(arr)//['wsscat', 'autumns', 'winds']
console.log(arrCopy)//['tacssw', 'autumns', 'winds']
concat
var arr = ['wsscat', 'autumns', 'winds'];
var arrCopy = arr.concat();
arrCopy[0] = 'tacssw'
console.log(arr)//['wsscat', 'autumns', 'winds']
console.log(arrCopy)//['tacssw', 'autumns', 'winds']
对象 对象我们可以定义一个新的对象并遍历新的属性上去实现深拷贝
var obj = {
name:'wsscat',
age:0
}
var obj2 = new Object();
obj2.name = obj.name;
obj2.age = obj.age
obj.name = 'autumns';
console.log(obj);//Object {name: "autumns", age: 0}
console.log(obj2);//Object {name: "wsscat", age: 0}
当然我们可以封装好一个方法来处理对象的深拷贝,代码如下
var obj = {
name: 'wsscat',
age: 0
}
var deepCopy = function(source) {
var result = {};
for(var key in source) {
if(typeof source[key] === 'object') {
result[key] = deepCopy(source[key])
} else {
result[key] = source[key]
}
}
return result;
}
var obj3 = deepCopy(obj)
obj.name = 'autumns';
console.log(obj);//Object {name: "autumns", age: 0}
console.log(obj3);//Object {name: "wsscat", age: 0}
Javascript数组存放函数
在javascript中函数也是一种数据,能够像操作一个对象对它进行操作。并且javascript不进行数据类型检查,数组可以存放任何东西,在下面代码中我们不但在数组中存放了函数,并且也可以在存放一个执行函数的返回值,所以数组前两个数据存放都是函数执行返回值
var funcA = function() {
console.log("funcA");
return "hello funA";
}
var funcB = function() {
console.log("funcB");
return "hello funB";
}
var funcC = function() {
console.log("funcC");
return "hello funC";
}
var arr = [funcA(), funcB(), funcC];
console.log(arr);
arr[2]();
输出的结果如下
对象中不含有函数的话。JSON解析反解析就行了
深拷贝
数组 对于数组我们可以使用slice()和concat()方法来解决上面的问题 silce ...
应该是手滑了吧,silce
-> slice
谢谢,已更正~
ES6中有 Object.assign() 方法,应该也可以解决你对于深克隆的需求吧?
concat 和slice对对象数组没有效果的?
@GreenMelon Object.assign()方法不适于深拷贝,MDN上有说明
@yeyuqiudeng concat和slice主要是针对基本数据类型数组的深拷贝,牵涉到多层复杂数据类型即对象的就要再对{a:{b:1}
进行深拷贝。有兴趣的话,可对Underscore、Lodash 和 JQuery的源码进行学习,相关文章《深入剖析 JavaScript 的深复制》
@flynntsc 嗯嗯,谢谢
我这里实现了对象包括数组的深拷贝:
Object.prototype.deepCopy=function(){
var obj=null;//用于最后返回一个对象,这个对象是深复制的结果
for(var attr in this){//遍历这个对象的每一个属性
if(this.hasOwnProperty(attr)){//主要是递归自有属性
if(typeof (this[attr]==='object')){//如果对象的属性是一个对象,就递归复制它的每一个属性
if(this[attr]===null){//如果对象为null
obj[attr]=null;
}else if(Object.prototype.toString(this[attr])==='[object Array]'){//如果是个数组
obj[attr]=[];
for(var i=0;i<this[attr].length;i++){
obj[attr].push(this[attr][i].deepCopy());
}
}else{//object
obj[attr]=this[attr].deepCopy();
}
}else{
obj[attr]=this[attr];
}
}
}
return obj;
}
test.html:12 Uncaught RangeError: Maximum call stack size exceeded 报错了 @specialCoder
基本类型在ES6
还有一个Symbol
,漏掉了
嗯嗯 by @Wscats
不错不错👍🏻
写得挺好的
涨知识啦
没看到对循环引用的处理啊
function deepCopy(jsonData){
return JSON.parse(JSON.stringify(jsonData));
};
这样也可以吧
@plane-hjh 这样有其他问题 具体可以看我 这篇文章
@kscript 循环引用解决方案可以看这个具体可以看这篇文章
slice、concat、assign都是浅拷贝,如果数据中没有函数,可以直接用JSON来实现拷贝,如果有函数,就得用递归来实现深拷贝,递归实现时,函数依然是公用的; 更高级的可以参考楼上的 文章https://github.com/LiuL0703/blog/issues/19
我这里实现了对象包括数组的深拷贝:
Object.prototype.deepCopy=function(){ var obj=null;//用于最后返回一个对象,这个对象是深复制的结果 for(var attr in this){//遍历这个对象的每一个属性 if(this.hasOwnProperty(attr)){//主要是递归自有属性 if(typeof (this[attr]==='object')){//如果对象的属性是一个对象,就递归复制它的每一个属性 if(this[attr]===null){//如果对象为null obj[attr]=null; }else if(Object.prototype.toString(this[attr])==='[object Array]'){//如果是个数组 obj[attr]=[]; for(var i=0;i<this[attr].length;i++){ obj[attr].push(this[attr][i].deepCopy()); } }else{//object obj[attr]=this[attr].deepCopy(); } }else{ obj[attr]=this[attr]; } } } return obj; }
typeof 对array和object都返回object,所以没有必要加一层array的判断吧?
function deepCopy(jsonData){ return JSON.parse(JSON.stringify(jsonData)); }; 这样也可以吧 只适合对象吧这个
js中函数也是一种引用类型数据,a,b指向两个不同的函数,执行的结果也是有区别的发自我的华为手机-------- 原始邮件 --------主题:Re: [Wscats/Good-Text-Share] Javascript深浅拷贝 (#57)发件人:燕睿涛 收件人:Wscats/Good-Text-Share 抄送:陈虎 ,Comment function() {}也算基本数据类型吗
let a = function() {console.log(123);}
let b = a
b = function() {console.log(456);}
执行上面只有,a、b是不同的两个函数,按理说,函数应该存储的也是,求解
—You are receiving this because you commented.Reply to this email directly, view it on GitHub, or mute the thread.
let deep_copy = function f(obj) {
let new_value;
if(typeof obj === 'object' && obj != null){
if(obj instanceof Array){
new_value = [];
for(let i=0;i<obj.length;i++){
if(typeof obj[i] !== 'object' || obj[i] === null){
new_value[i] = obj[i];
}else{
new_value.push(f(obj[i]));
}
}
}else{
new_value = {};
for(let item in obj){
if(obj.hasOwnProperty(item)){
if(typeof obj[item] !== 'object' || obj[item] === null){
new_value[item] = obj[item];
}else{
Object.assign(new_value, { [item]: f(obj[item])});
}
}
}
}
}else{
new_value = obj;
}
return new_value;
};
我觉的你的理解有点问题,对象的赋值操作不能算是浅拷贝。在对对象进行浅拷贝时,对象中的基本数据类型会开辟新的空间,引用类型指向的还是同一个地址。而赋值操作,是将对象B的地址指向对象A。 举个例子
var obj1 = {
name: 'zhangsan',
num: [1, 2, 3]
}
// 赋值操作
var obj2 = obj1
obj2.name = 'lisi'
console.log(obj1) // {name: "lisi", num: [1, 2, 3] }
console.log(obj2) // {name: "lisi", num: [1, 2, 3] }
// 浅拷贝
var obj3 = Object.assign({}, obj1)
obj3.name = 'wanger'
console.log(obj1) // {name: "lisi", num: [1, 2, 3] }
console.log(obj3) // {name: "wanger", num: [1, 2, 3] }
我同你认知的一样啊,将引用类型直接赋值给其他变量,只是传递了内存地址。
获取 Outlook for Androidhttps://aka.ms/ghei36
From: gudongz [email protected] Sent: Wednesday, February 13, 2019 5:47:45 PM To: Wscats/Good-Text-Share Cc: 谭峰; Comment Subject: Re: [Wscats/Good-Text-Share] Javascript深浅拷贝 (#57)
我觉的你的理解有点问题,对象的赋值操作不能算是浅拷贝。在对对象进行浅拷贝时,对象中的基本数据类型会开辟新的空间,引用类型指向的还是同一个地址。而赋值操作,是将对象B的地址指向对象A。 举个例子 var obj1 = { name: 'zhangsan', num: [1, 2, 3] } // 赋值操作 var obj2 = obj1 obj2.name = 'lisi' console.log(obj1) // {name: "lisi", num: [1, 2, 3] } console.log(obj2) // {name: "lisi", num: [1, 2, 3] } // 浅拷贝 var obj3 = Object.assign({}, obj1) obj3.name = 'wanger' console.log(obj1) // {name: "lisi", num: [1, 2, 3] } console.log(obj3) // {name: "wanger", num: [1, 2, 3] }
― You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/Wscats/Good-Text-Share/issues/57#issuecomment-463131186, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AayVkmZtc8X140WL6dPazBDB1MYAYOLFks5vM99BgaJpZM4KViK-.
如果需要深拷贝 函数和正则 呢
ES6中有 Object.assign() 方法,应该也可以解决你对于深克隆的需求吧?
assign不是深克隆