Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык JavaScript часть1.pdf
Скачиваний:
204
Добавлен:
22.03.2016
Размер:
8.92 Mб
Скачать

ООП в прототипном стиле

В этом разделе мы изучим прототипы и классы на них — де-факто стандарт объектноориентированной разработки в JavaScript.

Прототип объекта

Объекты в JavaScript можно организовать в цепочки, так, чтобы если свойство не найдено в одном объекте — оно автоматически искалось бы его в другом.

Связующим звеном выступает специальное свойство __proto__.

Прототип __proto__

Если один объект имеет специальную ссылку __proto__на другой объект, то при чтении свойства из него, если свойство отсутствует в самом объекте, оно ищется в объекте __proto__.

Свойство __proto__доступно во всех браузерах, кроме IE10-, а в более старых IE оно, конечно

же, тоже есть, но напрямую к нему не обратиться, требуются чуть более сложные способы, которые мы рассмотрим позднее.

Пример кода (кроме IE10-):

var animal = { eats: true

};

var rabbit = { jumps: true

};

rabbit.__proto__ = animal;

// в rabbit можно найти оба свойства alert( rabbit.jumps ); // true alert( rabbit.eats ); // true

1.Первый alertздесь работает очевидным образом — он выводит свойство jumpsобъекта rabbit.

2.Второй alertхочет вывести rabbit.eats, ищет его в самом объекте rabbit, не находит — и продолжает поиск в объекте rabbit.__proto__, то есть, в данном случае, в animal.

Иллюстрация происходящего при чтении rabbit.eats(поиск идет снизу вверх):

Объект, на который указывает ссылка __proto , называется «прототипом». В данном

случае получилось, что animalявляется прототипом для rabbit.

Также говорят, что объект rabbit«прототипно наследует» от animal.

Обратим внимание — прототип используется исключительно при чтении. Запись значения, например, rabbit.eats = valueили удаление delete rabbit.eats— работает напрямую с

объектом.

В примере ниже мы записываем свойство в сам rabbit, после чего alertперестаёт брать его у прототипа, а берёт уже из самого объекта:

var animal = { eats: true

};

var rabbit = { jumps: true, eats: false

};

rabbit.__proto__ = animal;

alert( rabbit.eats ); // false, свойство взято из rabbit

Другими словами, прототип — это «резервное хранилище свойств и методов» объекта, автоматически используемое при поиске.

У объекта, который является __proto__, может быть свой __proto__, у того — свой, и так далее. При этом свойства будут искаться по цепочке.

Ссылка __proto__ в спецификации

Если вы будете читать спецификацию EcmaScript — свойство __proto__обозначено в ней как [[Prototype]].

Двойные квадратные скобки здесь важны, чтобы не перепутать его с совсем другим свойством, которое называется prototype, и которое мы рассмотрим позже.

Метод hasOwnProperty

Обычный цикл for..inне делает различия между свойствами объекта и его прототипа.

Он перебирает всё, например:

var animal = { eats: true

};

var rabbit = { jumps: true, __proto__: animal

};

for (var key in rabbit) {

alert( key + " = " + rabbit[key] ); // выводит и "eats" и "jumps"

}

Иногда хочется посмотреть, что находится именно в самом объекте, а не в прототипе.

Вызов obj.hasOwnProperty(prop) возвращает true, если свойство propпринадлежит

самому объекту obj, иначе false.

Например:

var animal = { eats: true

};

var rabbit = { jumps: true, __proto__: animal

};

alert( rabbit.hasOwnProperty('jumps') ); // true: jumps принадлежит rabbit

alert( rabbit.hasOwnProperty('eats') ); // false: eats не принадлежит

Для того, чтобы перебрать свойства самого объекта, достаточно профильтровать keyчерез hasOwnProperty:

var animal = { eats: true

};

var rabbit = { jumps: true, __proto__: animal

};

for (var key in rabbit) {

if (!rabbit.hasOwnProperty(key)) continue; // пропустить "не свои" свойства alert( key + " = " + rabbit[key] ); // выводит только "jumps"

}

Методы для работы с __proto__

В современных браузерах есть два дополнительных метода для работы с __proto__. Зачем они нужны, если есть __proto__? В общем-то, не очень нужны, но по историческим причинам тоже существуют.

Чтение: Object.getPrototypeOf(obj)

Возвращает obj.__proto__(кроме IE8-)

Запись: Object.setPrototypeOf(obj, proto)

Устанавливает obj.__proto__ = proto(кроме IE10-).

Кроме того, есть ещё один вспомогательный метод:

Создание объекта с прототипом: Object.create(proto, descriptors)

Создаёт пустой объект с __proto__, равным первому аргументу (кроме IE8-), второй необязательный аргумент может содержать дескрипторы свойств.

Итого

В JavaScript есть встроенное «наследование» между объектами при помощи специального свойства __proto__.

При установке свойства rabbit.__proto__ = animalговорят, что объект animalбудет «прототипом» rabbit.

При чтении свойства из объекта, если его в нём нет, оно ищется в __proto__. Прототип задействуется только при чтении свойства. Операции присвоения obj.prop =или удаления delete obj.propсовершаются всегда над самим объектом obj.

Несколько прототипов одному объекту присвоить нельзя, но можно организовать объекты в цепочку, когда один объект ссылается на другой при помощи __proto__, тот ссылается на третий, и

так далее.

Всовременных браузерах есть методы для работы с прототипом:

Object.getPrototypeOf(obj) (кроме IE8-)

Object.setPrototypeOf(obj, proto) (кроме IE10-)

Object.create(proto, descriptors) (кроме IE8-)

Возможно, вас смущает недостаточная поддержка __proto__в старых IE. Но это не страшно. В последующих главах мы рассмотрим дополнительные методы работы с __proto__, включая те, которые работают везде.

Также мы рассмотрим, как свойство __proto__используется внутри самого языка JavaScript и как организовать классы с его помощью.

Задачи

Чему равно cвойство после delete?

важность: 5

Какие значения будут выводиться в коде ниже?

var animal = { jumps: null

};

var rabbit = { jumps: true

};

rabbit.__proto__ = animal;

alert( rabbit.jumps ); // ? (1)

delete rabbit.jumps;

alert( rabbit.jumps ); // ? (2)

delete animal.jumps;

alert( rabbit.jumps ); // ? (3)

Итого три вопроса.

К решению

Прототип и this

важность: 5

Сработает ли вызов rabbit.eat()?

Если да, то в какой именно объект он запишет свойство full: в rabbitили animal?

var animal = { eat: function() {

this.full = true;

}

};

var rabbit = { __proto__: animal

};

rabbit.eat();

К решению

Алгоритм для поиска

важность: 5

Есть объекты: