Сеттери, геттери, дескриптори в об'єктах
Об'єкти можуть містити в собі властивості, методи, сеттери, геттери:
let obj = {
name: 'Ivan',
hello: function () { return 'Hello ' + this.name; },
get letters() { return this.name.length; }
}
obj.name = 'Larisa';
alert(obj.hello());
alert('Letters: ' + obj.letters);
Властивості подібні змінним - їх можна переприсвоювати.
Методи, як правило, повертають певне значення або обробляють дані об'єкта.
Сеттери
Сеттери обробляють вхідні дані і вносять зміни в об'єкт.
Сеттер дозволяє контролювати вхідну інформацію і блокувати невалідні дані.
Наприклад, є поле textarea, в якому не задано атрибут maxlength і воно ніяк не валідується скриптами. В це поле вводиться опис деякого об'єкта:
<textarea name="descr"></textarea>
let obj = {
...,
description: ''
}
obj.description = document.getElementsByName('descr')[0].value;
Нехай за умовами ТЗ розміри поля description не повинні перевищувати 200 символів. У цьому випадку об'єкт ніяк не захищений від внесення даних більшого розміру.
Геттери
Геттери обробляють дані об'єкта і повертають певне значення.
Геттер на відміну від властивості неможна змінити, переприсвоїти. На відміну від метода звернення до геттера відбувається без круглих дужок.
Приклад геттера - властивість length у масива. Її неможна змінити напряму, вона щоразу обчислюється при зверненні до неї.
Приклад геттера:
let myObj = {
a: 5,
b: 7,
get sum() { return this.a + this.b; }
};
alert(myObj.sum); // 12
Додавання геттера до існуючого об'єкта:
let myObj = {
a: 5,
b: 7
};
Object.defineProperty(myObj, 'sum', {
get: function() { return this.a + this.b; }
});
Приблизна реалізація властивості length:
Array.prototype = {
...
get length(){
let max = -1;
for (let key in this)
if (typeof key === 'number' && key > max) max = key;
return max + 1;
}
}
Пам'ятаємо, що властивість length у масива на один більше за максимальний індекс.
Очевидно, що звертатися до властивості length у кожній ітерації при роботі з масивом незмінної довжини не зовсім коректно, весь час буде виконуватися один і той самий код:
let arr = масив із мільйона елементів;
for (let i = 0; i < arr.length; i++) { ... }
В цьому випадку геттер length виконається 1 000 001 раз, в самому геттері відбувається більше мільйона звернень до ключів, в результаті маємо більше, ніж 1 000 000 000 000 операцій.
На невеликих масивах (в межах 100-1000 значень) це не критично, але, все ж таки, не найоптимальніший варіант.
Скоріш за все, інтерпретатор проведе певні оптимізації, але вірним варіантом буде власноруч оптимізувати код, ніж покладатися на інтерпретатор:
let arr = масив із мільйона елементів;
let n = arr.length;
for (let i = 0; i < n; i++) { ... }
У випадку, коли масив динамічно і контрольовано змінюється - краще також використати зовнішню змінну:
let arr = масив із мільйона елементів;
let n = arr.length;
for (let i = 0; i < n; i++) {
if (arr[i] < 0) { arr.push(...); n++; }
if (arr[i] > 100) { bk.push(arr.pop()); n--; }
...
}
І лише у випадку, коли масив змінюється асинхронно, сторонніми засобами, доведеться щоразу використовувати реальний length.