В этом посте мы рассмотрим, как создаются объекты, затем поговорим о некоторых интересных свойствах свойств объекта, но сначала давайте начнем с рассмотрения того, как мы обычно создаем объекты, используя литералы объектов:

const car = { numberplate: '1234' };

Мы создали объект, содержащий свойство numberplate со значением '1234'. За кулисами javascript использует метод Object.create для создания этого объекта. Вот как это выглядит:

const car = Object.create(
  Object.prototype,
  {
    numberplate: {
      writable: true,
      enumerable: true,
      configurable: true,
      value: '1234',
    }
  },
);

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

Обратите внимание, что вы также можете использовать Object.defineProperties или Object.defineProperty для указания дескрипторов свойств для любого заданного объекта. Подробнее об этом здесь и здесь.

Давайте посмотрим, за что отвечает каждый дескриптор свойства.

Дескрипторы свойств

Доступно для записи

Дескриптор свойства writable определяет, может ли значение свойства (в данном случае numberplate) быть изменено по сравнению с его начальным значением.

'use strict'
const car = { numberplate: '1234' };
Object.defineProperty(
  car,
  'numberplate',
  { writable: false }
);
car.numberplate = '0000'; // -> Uncaught TypeError

Обратите внимание, что если вы не используете строгий режим (с 'use strict') в приведенном выше примере, интерпретатор не выдаст ошибку, и значение не будет изменено.

Есть предостережение, о котором следует знать. Дескриптор свойства writable останавливает перемещение указателя свойства. это означает, что если свойство указывает на объект, члены этого объекта все еще могут быть изменены, например:

'use strict'
const plane = { 
  numberplate: { value: '1234' },
};
Object.defineProperty(
  plane, 
  'numberplate', 
  { writable: false }
);
plane.numberplate.value = '0000';
plane.numberplate.value // -> '0000'
plane.numberplate = {}; // -> Uncaught TypeError

Исчисляемый

По умолчанию свойства объекта являются перечисляемыми, поэтому мы можем перечислить их с помощью циклов for...in и получить их в виде массива с помощью Object.keys.

Обратите внимание, что разница между этими двумя способами получения перечислимых свойств заключается в том, что Object.keys возвращает только массив с собственными свойствами объекта, а цикл for...in возвращает также ключи, найденные в цепочке прототипов.

const car = { 
  numberplate: '1234',
  brand: 'Koenigsegg',
};
Object.defineProperty(
  car, 
  'numberplate', 
  { enumerable: false }
);
Object.keys(car); // -> [brand]

Установка для enumerable значения false также повлияет на JSON сериализацию свойства, поскольку оно не будет сериализовано. Это может быть полезно в некоторых случаях.

Настраиваемый

Дескриптор свойства configurable предотвращает изменение дескрипторов (данного свойства, например: platenumber). Кроме того, это предотвращает удаление свойства из объекта. Давайте посмотрим пример:

'use strict'
const car = { 
  numberplate: '1234',
};
Object.defineProperty(
  car, 
  'numberplate', 
  { configurable: false }
);
delete car.numberplate; // -> Uncaught TypeError
Object.defineProperty(
  car, 
  'numberplate', 
  { enumerable: false }
); // -> Uncaught TypeError
Object.defineProperty(
  car, 
  'numberplate', 
  { configurable: true }
); // -> Uncaught TypeError

После того как вы установите настраиваемый дескриптор свойства на false, вы не сможете позже переключить его обратно на true.
Одно предостережение, о котором следует помнить, заключается в том, что даже если вы установите configurable на false, вы все равно можете изменить дескриптор writable.

Ценить

Наконец, дескриптор значения предназначен для установки или изменения значения свойства.

'use strict'
const car = { 
  numberplate: '1234',
};
Object.defineProperty(
  car, 
  'numberplate', 
  { value: '0000' }
);
car.numberplate; // -> '0000'

Сеттер и геттер

Добытчики

Еще одна полезная вещь, которую вы могли бы сделать с Object.create (или Object.defineProperty, или Object.defineProperties), — это реализация сеттеров и геттеров. Давайте посмотрим, как мы можем это сделать.

const point = { x: 0, y: 0 };
Object.defineProperty(
  point, 
  'position', 
  {
    get: function() {
      return [this.x, this.y];
    }
  }
);
point.position; // -> [0, 0]

Чтобы создать геттер, вы устанавливаете атрибут get в функцию, эта функция и есть наш геттер.

Сеттеры

const point = { x: 0, y: 0 };

Object.defineProperty(
  point, 
  'position', 
  {
    set: function(pointArray) {
      [this.x, this.y] = pointArray;
    }
  }
);

point.position = [4, 2];

point.x; // -> 4
point.y; // -> 2

Как и для геттера, для реализации сеттера мы устанавливаем атрибут set для функции, которая принимает аргумент, аргумент — это значение, которое вы хотите установить.

Обратите внимание, что когда вы устанавливаете геттер или сеттер для свойства, у него не может быть дескриптора свойства с возможностью записи или значения. См. ниже:

Object.getOwnPropertyDescriptor(
  point,
  'position'
); // -> { enumerable: false,
   //      configurable: false,
   //      get: ƒ, set: ƒ }

Обратите внимание, что при установке геттера или сеттера дескрипторам enumerable и configurable автоматически присваивается значение false. Таким образом, вы можете установить геттеры и сеттеры в одном и том же выражении или вручную установить configurable в true при их реализации.

шляпа это для этого поста. Надеюсь, вам понравилось. Если да, поделитесь, пожалуйста, с друзьями и коллегами. Кроме того, вы можете следить за мной в Твиттере по адресу @theAngularGuy, так как это очень поможет мне.

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

Хорошего дня!