Передать на печать

ECMA JavaScript 6: Объекты

Как известно, Microsoft Internet Explorer 12, что будет присутствовать в составе грядущей Windows 10, получит поддержку новой версии JavaScript, имеющей номер 6. И нам, Web-разработчикам, следует приготовиться к этому, изучить возможности, предлагаемые JavaScript 6, и понять, чем они нам будут полезны.

В настоящее время процесс формирования стандарта JavaScript 6 ещё не завершён. Организация European Computer Manufacturers Association (ECMA), занимающаяся, в том числе, выработкой этого стандарта (носящего полное наименование ECMA JavaScript 6), опубликовала лишь черновые спецификации, которые и были взяты в работу разработчиками Web-обозревателей. Понятно, что в дальнейшем возможно внесение в них изменений, но основной костяк возможностей этого языка вряд ли существенно изменится.
И мы можем начать рассматривать его уже сейчас.

Внимание!
Текущей версией JavaScript является 5.1. Именно эту версию поддерживают все современные Web-обозреватели. Именно с ней мы будем в дальнейшем сравнивать шестую версию этого языка.



1. Объекты
И начнём мы с рассмотрения средств, с помощью которых объявляются новые объекты.

1.1. Объявление объектов в JavaScript 5.1
В чём сила языка JavaScript? Правильно - в объектах. Объектами являются все типы данных, даже элементарные (строки, числа, логические величины и пр.), не говоря уже о массивах и функциях. У любой строки, у любого числа мы можем вызвать методы, чтобы, скажем, преобразовать их в другой тип.
В чём слабость языка JavaScript? Увы - всё в тех же объектах... Мы сразу же столкнёмся с ней, когда попытаемся объявить свой собственный объект. Используемый для этого синтаксис покажется программисту, владеющему другими объектно-ориентированными языками, тем же PHP, мягко говоря, странным. Судите сами:

function Control(name) {
  this.name = name;
  this.disabled = false;
}


Эта функция на самом деле представляет собой объявление объекта Control, который представит некий элемент управления. (Фактически эта функция станет конструктором объекта.) Данный объект получит два свойства: name и disabled.
Теперь изменим представленный выше код, добавив объекту Control метод showName, выводящий на экран имя элемента управления:

function showName() {
  write(this.name);
}
function Control(name) {
  this.name = name;
  this.disabled = false;
  this.showName = showName;
}


Довольно неклассический синтаксис, не правда ли? И это только для простого объявления объекта! Если же нам потребуется создать объект, являющийся потомком объявленного ранее, нам придётся долго шаманить с прототипом (свойствами prototype и __proto__), и написанный нами код станет ещё более невразумительным.
Неудивительно, что все популярные JavaScript-библиотеки, наподобие jQuery и prototype, равно как и исполняющая среда Windows RT for JavaScript, включают в свой состав более удобные средства для объявления новых объектов, в том числе и объектом-потомков.
Но скоро необходимость в них полностью отпадёт.

1.2. Объявление объектов в JavaScript 6
Потому что грядущая шестая версия JavaScript поддерживает полностью новый и несравнимо более удобный синтаксис объявления объектов.

1.2.1. Объявление объекта. Конструкторы
Проще всего объявить объект, не являющийся ничьим потомком. Для этого применяется синтаксис, знакомый нам по другим языкам программирования:

class <имя объекта> {
  <объявления методов объекта>
}

ов объекта>
}[/code]
Как видим, внутри объявления объекта могут находиться лишь объявления методов; объявления свойств не допускаются. Однако свойства можно объявить внутри объявлений методов, обычно - конструктора объекта (о нём - чуть позже).
Методы объекта объявляются так же, как и обычные функции, за единственным исключением - ключевое слово function не ставится:
[code]<имя метода>([<список формальных параметров>]) {
  <тело метода>
}[/code]
Конструктор объекта, то есть метод, который вызывается при создании каждого нового экземпляра этого объекта, должен иметь имя constructor.
Давайте перепишем представленный ранее код объявления объекта Control в стиле JavaScript 6:
[code]class Control {
  constructor(name) {
    this.name = name;
    this.disabled = false;
  }
  showName() {
    write(this.name);
  }
}[/code]
Такая запись заметно короче и существенно понятнее, не так ли?
Внимание!
Если при объявлении объекта мы не указали его потомка, объявляемый объект станет потомком объекта Function (функции).

