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 есть еще более вкусные фишки, но об этом как-нибудь в другой раз...















Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации