前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端面试(8)拷贝

前端面试(8)拷贝

作者头像
leader755
发布2022-03-09 14:48:43
2910
发布2022-03-09 14:48:43
举报
image.png
image.png

js 的基本数据类型的赋值,就是值传递。引用类型对象的赋值是将对象地址的引用赋值。这时候修改对象中的属性或者值,会导致所有引用这个对象的值改变。如果想要真的复制一个新的对象,而不是复制对象的引用,就要用到对象的深拷贝。

数据类型(基本数据类型和引用数据类型)

基本数据类型(栈内存,引用值,深拷贝)

代码语言:javascript
复制
var a = 3;
let b = a;
b = 4;
console.log(a, b); //a=3,b=4

引用数据类型(堆内存,引用址,指针指向该地址。浅拷贝)

代码语言:javascript
复制
console.log("引用数据类型");
let arr1 = [1, 2];
let arr2 = arr1;
arr2.push(3);
console.log(arr1, arr2); //[1,2,3] [1,2,3]

浅拷贝

1.‘=’赋值。只是将对象的引用赋值

代码语言:javascript
复制
const a = { name: "xiaoMing", age: 20 };
const b = a;
console.log(b); //{name:'xiaoMing',age:20};
a.name = "xiaohong";
console.log(b); //{name:'xiaohong',age:20}

###

2.Object.assign()

Object.assign 是 ES6 的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。Object.assgin 只能深拷贝第一层, 深层的还是浅拷贝, 记住这个就行了。

代码语言:javascript
复制
Object.assign(target, ...sources);

参数: target:目标对象。 sources:任意多个源对象。 返回值:目标对象会被返回。

代码语言:javascript
复制
let target = {};
let source = { a: "koala", b: { name: "程序员成长指北" } };
Object.assign(target, source);
console.log(target); // { a: 'koala', b: { name: '程序员成长指北' } }

source.a = "smallKoala";
source.b.name = "程序员成长指北哦";
console.log(source); // { a: 'smallKoala', b: { name: '程序员成长指北哦' } }
console.log(target); // { a: 'koala', b: { name: '程序员成长指北哦' } }

//Object.assign 是浅拷贝,拷贝的是对象的引用值,如果为引用类型对象时,一级属性为深拷贝,对象中有二级属性的话,则二级属性以后都是浅拷贝。

3.扩展运算符(…)

代码语言:javascript
复制
let obj = { a: 1, b: { c: 1 } };
let obj2 = { ...obj };
obj.a = 2;
console.log(obj); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj.b.c = 2;
console.log(obj); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}

//扩展运算符是浅拷贝,拷贝的是对象的引用值,如果为引用类型对象时,一级属性为深拷贝,如果对象中有二级属性的话,则二级属性以后都是浅拷贝。

如果对象或者数组中包含子数组和子对象,那子数组或者对象为浅拷贝

原因是…遍历时那部分为对象/数组类型指向原来的地址

###

对象

代码语言:javascript
复制
var obj = { a: 1, b: 2, c: { a: 3 }, d: [4, 5] };
var obj1 = obj;
var obj2 = JSON.parse(JSON.stringify(obj)); //深拷贝常用方法
var obj3 = { ...obj };
var obj4 = Object.assign({}, obj);
obj.a = 999;
obj.c.a = -999;
obj.d[0] = 123;
console.log(obj1); //{a: 999, b: 2, c: { a: -999 },d: [123, 5]}
console.log(obj2); //{a: 1, b: 2, c: { a: 3 },d: [4, 5]}
console.log(obj3); //{a: 1, b: 2, c: { a: -999 },d: [123, 5]}
console.log(obj4); //{a: 1, b: 2, c: { a: -999 },d: [123, 5]}

数组

代码语言:javascript
复制
var arr = [1, 2, 3, [4, 5], { a: 6, b: 7 }];
var arr1 = JSON.parse(JSON.stringify(arr)); //深拷贝常用方法
var arr2 = arr;
var arr3 = [...arr];
var arr4 = Object.assign([], arr);
console.log(arr === arr1); //false
console.log(arr === arr2); //true
console.log(arr === arr3); //false
console.log(arr === arr4); //false
arr[0] = 999;
arr[3][0] = -999;
arr[4].a = 123;

console.log(arr1); //[1, 2, 3, [4, 5], {a: 6, b: 7}]
console.log(arr2); //[999, 2, 3, [-999, 5], {a: 123, b: 7}]
console.log(arr3); //[1, 2, 3, [-999, 5], {a: 123, b: 7}]
console.log(arr4); //[1, 2, 3, [-999, 5], {a: 123, b: 7}]

深拷贝

1.手动复制

代码语言:javascript
复制
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;

console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到

console.log(obj2);
// { a: 10, b: 100, c: 30 }

2.JSON 做字符串转换

用 JSON.stringify 把对象转成字符串,再用 JSON.parse 把字符串转成新的对象。

代码语言:javascript
复制
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;

console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则。只能序列化对象的可枚举的自有属性。

弊端

1.如果 obj 里面有时间对象,则 JSON.stringify 后再 JSON.parse 的结果,时间将只是字符串的形式,而不是对象的形式

代码语言:javascript
复制
eg:
var test = {
    name: 'a',
    date: [new Date(1536627600000), new Date(1540047600000)],
  };
  let b;
  b = JSON.parse(JSON.stringify(test));
console.log(b);

解决方法,将 new Date()变为字符串,new Date().toString()

2.如果 obj 里有 RegExp(正则表达式的缩写)、Error 对象,则序列化的结果将只得到空对象;

代码语言:javascript
复制
const test = {
  name: "a",
  date: new RegExp("\\w+"),
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = "test";
//console.error('ddd', test, copyed);
console.log(copyed);

3.如果 obj 里有函数,undefined,则序列化的结果会把函数或 undefined 丢失;

代码语言:javascript
复制
const test = {
  name: "a",
  date: function hehe() {
    console.log("fff");
  },
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = "test";
console.error("ddd", test, copyed);

4.如果 obj 里有 NaN、Infinity 和-Infinity,则序列化的结果会变成 null

5.JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果 obj 中的对象是有构造函数生成的, 则使用 JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的 constructor;

代码语言:javascript
复制
function Person(name) {
  this.name = name;
  console.log(name);
}
const liai = new Person("liai");
const test = {
  name: "a",
  date: liai,
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = "test";
console.error("ddd", test, copyed);

6.如果对象中存在循环引用的情况也无法正确实现深拷贝; 总结: 用法简单,然而使用这种方法会有一些隐藏的坑:因为在序列化 JavaScript 对象时,所有函数和原型成员会被有意忽略。 通俗点说,JSON.parse(JSON.stringfy(X)),其中 X 只能是 Number, String, Boolean, Array, 扁平对象,即那些能够被 JSON 直接表示的数据结构。

3.递归拷贝

代码语言:javascript
复制
function deepClone(initalObj, finalObj) {
  var obj = finalObj || {};
  for (var i in initalObj) {
    var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if (prop === obj) {
      continue;
    }
    if (typeof prop === "object") {
      obj[i] = prop.constructor === Array ? [] : {};
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }
  return obj;
}
var str = {};
var obj = { a: { a: "hello", b: 21 } };
deepClone(obj, str);
console.log(str.a);

4.使用 Object.create()方法

直接使用 var newObj = Object.create(oldObj),可以达到深拷贝的效果。

代码语言:javascript
复制
function deepClone(initalObj, finalObj) {
  var obj = finalObj || {};
  for (var i in initalObj) {
    var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if (prop === obj) {
      continue;
    }
    if (typeof prop === "object") {
      obj[i] = prop.constructor === Array ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }
  return obj;
}

5.jquery

jquery 有提供一个$.extend 可以用来做 Deep Copy。

代码语言:javascript
复制
var $ = require("jquery");
var obj1 = {
  a: 1,
  b: { f: { g: 1 } },
  c: [1, 2, 3],
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false

6.第三方函数

还有一些其它的第三方函数库有深拷贝 function,如 lodash。

文章引用:

https://blog.csdn.net/ljw1412/article/details/79651725 https://www.jianshu.com/p/52db1d0c1780 https://segmentfault.com/a/1190000016440069

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据类型(基本数据类型和引用数据类型)
    • 基本数据类型(栈内存,引用值,深拷贝)
      • 引用数据类型(堆内存,引用址,指针指向该地址。浅拷贝)
      • 浅拷贝
        • 1.‘=’赋值。只是将对象的引用赋值
          • 2.Object.assign()
            • 3.扩展运算符(…)
              • 如果对象或者数组中包含子数组和子对象,那子数组或者对象为浅拷贝
                • 对象
                  • 数组
                  • 深拷贝
                    • 1.手动复制
                      • 2.JSON 做字符串转换
                        • 弊端
                          • 3.递归拷贝
                            • 4.使用 Object.create()方法
                              • 5.jquery
                                • 6.第三方函数
                                • 文章引用:
                                相关产品与服务
                                文件存储
                                文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档