Comments (43)
@fondadam 单独看这个结果,当然是 c = { value: { 3: 1 }}
,然而这其实是个陷阱,因为当执行前一句 var b = extend(true, obj1, obj2)
的时候, obj1 的值已经发生了改变~
from blog.
@TaurusWood 我觉得写得很好呀,文章中的写法是抽自 jQuery 的 extend 的写法,考虑到了很多情况,比如不同类型之间的拷贝、循环引用,甚至是性能上的考虑,比如 for 循环高于 for in ,直接遍历 arguments 对象而非先转换成数组再遍历等,你的写法相对简化,但相对的,实现的功能也少了一点。我觉得这个看需求吧,如果现在的可以满足你的需求,那就是很好的~
from blog.
jQuery中$.extend
没有办法深拷贝存在自身引用的对象,一旦出现自身引用会爆栈的。$.extend
对于循环引用的处理并不彻底。
这是我自己的实现方式,比$.extend
简单,但是加入了对象自环的处理。
/**
* obj: 待拷贝的对象
* target: 目标对象
* parent: target的各个父级
* _target: 用来分解target,如果有值,必有_target.target[_target.key] === target,主要用来while赋值用。(出现自引用)
**/
function deepCopy(obj, target, parent = null, _target = null) {
target = target || {};
let _parent = parent;
while (_parent) {
if (_parent.parentObj === obj) {
_target.target[_target.key] = obj;
// 不能直接给target赋值,否则引用的指针丢失,只能给target的属性赋值
// target = obj (错!!!)
// 所以才要把target[key]拆分成target和key
return;
}
_parent = _parent.parent;
}
Object.keys(obj).forEach(function(key) {
let currentCopy = obj[key];
if (typeof currentCopy === 'object' && currentCopy !== null) {
target[key] = currentCopy.constructor === Array ? [] : {};
deepCopy(
currentCopy,
target[key],
{
parentObj: obj, // 当前target的直接父级
parent: parent // 保存target的所有非直接父级
},
{
// 把target[key]拆分成target和key,供while赋值,否则对传入的target直接赋值会导致指针丢失
target: target,
key: key
}
);
} else {
target[key] = currentCopy;
}
});
return target;
}
from blog.
楼主的深复制方法只支持json对象,并不完美啊。可以参考http://jerryzou.com/posts/dive-into-deep-clone-in-javascript/
from blog.
var isObject = function (data) {
return Object.prototype.toString.call(data) === '[object Object]'
}
var extend = function(deep) {
var sources = typeof deep === 'boolean' && deep ? Array.prototype.slice.call(arguments, 1) : Array.prototype.slice.call(arguments)
var i = 0;
var obj = {};
for (; i < sources.length; i++) {
if (!isObject(sources[i])) {
console.error("Function[extend] parmas must be Object")
return false
}
for (var key in sources[i]) {
if (deep === true && isObject(sources[i][key]) && obj[key]) {
obj[key] = extend(deep, obj[key], sources[i][key])
continue
}
if (sources[i].hasOwnProperty(key)) {
obj[key] = sources[i][key]
}
}
}
return obj;
}
我没有考虑数组对象的情况,只是一个简单的对象的复制。我只是觉得没必要写的你那么繁琐,比如target
,i
,我觉得这些变量必要性都不大的,可以像我这样简写,不知道你对这种写法有啥看法,或者改进的建议
from blog.
为什么
var c = extend(true, obj2, obj1)
答案会是
c = {
value: [5, 6, 7]
}
感觉应该是
c = {
value: {
3: 1
}
}
from blog.
@mqyqingfeng 哎呀 掉坑里了
话说你的blog都讲的好详细,谢谢分享。
from blog.
extend 第一版中的 src
变量好像赋值后没用到哦
from blog.
@cobish 哈哈,确实如此,感谢指出~
from blog.
function extend() {
// 默认不进行深拷贝
var deep = false;
var name, options, src, copy;
var length = arguments.length;
// 记录要复制的对象的下标
var i = 1;
// 第一个参数不传布尔值的情况下,target默认是第一个参数
var target = arguments[0] || {};
// 如果第一个参数是布尔值,第二个参数才是target
if (typeof target == 'boolean') {
deep = target;
target = arguments[i] || {};
i++;
}
// 如果target不是对象,我们无法进行复制的所以设为{}
if (typeof target !== 'object') {
target = {}
}
for (; i < length; i++) { // 从i 开始 没有deep i = 1 有deep i = 2
options = arguments[i]
if (options !== null) { //不为null和undefined
for (name in options) {
src = target[name]
copy = options[name]
if (deep && copy && typeof copy == 'object') {
console.log(deep, src, copy)
src = extend(deep, src, copy)
// target[name] = extend(deep,src,copy)
} else if (copy !== undefined) {
target[name] = copy
}
}
}
}
return target
}
var obj1 = {
a: 1,
b: {
c: 2
}
}
var obj2 = {
b: {
c: [5],
}
}
var d = extend(true, obj1, obj2)
console.log(d); // {a:1,b:{c:2}}
只是在深拷贝的时候把 target[name]换成了 src
怎么会这样?
from blog.
@1391020381 我搞错了,第二版是没有这个问题的,这是因为:
if (deep && copy && typeof copy == 'object') {
console.log(deep, src, copy)
// wrong 这样并不会修改 target[name]的值
src = extend(deep, src, copy)
// right
target[name] = extend(deep,src,copy)
} else if (copy !== undefined) {
target[name] = copy
}
from blog.
@mqyqingfeng 是不是当 target[name]是对象的时候,src保存的是target[name]的指针副本,
当再次给它赋值时,只是改变了src的指向,而target[name]没有变化。即前面参数传递的意思是一样的。
from blog.
@1391020381 正是如此,不过无论 target[name] 是不是对象,修改 src 的值都不会改变 target[name] 的值,举个例子:
// 不是对象
var value = {
a: 1
}
var src = value.a;
src = 2;
console.log(value.a); // 1
// 是对象
var value = {
a: {
num: 1
}
}
var src = value.a;
src = 2;
console.log(value.a); // {num: 1}
from blog.
对的不是对象的时候,也是重新给src赋值,不会影响value.a。大神回复,我受宠若惊,我会一直关注大神的 @mqyqingfeng
from blog.
@1391020381 过奖了,大家一起相互交流讨论,有的时候,我可能因为某些原因没能及时回复,还请不要介意~
from blog.
// 要求不能为空 避免 extend(a,,b) 这种情况
if (options != null) {
........
}
参数如果是空的,按道理获取值是undefined,这个不应该判断是options!=undefined么,怎么是null,这个没看明白哈,求指教
from blog.
@zzzzzyb 因为 null == null
和 undefined == null
的结果都为 true,所以 options != null ,如果 options 为 null 或者 undefine,结果都为 false,所以无论是 extend(a,,b) 或者你传 extend(a, null, b)都是可以跳过这个参数的
from blog.
不严格的话,undefined==undefined,也是true,就结果来说用undefined来判断也是一样的吧@mqyqingfeng
from blog.
@zzzzzyb 所以也可以啦~ 不过 null 更短呀~ <( ̄︶ ̄)>
除此之外,值得一提的是,在低版本的浏览器中,undefined 的值是可以被更改的,举个例子:
window.undefined = null;
console.log(window.abc === undefined); // false
当然大多时候,不会出现这样的问题,但是有可能曲折的出现:
var n = window.abc; // 实际上 abc 并没有被设置,n 为 undefined
window[n]='text'; // 不小心更改了 undefined 的值
console.log(window.abc === undefined); // false
所以有些开发者可能更倾向于使用 null 进行判断
from blog.
@mqyqingfeng 确实更严谨点,谢谢大佬指点
from blog.
感觉ES6的Object.assign函数和extend很相似,不过assign使用的也是浅拷贝
from blog.
@liujuntao123 确实是这样的,我觉得正是 extend 这种函数的广泛应用才导致了 assign 函数的诞生~
from blog.
非常感谢作者的讲解,一开始看有点不懂,后来带着敲,自己理解懂了点,然后自己仿着写了一遍,也弄了个深拷贝,只支持数组和普通对象,循环引用没过滤,技术比较菜写这个都写了很久,害羞的丢个链接让前辈们帮我看看不足之处,我感觉我写的 i f判断太多。
https://github.com/lizhongzhen11/myStudy/blob/master/jq/extend.js
from blog.
@lizhongzhen11 写得很不错哈~ 直接看确实会有点难度啦,最好还是自己敲一遍,边敲边理解,效果会更好。写代码的时候注意哪些东西是重复书写的,就可以将其提取出来,也算是一个优化,比如 Object.prototype.toString.call(source) === '[object Object]' 和 Object.prototype.toString.call(source) === '[object Array]' 被重复的用到,就可以提取成 isObject 和 isArray 函数,这样代码的语义会更好~
from blog.
哈哈哈哈哈,好开心能得到前辈的点评,我会继续努力的!!!
from blog.
@DEVINNN dalao的思路非常赞~不过这种实现的话,每次只能从一个对象继承属性吧?需要继承多个对象的时候需要多次执行该方法。有个简单的修改:既然已经用了es6的默认参数功能,那么for (let key in obj) { if (obj.hasOwnProperty(key)) {xxx}
,这一段在没有else分支的情况下,可以用Object.keys(obj).forEach(key => {xxx})
来代替吧(不过不确定会不会性能较差)。
from blog.
@Tan90Qian 大佬谈不上,你写的文章质量都很高😄
代码是参考了知乎上一篇深拷贝“找爸爸”的思路。写的不那么仔细,感谢提出不足。
这个代码确实是只考虑拷贝一个对象的属性。
from blog.
@DEVINNN 认错人了吧233 我只是一个读者,不是作者,还没毕业的萌新一枚
from blog.
@Tan90Qian 同,大三哈~
from blog.
@Tan90Qian @StevenXN 后生可畏呀,两位同学!(๑•̀ㅂ•́)و✧
from blog.
求解释一下最终版里的clone有什么用直接这样写不行吗
if (copyIsArray) {
copyIsArray = false;
src = src && Array.isArray(src) ? src : [];
} else {
src = src && isPlainObject(src) ? src : {};
}
target[name] = extend(deep, src, copy);
from blog.
// 第一个参数不传布尔值的情况下,target 默认是第一个参数
var target = arguments[0] || {};
对于这句注释,如果第一个参数传入false,得到的结果是{},所以是不是可以理解成如果传入false,就不会把第二个参数当成target,而是直接使用{},然后从第二个参数开始copy呢。
from blog.
if (copyIsArray) {
copyIsArray = false;
src = src && Array.isArray(src) ? src : [];
}
大佬 这段代码中 copyIsArray 重新赋值false是 有必要的吗? 求解
from blog.
@gxr404 要的,如果这里不重新赋值的话会影响下一次for循环数组的判断
from blog.
使用Object.create({})创建的对象通过isPlainObject函数后返回false,也就会导致这个对象不会递归调用extend方法。这样是不是就没有达到深度拷贝的效果呢?
from blog.
有个问题,检测循环引用的部分,感觉有问题
var a = {
a:{
b:{
c:{
d:b
}
}
}
}
var b = {
a:{
b:{
c:{
d:a
}
}
}
}
var c = extend(a, b);
不知道是否是因为对于循环检测的定义不一样造成的
from blog.
if (target === copy) {continue; }
是直接跳过当前循环,那么最后name:undefined
是什么时候变成undefined
的呢?这个地方有点糊涂,想不明白
from blog.
if (target === copy) {continue; }
是直接跳过当前循环,那么最后name:undefined
是什么时候变成undefined
的呢?这个地方有点糊涂,想不明白
我的理解是
var a = {
b: 1
}
console.log(a.c) // undefined
对象中没有赋值的属性,取值都为 undefined
from blog.
if (target === copy) {continue; }
是直接跳过当前循环,那么最后name:undefined
是什么时候变成undefined
的呢?这个地方有点糊涂,想不明白我的理解是
var a = { b: 1 } console.log(a.c) // undefined对象中没有赋值的属性,取值都为 undefined
var a = {name : b};
var b = {name : a}
原本不是循环引用嘛,name都是有值的,extend后name都变成undefined了,它们是在哪里变成undefined的呢?
后来我想明白了,请看我下面的评论
from blog.
大佬,你的循环引用的例子有点问题:
var a = {name : b};
var b = {name : a}
var c = extend(a, b);
console.log(c);
---------------------------------
虽然var a的时候,b变量提升了,但是此时b是undefined。所以,这个例子里并不是循环引用。
实际结果是:
a = {name: undefined}
b= {name: {name: undefined}}
from blog.
大佬,这篇所讲的extend函数 感觉和上篇所说的深拷贝是一回事吧,这样理解对吧
from blog.
ES6的Object.assign好像就是extend 第一版的实现
from blog.
if (target === copy) {continue; } 感觉进不去,这里是不是有问题啊?无法判断循环引用
from blog.
Related Issues (20)
- 大佬您好,看完大有收获,但是遇到了个问题,不太会解释它的执行上下文入栈出栈顺序,关于尾递归,顺序是怎样呢? HOT 1
- 冴羽答读者问:如何在工作中打造影响力,带动同事?
- 无
- 冴羽答读者问:如何学习更有计划性、提升更稳更快? HOT 4
- 冴羽答读者问:过程比结果重要吗? HOT 1
- 冴羽答读者问:冴羽,你为什么写起了鸡汤? HOT 3
- 聊聊 npm 的语义化版本(Semver) HOT 4
- How to create Backlinks 😤
- 思考题第2题
- 可以理解为原型是prototype,原型链是通过__proto__ 链接起来的吗
- React 之 createElement 源码解读 HOT 8
- React 之元素与组件的区别 HOT 1
- React 之 Refs 的使用和 forwardRef 的源码解读
- React 之 Context 的变迁与背后实现 HOT 1
- 第一段不报错啊,刚试过了,会打印1
- how can i HOT 1
- Hosting of Blog Issue
- 全局对象
- 为啥每次都要创建一个 Child函数来new 子类?现在不都是 const p1 = new Person(); const p2 = new Person()吗
- 文档内容中文件结构的错位 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from blog.