Giter VIP home page Giter VIP logo

Comments (43)

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 17

@fondadam 单独看这个结果,当然是 c = { value: { 3: 1 }},然而这其实是个陷阱,因为当执行前一句 var b = extend(true, obj1, obj2)的时候, obj1 的值已经发生了改变~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 5

@TaurusWood 我觉得写得很好呀,文章中的写法是抽自 jQuery 的 extend 的写法,考虑到了很多情况,比如不同类型之间的拷贝、循环引用,甚至是性能上的考虑,比如 for 循环高于 for in ,直接遍历 arguments 对象而非先转换成数组再遍历等,你的写法相对简化,但相对的,实现的功能也少了一点。我觉得这个看需求吧,如果现在的可以满足你的需求,那就是很好的~

from blog.

xunan007 avatar xunan007 commented on May 2, 2024 3

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.

zhanba avatar zhanba commented on May 2, 2024 2

楼主的深复制方法只支持json对象,并不完美啊。可以参考http://jerryzou.com/posts/dive-into-deep-clone-in-javascript/

from blog.

TaurusWood avatar TaurusWood commented on May 2, 2024
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.

fondadam avatar fondadam commented on May 2, 2024

为什么
var c = extend(true, obj2, obj1) 答案会是

c = {
    value: [5, 6, 7]
}

感觉应该是

c = {
     value: {
         3: 1
    }
}

from blog.

fondadam avatar fondadam commented on May 2, 2024

@mqyqingfeng 哎呀 掉坑里了

话说你的blog都讲的好详细,谢谢分享。

from blog.

cobish avatar cobish commented on May 2, 2024

extend 第一版中的 src 变量好像赋值后没用到哦

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@cobish 哈哈,确实如此,感谢指出~

from blog.

1391020381 avatar 1391020381 commented on May 2, 2024
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.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@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.

1391020381 avatar 1391020381 commented on May 2, 2024

@mqyqingfeng 是不是当 target[name]是对象的时候,src保存的是target[name]的指针副本,
当再次给它赋值时,只是改变了src的指向,而target[name]没有变化。即前面参数传递的意思是一样的。

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@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.

1391020381 avatar 1391020381 commented on May 2, 2024

对的不是对象的时候,也是重新给src赋值,不会影响value.a。大神回复,我受宠若惊,我会一直关注大神的 @mqyqingfeng

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@1391020381 过奖了,大家一起相互交流讨论,有的时候,我可能因为某些原因没能及时回复,还请不要介意~

from blog.

zzzzzyb avatar zzzzzyb commented on May 2, 2024

// 要求不能为空 避免 extend(a,,b) 这种情况
if (options != null) {
........
}
参数如果是空的,按道理获取值是undefined,这个不应该判断是options!=undefined么,怎么是null,这个没看明白哈,求指教

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@zzzzzyb 因为 null == nullundefined == null 的结果都为 true,所以 options != null ,如果 options 为 null 或者 undefine,结果都为 false,所以无论是 extend(a,,b) 或者你传 extend(a, null, b)都是可以跳过这个参数的

from blog.

zzzzzyb avatar zzzzzyb commented on May 2, 2024

不严格的话,undefined==undefined,也是true,就结果来说用undefined来判断也是一样的吧@mqyqingfeng

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@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.

zzzzzyb avatar zzzzzyb commented on May 2, 2024

@mqyqingfeng 确实更严谨点,谢谢大佬指点

from blog.

liujuntao123 avatar liujuntao123 commented on May 2, 2024

感觉ES6的Object.assign函数和extend很相似,不过assign使用的也是浅拷贝

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@liujuntao123 确实是这样的,我觉得正是 extend 这种函数的广泛应用才导致了 assign 函数的诞生~

from blog.

lizhongzhen11 avatar lizhongzhen11 commented on May 2, 2024

非常感谢作者的讲解,一开始看有点不懂,后来带着敲,自己理解懂了点,然后自己仿着写了一遍,也弄了个深拷贝,只支持数组和普通对象,循环引用没过滤,技术比较菜写这个都写了很久,害羞的丢个链接让前辈们帮我看看不足之处,我感觉我写的 i f判断太多。
https://github.com/lizhongzhen11/myStudy/blob/master/jq/extend.js

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@lizhongzhen11 写得很不错哈~ 直接看确实会有点难度啦,最好还是自己敲一遍,边敲边理解,效果会更好。写代码的时候注意哪些东西是重复书写的,就可以将其提取出来,也算是一个优化,比如 Object.prototype.toString.call(source) === '[object Object]' 和 Object.prototype.toString.call(source) === '[object Array]' 被重复的用到,就可以提取成 isObject 和 isArray 函数,这样代码的语义会更好~

