В эпоху ES6 вы действительно клонируете объекты?

внешний интерфейс JavaScript ECMAScript 6

оригинал:ты действительно клонируешь объекты

до начала

Прежде чем мы начнем говорить о клонировании, давайте взглянем на типы данных js. js типы данных делятся набазовый тип данныха такжесложные типы данных.

  • Основные типы данных: число, логическое значение, строка, нуль, строка, символ (новое в ES6)
  • Сложные типы данных: объект, другие ссылочные типы (массив, дата, регулярное выражение, функция, базовые типы оболочки (логический, строковый, числовой), математические и т. д.) — все экземпляры объектов типа объекта.

Клонировать: основные данные => скопировать эту переменную; сложные данные => скопировать ссылку (есть много онлайн-представлений, а не углубленных)

обычная неглубокая копия

Что касается клонирования объектов, большинство людей должно уметь его реализовывать, и может быть несколько способов глубокого и поверхностного копирования.Давайте сначала поговорим о поверхностном копировании.

Обычная неглубокая копия обычно выглядит так:

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, могут возникнуть сомнения, и мы уже начали обсуждатьSymbolа такжеReflect.ownKeys, почему неглубокий клон не используется напрямуюObject.assignили оператор спреда (...) Шерстяная ткань?

Ну, подожди, пока я съем ветчину и успокоюсь, как будто ты прав!Object.assignдействительно копироватьSymbolтип. Но, но, мы обезьяны с преследованием, не позволит ли нам еще один способ реализации узнать больше о ямах? И не позволяет ли такой подход быть более гибкими для реализации непредсказуемых требований? Да, верно, вот так...

Object.assignПосле того, как вышел этот более совершенный человек, казалось, что мелкая копия тоже должна закончиться, что обычно и бывает. Однако, если мы внимательно подумаем о двух вышеупомянутых методах, кажется, что некоторые различия все же есть. Давайте еще раз посмотрим на этих двух мужчин:

  • Reflect.ownKeysВозвращает массив, содержащий все свойства самого объекта, независимо от того, является ли имя свойства символом или строкой, а также является ли оно перечисляемым или нет.
  • 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Можно использовать.

Представлен ES2017Object.getOwnPropertyDescriptorsМетод, возвращающий описание объекта всех его собственных свойств (не унаследованных свойств) указанного объекта

Внимательно прочитайте документ и, наконец, используйтеObject.getOwnPropertyDescriptors+Object.getPrototypeOfэто удалось?

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

Написание здесь, часть мелкого копирования должна заканчиваться

заключительные замечания

В реальном проекте может быть не так дотошно, но надеюсь у вас будет резюме и новое понимание различных обходов, реализация мелкой копии и некоторое знание ES6.Это не коротко, плюс длинная ночь, спать хочется , проблема глубокого копирования более сложная, я ее сначала отпущу, а потом расскажу о ней позже.

В конце концов, будучи учеником начальной школы в мире фронтенда, я впервые использую Nuggets для написания статьи.Хотя я использую Nuggets в течение длительного времени, я все еще немного нервничаю, чтобы опубликовать статью. , Если в статье есть какие-либо ошибки, пожалуйста, поправьте меня. В то же время друзья, которым интересна эта статья, могут продолжать обращать внимание на следующую статью.глубокий клонОбновлю, обновлю...