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

var head = { glasses: 1

};

var table = { pen: 3

};

var bed = { sheet: 1, pillow: 2

};

var pockets = { money: 2000

};

Задание состоит из двух частей:

1.Присвойте объектам ссылки __proto__так, чтобы любой поиск чего-либо шёл по алгоритму pockets > bed > table > head.

То есть pockets.pen == 3, bed.glasses == 1, но table.money == undefined.

2.После этого ответьте на вопрос, как быстрее искать glasses: обращением к pockets.glassesили head.glasses? Попробуйте протестировать.

Крешению

Свойство F.prototype и создание объектов через new

До этого момента мы говорили о наследовании объектов, объявленных через {...}.

Но в реальных проектах объекты обычно создаются функцией-конструктором через new. Посмотрим, как указать прототип в этом случае.

Свойство F.prototype

Самым очевидным решением является назначение __proto__в конструкторе.

Например, если я хочу, чтобы у всех объектов, которые создаются new Rabbit, был прототип animal, я могу сделать так:

var animal = { eats: true

};

function Rabbit(name) { this.name = name; this.__proto__ = animal;

}

var rabbit = new Rabbit("Кроль");

alert( rabbit.eats ); // true, из прототипа

Недостаток этого подхода — он не работает в IE10-.

К счастью, в JavaScript с древнейших времён существует альтернативный, встроенный в язык и полностью кросс-браузерный способ.

Чтобы новым объектам автоматически ставить прототип, конструктору ставится свойство prototype.

При создании объекта через new, в его прототип __proto__записывается ссылка из prototype функции-конструктора.

Например, код ниже полностью аналогичен предыдущему, но работает всегда и везде:

var animal = { eats: true

};

function Rabbit(name) { this.name = name;

}

Rabbit.prototype = animal;

var rabbit = new Rabbit("Кроль"); // rabbit.__proto__ == animal

alert( rabbit.eats ); // true

Установка Rabbit.prototype = animalбуквально говорит интерпретатору следующее: «При создании

объекта через new Rabbitзапиши ему __proto__ = animal».

Свойство prototypeимеет смысл только у конструктора

Свойство с именем prototypeможно указать на любом объекте, но особый смысл оно имеет, лишь если назначено функции-конструктору.

Само по себе, без вызова оператора new, оно вообще ничего не делает, его единственное назначение — указывать __proto__для новых объектов.

Значением prototypeможет быть только объект

Технически, в это свойство можно записать что угодно.

Однако, при работе new, свойство prototypeбудет использовано лишь в том случае, если это объект. Примитивное значение, такое как число или строка, будет проигнорировано.

Свойство constructor

У каждой функции по умолчанию уже есть свойство prototype.

Оно содержит объект такого вида:

function Rabbit() {}

Rabbit.prototype = { constructor: Rabbit

};

В коде выше я создал Rabbit.prototypeвручную, но ровно такой же — генерируется автоматически.

Проверим:

function Rabbit() {}

// в Rabbit.prototype есть одно свойство: constructor

alert( Object.getOwnPropertyNames(Rabbit.prototype) ); // constructor

// оно равно Rabbit

alert( Rabbit.prototype.constructor == Rabbit ); // true

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

function Rabbit(name) { this.name = name; alert( name );

}

var rabbit = new Rabbit("Кроль");

var rabbit2 = new rabbit.constructor("Крольчиха");

Эта возможность бывает полезна, когда, получив объект, мы не знаем в точности, какой у него был конструктор (например, сделан вне нашего кода), а нужно создать такой же.

Свойство constructorлегко потерять

JavaScript никак не использует свойство constructor. То есть, оно создаётся автоматически, а что с ним происходит дальше — это уже наша забота. В стандарте прописано только его создание.

В частности, при перезаписи Rabbit.prototype = { jumps: true }свойства constructor

больше не будет.

Сам интерпретатор JavaScript его в служебных целях не требует, поэтому в работе объектов ничего не «сломается». Но если мы хотим, чтобы возможность получить конструктор, всё же, была, то можно при перезаписи гарантировать наличие constructorвручную:

Rabbit.prototype = { jumps: true, constructor: Rabbit

};

Либо можно поступить аккуратно и добавить свойства к встроенному prototypeбез его замены:

// сохранится встроенный constructor Rabbit.prototype.jumps = true

Эмуляция Object.create для IE8-

Как мы только что видели, с конструкторами всё просто, назначить прототип можно кросс-браузерно при помощи F.prototype.

Теперь небольшое «лирическое отступление» в область совместимости.

Прямые методы работы с прототипом осутствуют в старых IE, но один из них — Object.create(proto)можно эмулировать, как раз при помощи prototype. И он будет работать

везде, даже в самых устаревших браузерах.

Кросс-браузерный аналог — назовём его inherit, состоит буквально из нескольких строк:

function inherit(proto) { function F() {} F.prototype = proto; var object = new F; return object;

}

Результат вызова inherit(animal)идентичен Object.create(animal). Она создаёт новый пустой объект с прототипом animal.

Например:

var animal = { eats: true

};

var rabbit = inherit(animal);

alert( rabbit.eats ); // true

Посмотрите внимательно на функцию inheritи вы, наверняка, сами поймёте, как она работает…

Если где-то неясности, то её построчное описание:

function inherit(proto) { function F() {} // (1) F.prototype = proto // (2) var object = new F; // (3) return object; // (4)

}

1.Создана новая функция F. Она ничего не делает с this, так что если вызвать new F, то получим пустой объект.

2.Свойство F.prototypeустанавливается в будущий прототип proto

3.Результатом вызова new Fбудет пустой объект с __proto__равным значению F.prototype.

4.Мы получили пустой объект с заданным прототипом, как и хотели. Возвратим его.

Для унификации можно запустить такой код, и метод Object.createстанет кросс-браузерным:

if (!Object.create) Object.create = inherit; /* определение inherit выше */

В частности, аналогичным образом работает библиотека es5-shim , при подключении которой Object.createстанет доступен для всех браузеров.

Итого

Для произвольной функции — назовём её Constructor, верно следующее:

Прототип __proto__новых объектов, создаваемых через new Constructor, можно задавать при помощи свойства Constructor.prototype.

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

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

Современный метод Object.create(proto)можно эмулировать при помощи prototype, если хочется, чтобы он работал в IE8-.

Задачи

Прототип после создания

важность: 5

В примерах ниже создаётся объект new Rabbit, а затем проводятся различные действия с prototype.

Каковы будут результаты выполнения? Почему?

Начнём с этого кода. Что он выведет?

function Rabbit() {} Rabbit.prototype = {

eats: true };

var rabbit = new Rabbit();

alert( rabbit.eats );

Добавили строку (выделена), что будет теперь?

function Rabbit() {} Rabbit.prototype = {

eats: true };

var rabbit = new Rabbit();

Rabbit.prototype = {};

alert( rabbit.eats );

А если код будет такой? (заменена одна строка):

function Rabbit(name) {} Rabbit.prototype = {

eats: true };

var rabbit = new Rabbit();

Rabbit.prototype.eats = false;

alert( rabbit.eats );

А такой? (заменена одна строка)

function Rabbit(name) {} Rabbit.prototype = {

eats: true };

var rabbit = new Rabbit();

delete rabbit.eats; // (*)

alert( rabbit.eats );

И последний вариант:

function Rabbit(name) {} Rabbit.prototype = {

eats: true };

var rabbit = new Rabbit();

delete Rabbit.prototype.eats; // (*)

alert( rabbit.eats );

К решению

Аргументы по умолчанию

важность: 4

Есть функция Menu, которая получает аргументы в виде объекта options:

/* options содержит настройки меню: width, height и т.п. */ function Menu(options) {

...

}

Ряд опций должны иметь значение по умолчанию. Мы могли бы проставить их напрямую в объекте options:

function Menu(options) {

options.width = options.width || 300; // по умолчанию ширина 300

...

}

…Но такие изменения могут привести к непредвиденным результатам, т.к. объект optionsможет быть повторно использован во внешнем коде. Он передается в Menuдля того, чтобы параметры из него читали, а не писали.

Один из способов безопасно назначить значения по умолчанию — скопировать все свойства optionsв локальные переменные и затем уже менять. Другой способ — клонировать optionsпутём

копирования всех свойств из него в новый объект, который уже изменяется.

При помощи наследования и Object.createпредложите третий способ, который позволяет избежать копирования объекта и не требует новых переменных.

К решению