Сеттери, геттери, дескриптори в об'єктах

Об'єкти можуть містити в собі властивості, методи, сеттери, геттери:

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.