没想到js对象内容这么多,单独摘出来。

特性

数据属性

  • configurable
  • enumerable
  • writable
  • value

相信看过vue源码,或者实现过深拷贝会熟悉这部分内容。如何读取?

  1. Object.getOwnPropertyDescriptors(obj)

利用这个方法可以获取到对象上的各个数据属性的具体情况。

也可以直接使用 Object.freeze(obj)冻结,只是冻结一层。

属性遍历

也谈对象属性遍历

继承

JS基础之原型、继承、面向对象

实战:深浅拷贝

最初也提到了数据有引用类型。

浅拷贝

  • Object.assign 和 … 拷贝对象
  • concat 拷贝数组

略过。

深拷贝

JSON.stringify 一招鲜

如果对象里有嵌套。那不行了,还是得深拷贝
首先是JSON.stringify 这样有问题:

  • 忽略函数、undefined、symbol
  • 拷贝Date会变成字符串
  • 不能拷贝不可枚举,不能拷贝对象的原型链
  • 拷贝RegExp会变成空对象
  • 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
  • 不能解决循环引用的对象

一般情况下可以干。

lodash deepClone

lodash 的深拷贝函数 https://lodash.com/docs/#cloneDeep

手写1

要考虑:

  • 多层嵌套
  • 特殊类型

先写一个基础:递归+for…in

  1. function deepClone(obj) {
  2. let cloneObj = {};
  3. for (let key in obj) {
  4. // 如果属性是引用
  5. if (typeof obj[key] === "object") {
  6. // 递归
  7. cloneObj[key] = deepClone(obj[key]);
  8. } else {
  9. // primitvie value最简单
  10. cloneObj[key] = obj[key];
  11. }
  12. }
  13. return cloneObj;
  14. }

这里只解决了一部分问题,但:

  • 不可枚举
  • symbol
  • 循环引用
  • 这种方法只是针对普通的引用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;

手写2

  • 不可枚举属性 和 symbol属性?Reflect.ownKeys
  • 如果是Date 和RegExp 生成新的实例返回
  • 通过 Object.getOwnPropertyDescriptiors 获取对象定义的特性
  • Object.create 创建新对象,继承传入原对象的原型链
  • 循环引用?weakMap做hash表,如果真有循环引用,返回WeakMap存储的值
  1. // 忽略null,且是引用类型object或者function
  2. const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
  3. const deepClone = function (obj, hash = new WeakMap()) {
  4. // 处理date
  5. if (obj.constructor === Date)
  6. return new Date(obj) // 日期对象直接返回一个新的日期对象
  7. // 处理 RegExp
  8. if (obj.constructor === RegExp)
  9. return new RegExp(obj) //正则对象直接返回一个新的正则对象
  10. // 如果存在相互引用,则从map中取出之前拷贝的结果对象并返回以便形成相互引用关系
  11. if (hash.has(obj)) return hash.get(obj)
  12. // 处理原型链,和不可枚举
  13. let allDesc = Object.getOwnPropertyDescriptors(obj)
  14. let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  15. hash.set(obj, cloneObj)
  16. // 少不了的迭代
  17. for (let key of Reflect.ownKeys(obj)) {
  18. cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
  19. }
  20. return cloneObj
  21. }
  • Reflect
  • Object.getOwnPropertyDescriptiors
  • weakMap

map的key可以是任意值,而 WeakMap 的key只能是对象。