Giter VIP home page Giter VIP logo

Comments (72)

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024 158

@mengxin-FE javaScript 我还有很多地方需要研究,倒不算透彻,不过谢谢夸奖哈~ 如果说学习方法的话,就是确定一个要研究的主题,然后大量阅读该主题相关的文章,尽量保证每篇文章都能理解,如果不能理解,第二天再看一遍,直到看懂为止,如果可以的话,再写写文章,将学到的知识梳理出来,与大家分享~

from blog.

naihe138 avatar naihe138 commented on May 2, 2024 57

我从《你不知道的 JavaScript》一书看到,工具函数 JSON.stringify(..) 在将JSON对象序列化为字符串时也用到了 ToString 。请注意, JSON 字符串化并非严格意义上的强制类型转换,因为其中也涉及 ToString 的相 关规则。
对大多数简单值来说, JSON 字符串化和 果总是字符串:toString()的效果基本相同,只不过序列化的结

JSON.stringify( 42 ); // "42" 
JSON.stringify( "42" ); // ""42""(含有双引号的字符串) 
JSON.stringify( null ); // "null" 
JSON.stringify( true ); // "true"

所有 安全的 JSON 值 (JSON-safe)都可以使用 JSON.stringify(..) 字符串化。 安全的 JSON 值是指能够呈现为有效 JSON 格式的值。

下面敲黑板划重点:

为了简单起见, 我们来看看什么是 不安全的 JSON 值 。 undefined 、 function 、 symbol (ES6+)和包含循环引用(对象之间相互引用,形成一个无限循环)的 对象 都不符合 JSON 结构标准,支持 JSON 的语言无法处理它们。

JSON.stringify(..) 在对象中遇到 undefined 、 function 和 symbol 时会自动将其忽略, 在 数组中则会返回 null (以保证单元位置不变)。

例如:

JSON.stringify( undefined ); 
JSON.stringify( function(){} );
JSON.stringify( [1,undefined,function(){},4] ); 
JSON.stringify({ a:2, b:function(){} } );
// undefined // undefined
// "[1,null,null,4]"
// "{"a":2}"

对包含循环引用的对象执行 JSON.stringify(..) 会出错。
...

from blog.

cell617 avatar cell617 commented on May 2, 2024 44

楼主对js的理解这么透彻,是怎么学的啊?

你水平是有多低啊,说他透彻 呵呵

from blog.

Tvinsh avatar Tvinsh commented on May 2, 2024 9
function deepClone (obj) {
  if (Array.isArray(obj)) {
    return obj.map(deepClone)
  } else if (obj && typeof obj === 'object') {
    var cloned = {}
    var keys = Object.keys(obj)
    for (var i = 0, l = keys.length; i < l; i++) {
      var key = keys[i]
      cloned[key] = deepClone(obj[key])
    }
    return cloned
  } else {
    return obj
  }
}

这样也可以

from blog.

KingsonCheng avatar KingsonCheng commented on May 2, 2024 6

想请问下博主,为什么深拷贝那里for (var key in obj) 之后还需要一个判断if (obj.hasOwnProperty(key)) 呢

from blog.

xiaolan92 avatar xiaolan92 commented on May 2, 2024 5

@KingsonCheng 避免拷贝继承下来的属性.

from blog.

pythonfirst avatar pythonfirst commented on May 2, 2024 2

为什么还要用hasOwnProperty判断一下啊

因为for in 不仅遍历对象自身属性,还会遍历继承的inumerable 属性,这里只拷贝自身属性。

from blog.

wuming-lzd avatar wuming-lzd commented on May 2, 2024 1

@superwtt
function Person() {} Person.prototype = { name: 'hello' } var p = new Person(); for (var key in p) { console.log(key); //name } 就是这种情况,p只是Person的实例,for...in循环却能拿到Person原型上的name属性

from blog.

justorez avatar justorez commented on May 2, 2024 1

处理循环引用的版本:

function deepClone(target, map = new WeakMap()) {
    if (map.has(target)) {
        return map.get(target)
    }

    // 特殊处理:正则、日期
    if (isRegExp(target) || isDate(target)) {
        return new target.constructor(target)
    }

    if (isObject(target)) {
        const cloneTarget = isArray(target) ? [] : {}
        map.set(target, cloneTarget)  // 缓存循环引用的拷贝结果
        for (const prop in target) {
            if (target.hasOwnProperty(prop)) {
                cloneTarget[prop] = deepClone(target[prop], map)
            }
        }
        return cloneTarget
    } else {
        return target
    }
}

拷贝函数,有两种写法貌似可以:

eval(target.toString())
// or
new Function(`return ${target.toString()}`)()

from blog.

522363215 avatar 522363215 commented on May 2, 2024

期待下一篇!!!

from blog.

zhouyingkai1 avatar zhouyingkai1 commented on May 2, 2024

养肥了再看一遍

from blog.

yunlzhang avatar yunlzhang commented on May 2, 2024

null应该特殊考虑一下吧,在深拷贝中,值为null会赋值一个空对象

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@yunlzhang 感谢指出,现在的 deepCopy 方法确实有这个问题

deepCopy({
        value: null
})

的值为:

{value: {}}

这篇的目的在于讲解深浅拷贝的概念以及深浅拷贝的思路,下一篇 《JavaScript专题之从零实现jQuery的extend》 才是讲解深浅拷贝的详细实现,在下一篇的 extend 方法就有对于 null 的处理~

from blog.

mengxin-FE avatar mengxin-FE commented on May 2, 2024

楼主对js的理解这么透彻,是怎么学的啊?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Tvinsh 确实可以,感谢分享哈~

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@naihe138 非常感谢补充,o( ̄▽ ̄)d JSON.stringify 这部分确实写得太浅薄了。

from blog.

 avatar commented on May 2, 2024

@mqyqingfeng

可以这样说吗?

=是浅拷贝

sliceconcat如果拷贝基本类型元素的数组是深拷贝,否则是浅拷贝

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@veedrin 可以,只是不知道为什么,我觉得怪怪的,好像一般不会这样描述……

from blog.

JHanLu avatar JHanLu commented on May 2, 2024

请问深拷贝的时候遇到相互引用的情况怎么处理?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@JHanLu 这个可以参照下一篇 extend 的实现方式 #33

from blog.

UNDERCOVERj avatar UNDERCOVERj commented on May 2, 2024

这个深拷贝问题很大,没有考虑dom对象,正则对象,时间对象

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@UNDERCOVERj 确实没有考虑这些场景,不过常遇到的场景都是普通对象的拷贝,这样的也够了~

from blog.

allenGKC avatar allenGKC commented on May 2, 2024

写的很清楚,谢谢楼主

from blog.

lizhongzhen11 avatar lizhongzhen11 commented on May 2, 2024

star一下以示支持

from blog.

CodeLittlePrince avatar CodeLittlePrince commented on May 2, 2024

深拷贝这样写应该会好点:

var deepCopy = function(obj) {
    if (obj === null || typeof obj !== 'object') return obj;
    ...
}

from blog.

ishowman avatar ishowman commented on May 2, 2024

Hi,你的深拷贝方法我觉得还是有点问题。如果遇到对象内嵌函数,typeof判断返回的是function,还是复制了指针而不是整个函数吧?可以看看下面代码理解我描述的问题

var arr = [function(){
    console.log(a)
}, {
    b: function(){console.log(b)
}], 
new_arr = deepCopy(arr);
arr[0] === new_arr[0] // true
new_arr[1].b === arr[1].b // true

by the way, 博主的基础真是扎实,佩服

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@ishowman 函数的复制是一个很难解决的问题呀,即使是 jQuery 的 extend 也没有去处理函数……

from blog.

youzaiyouzai666 avatar youzaiyouzai666 commented on May 2, 2024

浅拷贝:es6中有两种新方法
方法1:
` let [...spread]= [12, 5, 8, 130, 44];

//等同于:let spread = 浅克隆([12, 5, 8, 130, 44]) `

方法2:
Array.from(array)//创建一个新数组

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@youzaiyouzai666 感谢分享呀~ 这些都是 ES6 提供的很好的方法~

from blog.

Fiv5 avatar Fiv5 commented on May 2, 2024

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Fiv5 下一篇 #33 就涉及了深拷贝中的循环引用问题,不过处理的是

var a = {name : b};
var b = {name : a}
var c = extend(a, b);
console.log(c);

这种情况下导致的循环引用的问题,处理的思路是目标属性值和要复制的对象的属性值的引用是否相同,具体可以参见下一篇文章中的代码,对于本身就是循环引用的对象,正好评论中也有人提及了处理的方式,也可以参考一下~

from blog.

A-birdFlyHigner avatar A-birdFlyHigner commented on May 2, 2024

let a = {}
a = obj (手动滑稽)

from blog.

tzcodingjs avatar tzcodingjs commented on May 2, 2024

为什么这里递归调用就可以达到深拷贝呢?

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@A-birdFlyHigner 哈哈,你赢了 o(////▽////)q

from blog.

mqyqingfeng avatar mqyqingfeng commented on May 2, 2024

@Tzcodejs 嗯……我竟然不知道怎么回答……因为浅拷贝只能复制对象的一层属性,如果要复制更深层级的话,就需要用到递归吧……

from blog.

WangNianyi2001 avatar WangNianyi2001 commented on May 2, 2024

对于避免d归的深拷贝,我实现了一个:
原版
改进1
改进2
主要还是类似_.eq,用了一个栈来保存已经 copy 过的 kv 对

from blog.

JzHuangJian avatar JzHuangJian commented on May 2, 2024

真正的深拷贝

function deepClone(obj) {
var _toString = Object.prototype.toString;

    // null, undefined, non-object, function
    if (!obj || typeof obj !== 'object') {
        return obj;
        console.log("null, undefined, non-object, function");
    }

    // DOM Node
    if (obj.nodeType && 'cloneNode' in obj) {
        return obj.cloneNode(true);
        console.log("DOM Node");
    }

    // Date
    if (_toString.call(obj) === '[object Date]') {
        return new Date(obj.getTime());
        console.log("Date");
    }

    // RegExp
    if (_toString.call(obj) === '[object RegExp]') {
        var flags = [];
        if (obj.global) { flags.push('g'); }
        if (obj.multiline) { flags.push('m'); }
        if (obj.ignoreCase) { flags.push('i'); }
        // console.log("RegExp");
        return new RegExp(obj.source, flags.join(''));

    }

    var result = Array.isArray(obj) ? [] : {};
    // console.log(result);

    for (var key in obj) {
        result[key] = deepClone(obj[key]);

    }

    return result;
}

from blog.

youzaiyouzai666 avatar youzaiyouzai666 commented on May 2, 2024
//其实,简单粗暴copy;只有,function不满足
 function deepCopy(obj) {
      return new Promise((resolve) => {
        const {port1, port2} = new MessageChannel();
        port2.onmessage = ev => resolve(ev.data);
        port1.postMessage(obj);
      });
    }

    deepCopy(obj).then((copy) => {           // 请记住`MessageChannel`是异步的这个前提!
        let copyObj = copy;
        console.log(copyObj, obj)
        console.log(copyObj == obj)
    });

from blog.

daryl-z avatar daryl-z commented on May 2, 2024

我给加了个深度参数 不知道怎样 无限递归好像会卡死

// 对对象进行深拷贝
// deepin表示拷贝深度 deepin<=1 表示浅拷贝 >1表示拷贝深度 不填表示递归到底
const deepCopy = (obj, deepin) => {
  let count = 0;
  if (deepin === undefined) {
    count = undefined;
  } else {
    count = deepin - 1;
  }
  if (count < 1) {
    return obj;
  } else {
    if (typeof obj !== "object") return;
    const newObj = obj instanceof Array ? [] : {};
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        newObj[key] =
          typeof obj[key] === "object" ? deepCopy(obj[key], count) : obj[key];
      }
    }
    return newObj;
  }
};

from blog.

rutingjin avatar rutingjin commented on May 2, 2024
// 创建指定深度广度的对象
function createData(deep = 0, breadth = 0) {
    const root = {}
    let pointer = root
    for (let i = 0; i < deep; i++) {
        pointer.data = {}
        pointer = pointer.data
        for (let j = 0; j < breadth && i === deep -1; j++) {
            pointer[j] = j
        }
    }
    return root
}
JSON.parse(JSON.stringify(createData(3000)))
 // Uncaught RangeError: Maximum call stack size exceeded
// 层级够深时,会报错,这个拷贝也是不安全的

from blog.

uniquexiaobai avatar uniquexiaobai commented on May 2, 2024

@rutingjin 为什么我的电脑是4800层?这个和电脑内存有关系吧

from blog.

EvalGitHub avatar EvalGitHub commented on May 2, 2024

cloneData = (data) => {
if (Object.prototype.toString.call(data) === "[object Array]") {
var _arr = [];
for (let item of data) {
_arr.push(cloneData(item))
}
return _arr;
}
if (Object.prototype.toString.call(data) === "[object Object]") {
var _obj = {};
for (let item in data) {
_obj[item] = cloneData(data[item])
}
return _obj;
}
return data;
}
let obj = cloneData({a:1,b:2,c:{name:'tom',age:'22'},d:[1,3,4,5,6]});
console.log(obj);

from blog.

rutingjin avatar rutingjin commented on May 2, 2024

@rutingjin 为什么我的电脑是4800层?这个和电脑内存有关系吧

有可能,同一个电脑硬件下,用chrome测试和node测试层数也不一样

from blog.

pin84 avatar pin84 commented on May 2, 2024

感谢~对基础有很好的了解作用。

from blog.

xiehaitao0229 avatar xiehaitao0229 commented on May 2, 2024

点赞

from blog.

BeijiYang avatar BeijiYang commented on May 2, 2024

用 reduce 来个一行实现深拷贝的版本

const deepCopyObj = obj => Object.entries(obj).reduce((acc, [key, value]) => (
  typeof value === 'object' ? { ...acc, [key]: deepCopyFun(value) } : { ...acc, [key]: value }
), {})

const deepCopyArr = arr => arr.reduce((acc, cur) => (
  cur instanceof Array ? [...acc, deepCopyArr(cur)] : [...acc, cur]
), [])

from blog.

kakachake avatar kakachake commented on May 2, 2024

function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1)
}
function BFSDeepClone(obj) {
if(typeof obj != 'object') return
let res = obj instanceof Array ? [] : {}
const origin = [obj]
const copy = [res]
while (origin.length) {
const _obj = origin.shift()
const copyObj = copy.shift()
Object.keys(_obj).forEach(k => {
const item = _obj[k]
if (getType(item) === 'Object' || getType(item) === 'Array') {
copyObj[k] = getType(item) === 'Object' ? {} : []
origin.push(item)
copy.push(copyObj[k]) //相当于引用res>>>[k]的值
}else {
copyObj[k] = item
}
})
}
return res
}

广度遍历实现深拷贝😄

from blog.

wangyiman avatar wangyiman commented on May 2, 2024

浅拷贝也可以用var newObj = Object.assign({}, obj)

from blog.

db46rt00ors avatar db46rt00ors commented on May 2, 2024
var x = {
    a : {
        e : 6
    },
    b : 2,
    arr : [1,2,3],
    fun : function(){
        return 8
    }
}
function clone(x){
    if(x.constructor === Object){
        var obj = {}
        for(var k in x){
            obj[k] = clone(x[k])
        }
        return obj
    }else if(x.constructor === Array){
        var arr = [];
        for (var i = 0; i < x.length; i++) {
            arr[i] = clone(x[i])
        }
        return arr
    }else {
        return x
    }
}
var y = clone(x)

from blog.

MrRENGE avatar MrRENGE commented on May 2, 2024

楼主大大,深度拷贝遇到循环引用怎么去解这个问题呢?

from blog.

wuming-lzd avatar wuming-lzd commented on May 2, 2024

@cell617 你水平高,也没见你写个东西出来啊,键盘侠,就是你这种人(一般不怼人,忍不了)

from blog.

superwtt avatar superwtt commented on May 2, 2024

@KingsonCheng 避免拷贝继承下来的属性.

为啥要避免拷贝继承下来的属性?

