Pull to refresh

ECMAscript 5: Обьекты и Свойства

Reading time8 min
Views12K
Original author: John Resig
ECMAScript 5 идёт по своему пути. Воскреснув из пепла ECMAScript 4, который был ужат обратно к ECMAScript 3.1, который далее был вновь назван ECMAScript 5 (подробнее) — он приходит с новым слоем функциональности, построенным на базе нашего любимого ECMAScript 3.

Анонсировано несколько новых API, включённых в спецификацию, но самая интересная функциональность заключена в коде обьектов и свойств. Этот новый код дает возможность существенно влиять на то, как пользователи смогут взаимодействовать с объектами, позволяя обеспечить геттеры и сеттеры, предотвратить перечисление, манипуляции, или удаление, и даже предотвратить добавление новых свойств. Короче говоря: Вы будете иметь возможность повторить и расширить существующий набор API для JavaScript (например, DOM), с помощью JavaScript (не используя более ничего).

Очень хорошая новость: Эти функции должны появиться во всех основных браузерах. Все основные поставщики браузеров работали над этой спецификацией, и договорились осуществлять ее в своих продуктах. Точные сроки пока не ясны, но велика вероятность скорой реализации.

Судя по всему, не существует полной реализации ES5 на сегодня, но несколько проектов уже в процессе реализации. В то же время вы можете прочитать спецификацию ECMAScript 5 (PDF — в этой статье я обсуждаю страницы 107-109), либо наблюдать за последними обсуждениями команды ECMAScript на Google.
Примечание: я показываю несколько простых, примерных, реализаций этих методов для иллюстрации того, каким образом они могли бы функционировать. Почти все из них требуют других, новых методов для корректной работы — и они не реализованы в соответствии с спецификацией на 100% (например, нет проверок на ошибки).

Обьекты


Новая возможность ECMAScript 5 — расширяемость объектов теперь можно переключать. Отключение расширяемости может предотвратить добавление новых свойств к объекту.

ES5 предусматривает два способа манипулирования и проверки расширяемости объектов:

Object.preventExtensions( obj ), Object.isExtensible( obj )

preventExtensions блокирует обьект и предотвращает создание любых новых свойств обьекта в будущем. isExtensible — возможность определить, расширяем в настоящее время обьект или нет.

Пример использования:
var obj = {};
obj.name = "John";
print( obj.name );
// John

print( Object.isExtensible( obj ) );
// true

Object.preventExtensions( obj );

obj.url = "http://ejohn.org/"; // Exception in strict mode

print( Object.isExtensible( obj ) );
// false


* This source code was highlighted with Source Code Highlighter.

Свойства и дескрипторы


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

Свойства обьектов разбиты на две части.