1.2.2. Наследование объектов. Переопределение методов
Если нам нужен объект, являющийся потомком какого-то другого объекта, не Function, мы укажем это в его объявлении:
[code]class <имя объекта> extends <имя объекта-родителя> {
  <объявления методов объекта>
}[/code]
Объявленный таким образом объект унаследует все свойства и методы родителя.
Мы можем без проблем переопределить метод, унаследованный от родителя, в потомке. При этом, если нам понадобится в теле метода потомка вызвать метод, объявленный в родителе, мы используем следующий синтаксис:
[code]super.<имя метода родителя>([<список фактических параметров>])[/code]
Чтобы вызвать конструктор родителя, применяется похожий синтаксис:
[code]super([<список фактических параметров>])[/code]
Для примера давайте объявим объект TextField, являющийся потомком Control:
[code]class TextField extends Control {
  constructor(name, value) {
    super(name);
    this.value = value;
    this.readOnly = false;
  }
  showName() {
    super.showName();
    write(this.value);
  }
}[/code]
В новом объекте мы объявили свойства value и readOnly и переопределили метод showName таким образом, чтобы он дополнительно выводил на экран содержимое свойства value.
И обратим внимание, как мы записали конструктор нового объекта. Сначала мы вызвали конструктор объекта-родителя, чтобы он инициализировал функциональность, унаследованную от родителя. Если мы не сделаем этого, наш объект может работать не так, как мы планировали.

1.2.3. Наследование от встроенных объектов
JavaScript 6 позволяет нам объявить объекты, являющиеся потомками встроенных объектов как самого языка, так и Web-обозревателя. Так, мы можем объявить объект, являющийся потомком объекта Date:
[code]class ExtendedDate extends Date {
  . . .
}[/code]
Также мы можем объявить объект - потомок объекта Element, представляющий элемент Web-страницы:
[code]class ExtendedElement extends Element {
  . . .
}[/code]

1.2.4. Объявление настоящих свойств
Свойство в JavaScript 5 - просто переменная, являющаяся частью объекта и хранящая некоторую величину, которая является составной частью экземпляра объекта. Однако в более развитых объектно-ориентированных языках, например, в C#, свойство - это код, выполняющийся при получении значения свойства и при занесении в него нового значения. (Знакомые нам свойства JavaScript 5 там носят название полей.)
JavaScript 6 получил поддержку настоящих свойств. Объявляются они очень просто.
Фрагмент кода, выполняющийся при получении значения свойства (его часто называют геттером), записывается следующим образом:
[code]get <имя свойства>() {
  <код, вычисляющий и возвращающий значение свойства>
}[/code]
Как видим, геттер фактически представляет собой метод.
Фрагмент кода, выполняющийся при занесении в свойство нового значения (сеттер), также является методом и записывается в формате:
[code]set <имя свойства>(<формальный параметр, хранящий новое значение свойства>) {
  <код, сохраняющий новое значение и, возможно, выполняющий дополнительные действия>
}[/code]
Обычно значение свойства сохраняется в особом поле. Чтобы отличить его от прочих полей, к его имени добавляются либо два символа подчёркивания спереди, либо один символ подчёркивания сзади.
В качестве примера перепишем объявление класса Control, создав в нём настоящие свойства name и value:
[code]class Control {
  constructor(name) {
    this.__name = name;
    this.__disabled = false;
  }
  get name() { return this.__name; }
  set name(new_value) {
    if (this.__name != new_value) {
      this.__name = new_value;
      write("Новое значение свойства name: " + this.__name;)
    }
  }
  get disabled() { return this.__disabled; }
  set disabled(new_value) {
    if (this.__disabled != new_value) {
      this.__disabled = new_value;
      write("Новое значение свойства disabled: " + this.__disabled;)
    }
  }
  showName() {
    write(this.name);
  }
}[/code]
Для хранения значений этих свойств мы используем поля __name и __disabled соответственно. Геттеры свойств будут просто возвращать значения этих полей, а сеттеры - проверять, действительно ли новое значение свойства отличается от предыдущего, и, если это так, заносить новое значение в поле и выводить его на экран.
Если свойство было объявлено с применением и геттера, и сеттера, оно станет доступным и для чтения, и для записи. Так, в приведённом выше коде мы можем как читать содержимое свойств name и disabled, так и заносить в них новые значения.
Если свойство объявлено лишь с применением геттера, оно будет доступно лишь для чтения. Если же свойство включает один сеттер, оно будет доступно только для записи.
Объявим в объекте TextField свойство outputName, доступное только для чтения:
[code]class TextField extends Control {
  get outputName() { return "Поле ввода " + this.name; }
}[/code]

1.2.5. Статические методы
Методы, что мы объявляли ранее, вызываются у экземпляра объекта и называются методами экземпляра.
[code]var obj = new Control("ctl");
obj.showName();[/code]
Здесь мы создаём экземпляр объекта Control и вызываем метод экземпляра showName.
Статические методы, или методы объекта, вызываются у самого объекта. Обычно с их помощью реализуется функциональность, общая для всех экземпляров данного объекта.
Объявление статического метода предваряется ключевым словом static. Внутри тела статического метода можно обращаться только к другим статическим методам.
[code]class Control {
  . . .
  static getType() { return "Control"; }
}
. . .
write(Control.getType());
[/code]

2. Инициализаторы
Другой набор нововведений касается инициализаторов, с помощью которых объявляются экземпляры объекта Object:
[code]var control = {
  name: "ctl",
  disabled: true,
  showName: function() {
    write(this.name);
  }
};[/code]
Давайте рассмотрим все эти нововведения.

  • Упрощённый синтаксис объявления методов - в стиле, используемом при объявлении объектов. Достаточно указать лишь имя метода без ключевого слова function:
    [code]var control = {
      name: "ctl",
      disabled: true,
      showName() {
        write(this.name);
      }
    };[/code]
  • Вычисляемые имена свойств. Допускается использование в качестве имён свойств строковых значений сформированных программно. Имя переменной, хранящей это значение, берётся в квадратные скобки:
    [code]
    var pName = "name", pDisabled = "disabled";
    var control = {
      [pName]: "ctl",
      [pDisabled]: true,
      . . .
    };[/code]
    Объявленный нами экземпляр control будет содержать свойства name и disabled.
  • Возможность наследования одного экземпляра объекта от другого. Экземпляр объекта, который станет родителем, просто присваивается свойству __proto__ экземпляра объекта - потомка:
    [code]var engine = {
      showName() {
        write(this.name);
      }
    };
    var control = {
      __proto__: engine,
      name: "ctl",
      disabled: true,
    };[/code]
    В результате экземпляр объекта control будет поддерживать метод showName, унаследованный от экземпляра объекта engine:
    [code]control.showName();[/code]
  • Возможность вызова метода родителя из метода потомка. Для этого используется тот же синтаксис, что применяется при объявлении объектов, - с помощью ключевого слова super:
    [code]var engine = {
      showName() {
        write(this.name);
      }
    };
    var control = {
      __proto__: engine,
      name: "ctl",
      disabled: true,
      showName() {
        write("Control: ");
        super.showName();
      }
    };[/code]
    Здесь мы переопределяем в потомке control метод showName, унаследованный от родителя engine, и в его теле вызываем метод родителя.



3. Создание и использование примесей
Иногда бывает необходимо дать целой группе объектов, не являющихся потомками одного родителя, некую общую функциональность. Это можно сделать, создав особый объект, реализующий эту функциональность, - примесь. Эта примесь потом добавляется ко всем объектам, которые должны поддерживать эту функциональность.
В JavaScript 6 добавление примеси к объекту можно выполнить вызовом статического метода assign, поддерживаемого объектом Object и, следовательно, доступного для любых объектов. Формат вызова метода assign таков:
[code]Object.assign(<объект, к которому добавляется функциональность примесей>, <примесь 1>, <примесь 2>...)[/code]
Как видим, мы можем добавить в одному объекту сколько угодно примесей. Результата этого метод не возвращает.
Для примера давайте объявим экземпляр объекта engine, который станет примесью:
[code]var engine = {
  showName() {
    write(this.name);
  }
};[/code]
Теперь объявим экземпляр объекта control, к которому потом добавим функциональность примеси engine:
[code]var control = {
  name: "ctl",
  disabled: true,
};[/code]
И, собственно, добавим к нему примесь:
[code]Object.assign(control, engine);[/code]
Теперь мы можем использовать в экземпляре объекта control функциональность, реализованную в примеси engine, например, вызвать метод showName:
[code]control.showName();[/code]

4. Заключение
Здесь мы рассмотрели часть новых возможностей языка ECMA JavaScript 6, касающихся объектов и их экземпляров. Эти возможности уже определены в черновой спецификации этого языка и даже поддерживаются некоторыми Web-обозревателями, и их реализация вряд ли сильно изменится в дальнейшем.
В целом, можно сказать, что эти возможности принесут пользу. На JavaScript сейчас создаются исключительно сложные программные комплексы, и нововведения в плане объявления объектов позволят существенно упростить их код.
Существуют JavaScript-библиотеки, позволяющие использовать возможности JavaScript 6 уже сейчас. Они преобразуют код, написанный на JavaScript 6, в формат языка JavaScript 5, поддерживаемый всеми современными Web-обозревателями, непосредственно при загрузке страницы. К таким библиотекам относится, в частности, Google Traceur.

Использованные материалы



dronov_va, MSInsider.ru Team
Апрель 2015

  Передать на печать





Все права принадлежат © MSInsider.ru и TheVista.ru, 2013
Сайт является источником уникальной информации о семействе операционных систем Windows и других продуктах Microsoft. Перепечатка материалов возможна только с разрешения редакции.
Работает на WMS 1.1 (Страница создана за 0.041 секунд)