from blog.

LeoTao avatar LeoTao commented on May 2, 2024

Date RegExp Error Promise Map Set... 写一个好的深拷贝好难呀

from blog.

superwtt avatar superwtt commented on May 2, 2024

@superwtt
function Person() {} Person.prototype = { name: 'hello' } var p = new Person(); for (var key in p) { console.log(key); //name } 就是这种情况,p只是Person的实例,for...in循环却能拿到Person原型上的name属性

所以意思是,不想把原型上的属性也拷贝过来,指向拷贝这个对象上自己的属性 对吧?

from blog.

kitiho avatar kitiho commented on May 2, 2024

感谢分享!

from blog.

zhoufanglu avatar zhoufanglu commented on May 2, 2024

这句可以优化
var newObj = obj instanceof Array ? [] : {};
可以这么写 var newObj = new obj.constructor
我还想问下,如果里面好几个元素都是一样的,请问怎么优化? 遇到面试官问了。。

from blog.

vnues avatar vnues commented on May 2, 2024

@mqyqingfeng

可以这样说吗?

=是浅拷贝

sliceconcat如果拷贝基本类型元素的数组是深拷贝,否则是浅拷贝
并不能这么说 浅拷贝和赋值不能混为一谈 之所以需要深浅拷贝是因为赋值时候 如果遇到引用类型时候 两个变量直接会相互影响 所以是为了解决这一现象而采取的方案

赋值:

  • 基本数据类型:赋值,赋值之后两个变量互不影响

  • 引用数据类型:赋,两个变量具有相同的引用,指向同一个对象,相互之间有影响 (为了解决这种场景)

from blog.

leviding avatar leviding commented on May 2, 2024

这句可以优化
var newObj = obj instanceof Array ? [] : {};
可以这么写 var newObj = new obj.constructor
我还想问下,如果里面好几个元素都是一样的,请问怎么优化? 遇到面试官问了。。

请问你所说的,好几个元素都是一样的,是什么意思?我所理解的,如果是数组,即使值一样,但它们的 index 不一样吧?如果是对象,即使它们的值一样,但它们的 key 不一样吧?

是指将多个对象/数组拷贝合并到一个对象/数组中的情况吗?不是单单地对一个数组/对象进行深/浅拷贝。

from blog.

wweggplant avatar wweggplant commented on May 2, 2024
//其实,简单粗暴copy;只有,function不满足
 function deepCopy(obj) {
      return new Promise((resolve) => {
        const {port1, port2} = new MessageChannel();
        port2.onmessage = ev => resolve(ev.data);
        port1.postMessage(obj);
      });
    }

    deepCopy(obj).then((copy) => {           // 请记住`MessageChannel`是异步的这个前提!
        let copyObj = copy;
        console.log(copyObj, obj)
        console.log(copyObj == obj)
    });

postMessage应该也能实现

from blog.

Kitaharakasusa avatar Kitaharakasusa commented on May 2, 2024

为什么还要用hasOwnProperty判断一下啊

from blog.

lazyken avatar lazyken commented on May 2, 2024

if (typeof obj !== 'object') return;
是不是return obj比较好

from blog.

tancgo avatar tancgo commented on May 2, 2024
function deepClone(obj) {
  function isObject(o) {
    return typeof o === "object" && o !== null;
  }

  if (!isObject(obj)) {
    throw new Error("非对象");
  }

  const newObj = obj instanceof Array ? [] : {};

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      const cur = obj[key];
      newObj[key] = isObject(cur) ? deepClone(cur) : cur;
    }
  }

  return newObj;
}

from blog.

lqPrototype avatar lqPrototype commented on May 2, 2024

为什么没有人说递归深拷贝有缺点的: 1. 栈溢出、2. 尾递归优化、3. 动态规划来解决.

from blog.

xsfxtsxxr avatar xsfxtsxxr commented on May 2, 2024

大佬深拷贝没考虑环么?

from blog.

CoderCxb avatar CoderCxb commented on May 2, 2024

有两个问题

typeof obj[key] === 'object' 无法判断null的情况
如果拷贝的是类的实例对象 方法无法拷贝 并且输出的对象的类型是Object而不是class
var deepCopy = function(obj) {
if (!(obj instanceof Object)) return obj;
var newObj = obj instanceof Array ? [] : Object.create(obj.proto);
for (var key of Object.getOwnPropertyNames(obj)) {
newObj[key] = obj[key] instanceof Object ? deepCopy(obj[key]) : obj[key];
}
return newObj;
}
进行了略微的修改,大佬你看看
文章写得很好,点个赞!

from blog.

growYdp avatar growYdp commented on May 2, 2024

深拷贝的实现中 好像数组还是浅拷贝

from blog.

zxk-github avatar zxk-github commented on May 2, 2024

想咨询一下
var newObj = obj instanceof Array ? [] : {}; 这一句修改修改为:
var newObject = new obj.constructr(); 存在什么风险吗

from blog.

opamine avatar opamine commented on May 2, 2024

想咨询一下
var newObj = obj instanceof Array ? [] : {}; 这一句修改修改为:
var newObject = new obj.constructr(); 存在什么风险吗

严格意义上讲两个风险差不多,从原型链方面考虑:1. 开发者会存在修改 obj 的原型的情况,此时 instanceof 判断失效,constructor 同样不起作用 2. 基于前者,开发者还可以自定义原型、修改 constructor 变量指向等

能用 typeof 和 Object.prototype.toString.call() 就优先使用这两个

from blog.

SilenceTiger avatar SilenceTiger commented on May 2, 2024

完美深复制window.XMLHttpRequest这种类型的有啥办法吗? 上面的方法丢失构造方法或者原型方法

from blog.

w2xi avatar w2xi commented on May 2, 2024

最新的浏览器可以使用 structuredClone API 来深拷贝了 structuredClone

浏览器兼容:

Chrome Firefox Edge Safari Opera
98 ✔ 94 ✔ 98 ✔ 15.4 ✔ 84 ✔

from blog.

ethanzhongyi avatar ethanzhongyi commented on May 2, 2024

hi,问下深拷贝中对于递归调用的怎么处理

from blog.

justorez avatar justorez commented on May 2, 2024

hi,问下深拷贝中对于递归调用的怎么处理

@ethanzhongyi 用 WeakMap 缓存已处理的对象

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.