ECMAScript 5 идёт по своему пути. Воскреснув из пепла ECMAScript 4, который был ужат обратно к ECMAScript 3.1, который далее был вновь назван ECMAScript 5 (подробнее) — он приходит с новым слоем функциональности, построенным на базе нашего любимого ECMAScript 3.
Анонсировано несколько новых API, включённых в спецификацию, но самая интересная функциональность заключена в коде обьектов и свойств. Этот новый код дает возможность существенно влиять на то, как пользователи смогут взаимодействовать с объектами, позволяя обеспечить геттеры и сеттеры, предотвратить перечисление, манипуляции, или удаление, и даже предотвратить добавление новых свойств. Короче говоря: Вы будете иметь возможность повторить и расширить существующий набор API для JavaScript (например, DOM), с помощью JavaScript (не используя более ничего).
Очень хорошая новость: Эти функции должны появиться во всех основных браузерах. Все основные поставщики браузеров работали над этой спецификацией, и договорились осуществлять ее в своих продуктах. Точные сроки пока не ясны, но велика вероятность скорой реализации.
Судя по всему, не существует полной реализации ES5 на сегодня, но несколько проектов уже в процессе реализации. В то же время вы можете прочитать спецификацию ECMAScript 5 (PDF — в этой статье я обсуждаю страницы 107-109), либо наблюдать за последними обсуждениями команды ECMAScript на Google.
Новая возможность ECMAScript 5 — расширяемость объектов теперь можно переключать. Отключение расширяемости может предотвратить добавление новых свойств к объекту.
ES5 предусматривает два способа манипулирования и проверки расширяемости объектов:
Пример использования:
Свойства были полностью пересмотрены. Они теперь не являются простыми значениями, связанными с объектом — теперь у вас есть полный контроль над тем, как они могут действовать. Большая сила связана, тем не менее, с повышенной сложностью.
Свойства обьектов разбиты на две части.
Содержание свойства может быть определено двумя вариантами: значение (свойство-данные — это традиционные свойства, которые мы знаем и любим в ECMAScript 3) или геттер и сеттер (свойства-«аксессоры» — мы знаем их по некоторым современным браузерам, таким, как WebKit и Gecko.
К тому же свойства могут быть…
В общей сложности эти различные атрибуты составляют дескриптор свойства. Например, простой дескриптор может выглядеть примерно следующим:
Три атрибута (writable, enumerable и configurable) являются факультативными и все по умолчанию истинны. Таким образом, для свойства вам обязательно нужно предоставить только либо значение, либо геттер и сеттер.
Вы можете использовать новый метод
Этот метод позволяет получить доступ к дескриптору свойства. Он является единственным способом получить эту информацию (иначе говоря, дескрипторы не в распоряжении пользователя — они не существуют как видимые атрибуты свойства, они хранятся внутри в движке ECMAScript).
Пример использования:
Этот метод позволяет определить новое свойство для объекта (или изменить дескриптор существующего свойства). Этот метод принимает дескриптор свойства и использует его для инициализации (или обновления) свойства.
Пример использования:
Object.defineProperty — базовый метод новой версии ECMAScript. Фактически все остальные важные возможности зависят от существования этого метода.
Определяет несколько свойств для обьекта за один раз (вместо определения каждого индивидуально).
Примерная реализация:
Пример использования:
Дескрипторы свойств (и связанные с ними методы) является, пожалуй, наиболее важной новой особенностью ECMAScript 5. Это дает разработчикам возможности очень чётко контролировать свои объекты, предотвратить нежелательные модификации, а также поддерживать единый веб-совместимый API.
Некоторые интересные новые возможности были введены в язык, используя вышеописанные расширения.
Следующие два метода являются очень полезными для сбора массивов всех свойств объекта.
Возвращает массив строк, представляющих имена всех перечислимых свойств объекта. Его поведение совпадает с поведением метода из библиотеки Prototype.js.
Примерная реализация:
Пример использования:
Почти идентичен Object.keys, но возвращает имена всех свойств объекта (а не только перечислимых).
Пример реализации не представляется возможным в ECMAScript 3, так как неперечислимые свойства не могут быть перечислены. Возвращаемое значение и использование идентично Object.keys.
Создает новый объект, прототипом которого является proto, и чьи свойства устанавливаются с помощью Object.defineProperties (props).
Простая реализация будет выглядеть так (требует нового метода Object.defineProperties):
Пример использования:
Запечатывание объекта мешает коду удалять или изменять дескрипторы любых свойств обьекта- и запрещает добавлять новые свойства.
Пример реализации:
Вы предпочтёте запечатывание объекта, если вы хотите, чтобы набор его существующих свойств оставался неповрежденным, без предоставления новых дополнений, но в то же время позволяя пользователю редактировать их значения.
Замораживание объекта практически идентично запечатыванию, но с тем дополнением, что свойства становятся неизменяемыми.
Пример реализации:
Замораживание объекта является ультимативной формой блокировки. После того, как объект был заморожен, он не может быть разморожен — не может быть изменен в любой форме. Это лучший способ убедиться в том, что ваши объекты будут оставаться в точности такими, какими вы оставили их — на неопределенный срок.
Все вместе эти изменения являются очень интересными, они дают вам беспрецедентный уровень контроля над объектами, которые вы создаёте. Замечательный аспект заключается в том, что вы сможете использовать эти возможности для создания более крупных и более сложных функций в чистом ECMAScript (таких, как строительство новых модулей DOM, или перемещение большей части API браузера в чистый JavaScript). А поскольку все браузеры имеют на борту JavaScript, это абсолютно то, что мы с нетерпением ожидаем.
— примечание переводчика: descriptor переведено как дескриптор, хотя можно переводить и как описатель. getter и setter не имеют столь же коротких и ёмких определений на русском языке (получатель значения и установщик значения), поэтому использованы жаргонизмы геттер и сеттер.
Анонсировано несколько новых 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 не имеют столь же коротких и ёмких определений на русском языке (получатель значения и установщик значения), поэтому использованы жаргонизмы геттер и сеттер.