Содержание свойства может быть определено двумя вариантами: значение (свойство-данные — это традиционные свойства, которые мы знаем и любим в ECMAScript 3) или геттер и сеттер (свойства-«аксессоры» — мы знаем их по некоторым современным браузерам, таким, как WebKit и Gecko.
  • Value. Хранит значение свойства.
  • Get. Эта функция вызывается каждый раз, когда происходит доступ к значению свойства.
  • Set. Эта функция вызывается каждый раз, когда значение свойства изменяется.

К тому же свойства могут быть…
  • Изменяемыми. Если ложь, то значение этого свойства не может быть изменено.
  • Конфигурируемыми. Если ложь, любые попытки удалить свойство или изменить его атрибуты (записываемость, конфигурируемость или перечислимость) не получатся.
  • Перечислимыми. Если истина, то свойство будет итерировано, когда пользователь делает for (var prop in obj) (или что-то в этом духе).

В общей сложности эти различные атрибуты составляют дескриптор свойства. Например, простой дескриптор может выглядеть примерно следующим:
{
 value: "test",
 writable: true,
 enumerable: true,
 configurable: true
}


* This source code was highlighted with Source Code Highlighter.

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

Вы можете использовать новый метод Object.getOwnPropertyDescriptor для получения этой информации для уже существующего свойства обьекта.

Object.getOwnPropertyDescriptor( obj, prop )

Этот метод позволяет получить доступ к дескриптору свойства. Он является единственным способом получить эту информацию (иначе говоря, дескрипторы не в распоряжении пользователя — они не существуют как видимые атрибуты свойства, они хранятся внутри в движке ECMAScript).

Пример использования:
var obj = { foo: "test" };
print(JSON.stringify(
 Object.getOwnPropertyDescriptor( obj, "foo" )
));
// {"value": "test", "writable": true,
// "enumerable": true, "configurable":true}

* This source code was highlighted with Source Code Highlighter.


Метод Object.defineProperty( obj, prop, desc )

Этот метод позволяет определить новое свойство для объекта (или изменить дескриптор существующего свойства). Этот метод принимает дескриптор свойства и использует его для инициализации (или обновления) свойства.

Пример использования:
var obj = {};
Object.defineProperty( obj, "value", {
 value: true,
 writable: false,
 enumerable: true,
 configurable: true
});

(function(){
 var name = "John";
 
 Object.defineProperty( obj, "name", {
  get: function(){ return name; },
  set: function(value){ name = value; }
 });
})();

print( obj.value )
// true

print( obj.name );
// John

obj.name = "Ted";
print( obj.name );
// Ted

for ( var prop in obj ) {
 print( prop );
}
// value
// name

obj.value = false; // Exception if in strict mode

Object.defineProperty( obj, "value", {
 writable: true,
 configurable: false
});

obj.value = false;
print( obj.value );
// false

delete obj.value; // Exception


* This source code was highlighted with Source Code Highlighter.


Object.defineProperty — базовый метод новой версии ECMAScript. Фактически все остальные важные возможности зависят от существования этого метода.

Object.defineProperties( obj, props )

Определяет несколько свойств для обьекта за один раз (вместо определения каждого индивидуально).

Примерная реализация:
Object.defineProperties = function( obj, props ) {
 for ( var prop in props ) {
  Object.defineProperty( obj, prop, props[prop] );
 }
};


* This source code was highlighted with Source Code Highlighter.


Пример использования:
var obj = {};
Object.defineProperties(obj, {
 "value": {
  value: true,
  writable: false
 },
 "name": {
  value: "John",
  writable: false
 }
});


* This source code was highlighted with Source Code Highlighter.

Дескрипторы свойств (и связанные с ними методы) является, пожалуй, наиболее важной новой особенностью ECMAScript 5. Это дает разработчикам возможности очень чётко контролировать свои объекты, предотвратить нежелательные модификации, а также поддерживать единый веб-совместимый API.

Новые возможности



Некоторые интересные новые возможности были введены в язык, используя вышеописанные расширения.

Следующие два метода являются очень полезными для сбора массивов всех свойств объекта.

Object.keys( obj )

Возвращает массив строк, представляющих имена всех перечислимых свойств объекта. Его поведение совпадает с поведением метода из библиотеки Prototype.js.

Примерная реализация:
Object.keys = function( obj ) {
 var array = new Array();
 for ( var prop in obj ) {
  array.push( prop );
 }
 return array;
};


* This source code was highlighted with Source Code Highlighter.


Пример использования:
var obj = { name: "John", url: "http://ejohn.org/" };
print( Object.keys(obj).join(", ") );
// name, url


* This source code was highlighted with Source Code Highlighter.


Object.getOwnPropertyNames( obj )

Почти идентичен Object.keys, но возвращает имена всех свойств объекта (а не только перечислимых).

Пример реализации не представляется возможным в ECMAScript 3, так как неперечислимые свойства не могут быть перечислены. Возвращаемое значение и использование идентично Object.keys.

Object.create (proto, props)

Создает новый объект, прототипом которого является proto, и чьи свойства устанавливаются с помощью Object.defineProperties (props).

Простая реализация будет выглядеть так (требует нового метода Object.defineProperties):
Object.create = function( proto, props ) {
 var obj = new Object();
 obj.__proto__ = proto;
 if ( typeof props !== "undefined" ) {
  Object.defineProperties( obj, props );
 }
 
 return obj;
};


* This source code was highlighted with Source Code Highlighter.


Примечание: Приведенный выше код использует специфичное для Mozilla свойство _proto_. Это свойство дает вам доступ к внутреннему прототипу объекта, — и позволяет также установить его значение. В ES5 метод Object.getPrototypeOf позволяет получить доступ к этому свойству, но не позволяет его устанавливать — следовательно, метод выше невозможно реализовать в общей, совместимой с ES, манере.

Я обсудил Object.getPrototypeOf ранее, поэтому я не буду обсуждать здесь его еще раз.

Пример использования:
function User(){}
User.prototype.name = "Anonymous";
User.prototype.url = "http://google.com/";
var john = Object.create(new User(), {
 name: { value: "John", writable: false },
 url: { value: "http://google.com/" }
});

print( john.name );
// John

john.name = "Ted"; // Exception if in strict mode


* This source code was highlighted with Source Code Highlighter.


Object.seal( obj ), Object.isSealed( obj )

Запечатывание объекта мешает коду удалять или изменять дескрипторы любых свойств обьекта- и запрещает добавлять новые свойства.

Пример реализации:
Object.seal = function( obj ) {
 var props = Object.getOwnPropertyNames( obj );
 
 for ( var i = 0; i < props.length; i++ ) {
  var desc = Object.getOwnPropertyDescriptor( obj, props[i] );
  
  desc.configurable = false;
  Object.defineProperty( obj, props[i], desc );
 }
 
 return Object.preventExtensions( obj );
};


* This source code was highlighted with Source Code Highlighter.


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

Object.freeze( obj ), Object.isFrozen( obj )

Замораживание объекта практически идентично запечатыванию, но с тем дополнением, что свойства становятся неизменяемыми.

Пример реализации:
Object.freeze = function( obj ) {
 var props = Object.getOwnPropertyNames( obj );
 
 for ( var i = 0; i < props.length; i++ ) {
  var desc = Object.getOwnPropertyDescriptor( obj, props[i] );
  
  if ( "value" in desc ) {
   desc.writable = false;
  }
  
  desc.configurable = false;
  Object.defineProperty( obj, props[i], desc );
 }
 
 return Object.preventExtensions( obj );
};


* This source code was highlighted with Source Code Highlighter.


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

Все вместе эти изменения являются очень интересными, они дают вам беспрецедентный уровень контроля над объектами, которые вы создаёте. Замечательный аспект заключается в том, что вы сможете использовать эти возможности для создания более крупных и более сложных функций в чистом ECMAScript (таких, как строительство новых модулей DOM, или перемещение большей части API браузера в чистый JavaScript). А поскольку все браузеры имеют на борту JavaScript, это абсолютно то, что мы с нетерпением ожидаем.

— примечание переводчика: descriptor переведено как дескриптор, хотя можно переводить и как описатель. getter и setter не имеют столь же коротких и ёмких определений на русском языке (получатель значения и установщик значения), поэтому использованы жаргонизмы геттер и сеттер.
Tags:
Hubs:
Total votes 47: ↑47 and ↓0+47
Comments12

Articles