-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
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
javaScript中浅拷贝和深拷贝的实现 #3
Comments
if(obj === null || typeof obj !== "object"){ |
@zhoujiamin 之前漏了,已修复哈。 |
在“自己动手实现一个拷贝方法”中,有一个小bug。 在if语句中判断时会出现一点小问题: 文章写的很好,学到很多,谢谢分享。 |
@Ts799498164 已修复,多谢指正。 |
@wengjq 有个bug,
|
对象直接用等号和用shallowClone(copyObj),Object.assign有什么区别呢? |
@qw789 对象等号赋值是地址引用,修改其中一个,另一个的值也会随之改变。 |
@mrxf 是的,但是浅复制呢?和对象等号赋值有区别? |
浅拷贝是和深拷贝比较的,深拷贝可以理解为递归进行浅拷贝。 let a = {
name: 'LiHua',
habits: ['a', 'b']
}
|
@mrxf 那浅拷贝是不是意味着只能让第一层互相互相不影响。普通等于赋值连第一层都做不到。。 |
@mrxf 谢谢。我大概明白了 |
@VimMing 已修复,多谢指正。 |
你好。
但是为什么没有考虑某个属性为函数的情况呢?函数非基本数据类型,不可直接赋值的。 |
for下面存在循环引用,用什么方法比较好? 看到了你说JSON在循环引用下是无法正确处理的。 |
@wengjq 你这个方法可行不? |
行不行 如果能够copy jq的元素就没问题 |
首先非常感谢作者大大的付出,让我学到了不少,尤其是$,extend(true,{},obj),以前都没接触过,项目做得少,实践的知识也就少,在作者这里倒是好好了解了一番,感谢! |
厉害 |
if (deep) {
if ($.isArray(value) || $.isObject(value)) {
target[name] = copy(value,deep);
} else if ($.isFunction(value)) {
target[name] = new Function("return " + value.toString())();
}
} else {
target[name] = value;
} 感觉这里的逻辑有点问题。如果deep为false的话是没错的,但是如果deep为true的话,当value值是基本类型的值的话,没有target[name] = value。同时感觉这里判断$.isFunction(value)这个步骤我觉得是多余的,因为在你递归调用copy的时候也会判断,function的情况可以合并到上面去。 因为自己复制你的代码到控制台执行的时候报错了,所以也没有验证,不清楚你的代码是不是真的有bug。以上供博主参考。感谢博主的分享交流 |
var x = {}
var y = {}
x.i = y
y.i = x
var z = copy(x,true) 这种情况下的循环引用好像没解决 |
这段这么写应该是因为闭包的原因吧,现在for循环里面用let或者const可以解决,不用使用立即执行函数
|
@caistrong 感谢指出错误之处,第一个问题已经更正,第二个问题的话是为了复制对象属性是函数的情况。然后那段循环引用的我还没用深入研究。等我研究了再来答。 |
@Borkes 嗯嗯。也可以哈 |
执行完之后c 并没有拷贝到a中数组的值, |
@shayminsky 已更正 |
实现数据类型判断这么写是不是更简约一点儿?望指正! |
@quokaa 没毛病,版本不一样而已 |
等号赋值不等于浅拷贝?🤔 还有引用类型不是将指针保存在堆内存中吗,为什么开头说
|
可以结合图形理解下: |
hi,抱歉看到你的代码里有一个小错误。
这是为了防止拷贝无限循环的,正确的应该为
|
函数的方法和属性拷贝? |
这一个片段for (var i = types.length; i--;) { |
你好,很高兴看到你的代码,我把copy函数整理一下,变更了一下代码执行顺序,会不会更好?
|
if (deep) { |
if (value === obj) { |
@liuyanansiyu |
要不要考虑下Date这种类型的深拷贝? |
请问如果属性是函数而且还有参数, return new Function('return' + value.toString())是否没有考虑参数问题呢? |
有参数的方法: const a = {
fn:function(s,t) {
console.log(s);
console.log(t)
}
}
const args = a.fn.length;
let str = [];
for(let i = 0 ; i<args;i++) {
str.push("a"+i);
}
str = str.join(',')
const l = new Function( str,'return '+ a.fn.toString())();
a.fn(1,2); // 1 2 |
这边循环引用是否要考虑祖父节点 |
稍作修改的: (function($) {
"use strict";
var types = "Array,Object,String,Date,RegExp,Function,Boolean,Number,Null,Undefined".split(
","
);
for (let i = types.length; i--; ) {
$["is" + types[i]] = str =>
Object.prototype.toString.call(str).slice(8, -1) === types[i];
}
return $;
})(window.$ || (window.$ = {})); //类型判断
function copy(obj, deep = false, hash = new WeakMap()) {
if (hash.has(obj)) {
return hash.get(obj);
}
if ($.isFunction(obj)) {
return new Function("return " + obj.toString())();
} else if (obj === null || typeof obj !== "object") {
return obj;
} else {
var name,
target = $.isArray(obj) ? [] : {},
value;
hash.set(obj, target);
for (name in obj) {
value = obj[name];
if (deep) {
if ($.isArray(value) || $.isObject(value)) {
target[name] = copy(value, deep, hash);
} else if ($.isFunction(value)) {
target[name] = new Function("return " + value.toString())();
} else {
target[name] = value;
}
} else {
target[name] = value;
}
}
return target;
}
}
var x = {};
var y = {};
x.i = y;
y.i = x;
var z = copy(x, true);
console.log(x, z);
var a = {};
a.a = a;
var b = copy(a, true);
console.log(a, b); |
1、javaScript的变量类型
(1)基本类型:
5种基本数据类型Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。
(2)引用类型:
存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
例如:数组拷贝
2、浅拷贝的实现
2.1、简单的引用复制###
2.2、Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
3、深拷贝的实现
3.1、Array的slice和concat方法
Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。之所以把它放在深拷贝里,是因为它看起来像是深拷贝。而实际上它是浅拷贝。原数组的元素会按照下述规则拷贝:
如果向两个数组任一中添加了新元素,则另一个不会受到影响。例子如下:
可以看出,concat和slice返回的不同的数组实例,这与直接的引用复制是不同的。而从另一个例子可以看出Array的concat和slice并不是真正的深复制,数组中的对象元素(Object,Array等)只是复制了引用。如下:
3.2、JSON对象的parse和stringify
JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝。
这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。
4、jQuery.extend()方法源码实现
jQuery的源码 - src/core.js #L121源码及分析如下:
jQuery的extend方法使用基本的递归思路实现了浅拷贝和深拷贝,但是这个方法也无法处理源对象内部循环引用,例如:
5、自己动手实现一个拷贝方法
The text was updated successfully, but these errors were encountered: