ES6时期,你真的会克隆对象吗?

原文:你真的会克隆对象吗

最先之前

在最先聊克隆之前,我们照样先来看看js数据范例。js的数据范例分为基础数据范例庞杂数据范例

  • 基础数据范例:Number、Boolean、String、Null、String、Symbol(ES6 新增)
  • 庞杂数据范例:Object,其他援用范例(Array、Date、RegExp、Function、基础包装范例(Boolean、String、Number)、Math等)都是Object范例的实例对象

克隆:基础数据 => 复制这个变量;庞杂数据 => 拷贝援用(网上的引见许多,不深入了)

罕见浅拷贝

关于对象的克隆,应当大多数人都能完成出来,能够深、浅拷贝都能想出好几种体式格局,我们先来聊聊浅拷贝。

一个罕见的浅拷贝平常是下面如许:

function shallowCopy (obj) {
  if (typeof obj !== 'object') {
    return
  }
  var newObj = obj instanceof Array ? [] : {}
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key]
    }
  }
  return newObj
}

也许更严谨一点的完成数组的推断:

Object.prototype.toString.call(arr) === '[object Array]'

彷佛是没什么题目呢,毕竟经过了很多项目的检测,网上一搜就能够涌现一大堆。

然则,我们开首引见数据范例的时刻就已说过了,ES6新增了Symbol范例,状况彷佛就有点不一样了

Symbol

Symbol是ES6中引入的原始数据范例。Symbol值经由过程Symbol函数天生,是举世无双的。同时,ES6中划定了对象的属性名有两种范例,一种是字符串,另一种就是 Symbol 范例。通常属性名属于 Symbol 范例,就不会与其他属性名发生争执。然则,随之而来的题目是,我们的for...in轮回不能遍历出该属性

Symbol 作为属性名,该属性不会涌现在
for...in
for...of轮回中,也不会被
Object.keys()
Object.getOwnPropertyNames()
JSON.stringify()返回。然则,它也不是私有属性,有一个
Object.getOwnPropertySymbols要领,能够猎取指定对象的一切 Symbol 属性名。

Symbol范例,天然有遍历Symbol范例的要领。Object.getOwnPropertySymbols + for...in的组合起来彷佛是能满足我们请求的了。嗯,看起来还不错,然则好像有点麻烦了,有无更便利一点的体式格局呢?也许新时代的男子—Reflect.ownKeys,要闪亮上台了,这个既能遍历字符串,又能遍历Symbol的死变态(请许可我这么夸他)。

Reflect.ownKeys返回一个数组,包含对象本身的一切属性,不论是属性名是
Symbol或字符串,也不论是不是可罗列

Object.assign

这个时刻熟习ES6的人也许最先有疑问了,我们已最先议论SymbolReflect.ownKeys,为何浅克隆不直接用Object.assign也许睁开运算符(...)呢?

嗯,待我吃根火腿岑寂岑寂,彷佛你说的很对!Object.assign的确是能拷贝Symbol范例的呢。然则呢,然则呢,我们是一个有寻求的猿类,多一种完成体式格局不是能让我们多相识一些坑吗?而且这类体式格局不是能让我们更天真的完成不可预知的需求吗?对,没错,是如许子的…

Object.assign这个更圆满的男子出来以后,彷佛浅拷贝部份也该完毕了,一般来讲,的确是如许。不过我们再仔细想想上面的两种体式格局,彷佛照样有点区分的呢。我们再来看看这两个男子:

  • Reflect.ownKeys返回一个数组,包含对象本身的一切属性,不论是属性名是Symbol或字符串,也不论是不是可罗列
  • Object.assign拷贝的属性是有限定的,只拷贝源对象的本身属性(不拷贝继承属性),也不拷贝不可罗列的属性

注意到了吗?这里面有一个是不是可罗列的观点,这个时刻是不是是应当慨叹我们晓得怎样完成不可预知的需求了呢。

不可罗列

我们先看个例子:

var obj = Object.create({ foo: 1 }, {
  [Symbol()]: {
    value: 1,
    enumerable: false
  },
  bar: {
    value: 2,
    enumerable: false
  },
  [Symbol()]: {
    value: 3,
    enumerable: true
  },
  baz: {
    value: 4,
    enumerable: true
  }
})

Object.assign({}, obj)  // {baz: 4, Symbol(): 3}

唉,的确是如许呢!看来Object.assign也不是我们的抱负归宿啊。我们再回过头来看看Reflect.ownKeys,上面挖的坑也该填了,我们在讲Symbol的时刻,Object.getOwnPropertySymbols + for...in直接用Reflect.ownKeys替换了,在从可罗列的角度动身看看,彷佛那里不对,for...in只能轮回遍历对象本身的和继承的可罗列的属性,且不含 Symbol。头都大了吗?来来来,喝完这杯,另有一杯,继承接着来。这么多轮回,我们来缕缕眉目:

  • for...in轮回遍历对象本身的和继承的可罗列属性(不含 Symbol 属性)。
  • Object.keys()返回一个数组,包含对象本身的(不含继承的)一切可罗列属性(不含 Symbol 属性)的键名。
  • Object.getOwnPropertyNames()返回一个数组,包含对象本身的一切属性(不含 Symbol 属性,然则包含不可罗列属性)的键名。
  • Object.getOwnPropertySymbols()返回一个数组,包含对象本身的一切 Symbol 属性的键名。
  • Reflect.ownKeys()返回一个数组,包含对象本身的一切键名,不论键名是 Symbol 或字符串,也不论是不是可罗列。

终究清楚了,也许也该完毕了吧。

慢着,彷佛上面的例子让我想到了什么!!!

属性形貌符

我们在来思索一个例子:

const source = {
  get foo() { return 1 }
};
const target = {};

Object.assign(target, source) // { foo: 1 }

彷佛并非我们想要的呢,遍历的体式格局彷佛也不适用了,这可怎样办。别急,另有Object.getOwnPropertyDescriptors能够用。

ES2017 引入了
Object.getOwnPropertyDescriptors要领,返回指定对象一切本身属性(非继承属性)的形貌对象

仔细阅读下文档,终究用Object.getOwnPropertyDescriptors+Object.getPrototypeOf胜利了呢

Object.create(
  Object.getPrototypeOf(obj), 
  Object.getOwnPropertyDescriptors(obj) 
)

写到这里,浅拷贝部份也该完毕了

完毕语

能够现实项目中并不需要处置惩罚的这么仔细,然则愿望人人对种种遍历、完成一个浅拷贝以及ES6的一些学问有一个总结和一点新的熟悉吧,本来想继承写深拷贝的,恶棍篇幅已不短,加上长夜漫漫,我想睡觉,深拷贝的题目更庞杂,我先放放,往后再说。

末了的末了,对这篇文章有兴致的朋侪,能够继承关注下一篇的深克隆,会更新的会更新的…

    原文作者:Lemo_Liu
    原文地址: https://segmentfault.com/a/1190000013056850
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