from blog.

lizhongzhen11 avatar lizhongzhen11 commented on May 2, 2024

哈哈哈哈哈,好开心能得到前辈的点评,我会继续努力的!!!

from blog.

Tan90Qian avatar Tan90Qian commented on May 2, 2024

@DEVINNN dalao的思路非常赞~不过这种实现的话,每次只能从一个对象继承属性吧?需要继承多个对象的时候需要多次执行该方法。有个简单的修改:既然已经用了es6的默认参数功能,那么for (let key in obj) { if (obj.hasOwnProperty(key)) {xxx},这一段在没有else分支的情况下,可以用Object.keys(obj).forEach(key => {xxx})来代替吧(不过不确定会不会性能较差)。

from blog.

xunan007 avatar xunan007 commented on May 2, 2024

@Tan90Qian 大佬谈不上,你写的文章质量都很高😄
代码是参考了知乎上一篇深拷贝“找爸爸”的思路。写的不那么仔细,感谢提出不足。
这个代码确实是只考虑拷贝一个对象的属性。

from blog.

Tan90Qian avatar Tan90Qian commented on May 2, 2024

@DEVINNN 认错人了吧233 我只是一个读者,不是作者,还没毕业的萌新一枚

from blog.

xunan007 avatar xunan007 commented on May 2, 2024

@Tan90Qian 同,大三哈~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Tan90Qian @StevenXN 后生可畏呀,两位同学!(๑•̀ㅂ•́)و✧

from blog.

tr2v avatar tr2v commented on May 2, 2024

求解释一下最终版里的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.

onexwolf avatar onexwolf commented on May 2, 2024
// 第一个参数不传布尔值的情况下,target 默认是第一个参数
var target = arguments[0] || {};

对于这句注释,如果第一个参数传入false,得到的结果是{},所以是不是可以理解成如果传入false,就不会把第二个参数当成target,而是直接使用{},然后从第二个参数开始copy呢。

from blog.

gxr404 avatar gxr404 commented on May 2, 2024
if (copyIsArray) {
    copyIsArray = false; 
    src = src && Array.isArray(src) ? src : [];

}

大佬 这段代码中 copyIsArray 重新赋值false是 有必要的吗? 求解

from blog.

NameWjp avatar NameWjp commented on May 2, 2024

@gxr404 要的,如果这里不重新赋值的话会影响下一次for循环数组的判断

from blog.

xianping-yan avatar xianping-yan commented on May 2, 2024

使用Object.create({})创建的对象通过isPlainObject函数后返回false,也就会导致这个对象不会递归调用extend方法。这样是不是就没有达到深度拷贝的效果呢?

from blog.

wweggplant avatar wweggplant commented on May 2, 2024

有个问题,检测循环引用的部分,感觉有问题

var a = {
    a:{
        b:{
            c:{
                d:b
            }
        }
    }
}
var b = {
    a:{
        b:{
            c:{
                d:a
            }
        }
    }
}
var c = extend(a, b);

结果是
image

不知道是否是因为对于循环检测的定义不一样造成的

from blog.

lazyken avatar lazyken commented on May 2, 2024

if (target === copy) {continue; }是直接跳过当前循环,那么最后name:undefined是什么时候变成undefined的呢?这个地方有点糊涂,想不明白

from blog.

Richard-zhao93 avatar Richard-zhao93 commented on May 2, 2024

if (target === copy) {continue; }是直接跳过当前循环,那么最后name:undefined是什么时候变成undefined的呢?这个地方有点糊涂,想不明白

我的理解是

var  a = {
    b: 1
}
console.log(a.c) // undefined

对象中没有赋值的属性,取值都为 undefined

from blog.

lazyken avatar lazyken commented on May 2, 2024

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.

lazyken avatar lazyken commented on May 2, 2024

大佬,你的循环引用的例子有点问题:
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.

bingyuea avatar bingyuea commented on May 2, 2024

大佬,这篇所讲的extend函数 感觉和上篇所说的深拷贝是一回事吧,这样理解对吧

from blog.

gdmec07150725 avatar gdmec07150725 commented on May 2, 2024

ES6的Object.assign好像就是extend 第一版的实现

from blog.

muyudou avatar muyudou commented on May 2, 2024

if (target === copy) {continue; } 感觉进不去,这里是不是有问题啊?无法判断循环引用
截屏2023-06-09 19 22 44

from blog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.