1. Геттеры и сеттеры.
1.1. Геттеры и сеттеры в литерале:
var cat = { type_: 'cat', name_: '', get name() { return this.type_ + ': ' + this.name_; }, set name(name) { this.name_ = name; } }; cat.name = 'Tom'; console.log(cat.name); console.log(cat.name = 'Max'); // здесь Max! не cat: Max console.log(cat.name);
function Animal(type) { this.type_ = type; this.name_ = ''; } Animal.prototype = { get name () { return this.type_ + ': ' + this.name_; }, set name (name) { this.name_ = name; } };
1.3. Геттеры и сеттеры в дескрипторе (дескриптору посвящен следующий раздел поста):
function Animal(type) { this.type_ = type; this.name_ = ''; } Object.defineProperty(Animal.prototype, 'name', { get: function() { return this.type_ + ': ' + this.name_; }, set: function(name) { this.name_ = name; } });
Тестируем листинги 1.2 и 1.3:
var cat = new Animal('cat'); cat.name = 'Tom'; console.log(cat.name); console.log(cat.name = 'Max'); // здесь Max! не cat: Max console.log(cat.name);
Обращаю внимание на комментарий в коде - пытаться возвращать свойство непосредственно из сеттера бесполезно, например следующим образом:
set name (name) { this.name_ = name; return this.type_ + ': ' + this.name_; // не помогает }
2. Дескриптор.
Дескриптор - это объект, который содержит описание свойства объекта (именно его мы отправляли в качестве третьего аргумента метода Object.defineProperty чуть выше - листинг 1.3).
2.1. Свойства дескриптора по умолчанию:
var cat1 = {name: 'Tom'}; var cat2 = Object.defineProperty({}, 'name', { value: 'Max' }); var cat3 = { get name() { return this.name_; }, set name(name) { this.name_ = name; } }; console.log(Object.getOwnPropertyDescriptor(cat1, 'name')); console.log(Object.getOwnPropertyDescriptor(cat2, 'name')); console.log(Object.getOwnPropertyDescriptor(cat3, 'name'));
Найдите отличия.
2.2. Object.defineProperty, не объявляем свойства дескриптора (оставляем по умолчанию):
var cat = {name: 'Tom'}; console.log(cat.name); delete cat.name; // если не удалить, то свойство останется configurable и writable Object.defineProperty(cat, 'name', { value: 'Max' }); console.log(cat.name); cat.name = 'Sam'; // в строгом режиме ошибка будет уже здесь console.log(cat.name); // здесь Max delete cat.name; console.log(cat.name); // все равно Max Object.defineProperty(cat, 'name', { // здесь Cannot redefine property: name value: 'Sam', });
По умолчанию свойства configurable и writable равны false.
2.3. Object.defineProperty + configurable + writable:
var cat = {}; Object.defineProperty(cat, 'name', { value: 'Max', configurable: true, writable: true }); console.log(cat.name); cat.name = 'Sam'; console.log(cat.name); delete cat.name; console.log(cat.name); Object.defineProperty(cat, 'name', { value: 'Tom', configurable: true, writable: true }); console.log(cat.name); for (var key in cat) { // здесь никого нет console.log('%s: %s',key, cat[key]); }
По умолчанию свойство enumerable тоже равно false.
2.4. Object.defineProperty + enumerable:
var cat = {}; Object.defineProperty(cat, 'name', { value: 'Tom', enumerable: true }); for (var key in cat) { console.log('%s: %s', key, cat[key]); console.log('%s is enumerable:', key, cat.propertyIsEnumerable(key)); }
2.5. Пытаемся миксовать value или writable с get/set:
var cat = {}; // здесь TypeError: Invalid property. A property cannot both have accessors and be writable or have a value Object.defineProperty(cat, 'name', { value: 'Tom', writable: true, get: function () {} });
2.6. Object.defineProperties, объявляем несколько свойств сразу:
var cat = Object.defineProperties({}, { name: { value: 'Tom', writable: true, }, greet: { value: function() { console.log('Hi, my name is', this.name); } } }); cat.greet(); cat.name = 'Sam'; cat.greet();
2.7. Object.keys vs. Object.getOwnPropertyNames:
var cat = Object.defineProperties({}, { name: { value: 'Tom', enumerable: true, }, greet: { value: function() { console.log('Hi, my name is', this.name); } } }); console.log(Object.keys(cat)); console.log(Object.getOwnPropertyNames(cat));
Найдите отличия.
3. Наследование.
3.1. Object.create, создаем новый объект с указанным прототипом и свойствами:
var animal = { roars: 'r-r-r', roar: function () { console.log(this.roars); } }; var props = { meows: { value: 'meow' }, meow: { value: function () { console.log(this.meows); } } }; var cat = Object.create(animal, props); cat.roar(); cat.meow();
3.2. Cвойства, объявленные в объекте перекрывают свойства прототипа ("стандартное" наследование):
var animal = { greeting: 'r-r-r', roar: function () { console.log(this.greeting); } }; var props = { greeting: { value: 'meow' }, meow: { value: function () { console.log(this.greeting); } } }; var cat = Object.create(animal, props); cat.roar(); cat.meow();
3.3. Установка свойства непримитивного типа при создании объекта с помощью Object.create:
var animal = { roars: ['r-r-r'], roar: function () { console.log(this.roars.join('...')); } }; var props = { meows: { value: ['meow'] }, meow: { value: function () { console.log(this.meows.join(', ')); } } }; var cat1 = Object.create(animal, props); var cat2 = Object.create(animal, props); cat1.roars.push('meow'); // изменили свойство прототипа cat2.roar(); // cat2 рычит иначе cat1.meows.push('meow'); // изменили свойство cat1 cat2.meow(); // cat2 мяукает иначе!
Обращаю внимание на комментарии в коде - использование одного объекта props, содержащего дескрипторы (или чьи свойства указывают на дескрипторы - не силен в терминах, думаю понятно о чем речь), для создания нескольких объектов в этом случае является ошибкой. Угадайте почему.
3.4. Создавая объекты с помощью Object.create мы не используем конструктор, поэтому для идентификации объекта вместо instanceof следует применять isPrototypeOf:
var animal = { greeting: 'r-r-r', roar: function () { console.log(this.greeting); } }; var tiger = Object.create(animal); var cat = Object.create(tiger, {greeting: {value: 'meow'}}); tiger.roar(); cat.roar(); console.log('tiger is prototype of cat:', tiger.isPrototypeOf(cat)); console.log('animal is prototype of cat:', animal.isPrototypeOf(cat));
Обращаю внимание на то, что метод isPrototypeOf отрабатывает по всей цепочке прототипов.
4. Запрет изменения объекта.
4.1. Object.preventExtensions, запрет добавления свойств:
var cat = {name: 'Tom', greeting: 'r-r-r'}; console.log(cat); console.log('cat is extensible:', Object.isExtensible(cat)); Object.preventExtensions(cat); console.log('cat is extensible:', Object.isExtensible(cat)); delete cat.greeting; console.log(cat); cat.greeting = 'meow'; // в строгом режиме здесь будет ошибка console.log(cat); Object.defineProperty(cat, 'name', { // здесь без ошибок value: 'Max', configurable: true }); console.log(cat);
4.2. Object.seal, запрет добавления, удаления и конфигурирования свойств:
var cat = {name: 'Tom', greeting: 'r-r-r'}; console.log(cat); console.log('cat is sealed:', Object.isSealed(cat)); Object.seal(cat); console.log('cat is sealed:', Object.isSealed(cat)); delete cat.greeting; // в строгом режиме ошибка будет уже здесь console.log(cat); cat.greeting = 'meow'; console.log(cat); Object.defineProperty(cat, 'name', { // здесь Cannot redefine property: name value: 'Max', configurable: true }); console.log(cat);
4.3. Object.freeze, запрет добавления, удаления, конфигурирования и изменения значения свойств:
var cat = {name: 'Tom', greeting: 'r-r-r'}; console.log(cat); console.log('cat is frozen:', Object.isFrozen(cat)); Object.freeze(cat); console.log('cat is frozen:', Object.isFrozen(cat)); cat.name = 'Max'; // в строгом режиме здесь будет ошибка console.log(cat);
В ECMAScript 6 есть еще более вкусные фишки, но об этом как-нибудь в другой раз...
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации