В предыдущей лекциибыла создана функция 'Cat' :
function Cat(name){
this.name = name;
}
Cat.prototype = {
species: 'Cat',
talk: function(){ alert('Meow!'); },
callOver: function(){ alert(this.name+' ignores you'); },
pet: function(){ alert('Pet!'); }
}
Теперь можно создать любое количество котов, но как быть, если мы захотим создать объект другого типа, например, собаку? В этом случае понадобится создать совершенно новую функцию, со своими собственными прототипами. Если два объекта используют одни и те же функции (например, можно было бы добавить функции sleep (спать), eat (есть), и play (играть)), то в результате мы бы имели чрезмерное дублирование кода. Решением является концепция наследования.
По сути наследование позволяет определить объекты "предки" и "потомки". Потомок наследует все свойства своего предка. Можно было создать, например, функцию Animal , Pet или Mammal . Обе функции Cat и Dog обладали бы многими свойствами функции предка Animal , и нам пришлось бы писать этот код один раз.
Проблема в том, что JavaScript не имеет в действительности встроенного механизма наследования, поэтому эту функциональность необходимо создавать самостоятельно. Для этого существует несколько различных способов. Один из них состоит в использовании функции call . Эта функция позволяет вызывать одну функцию из контекста другой, т.е. мы можем определить, как действует ключевое слово this . С помощью call можно написать класс Animal (Животное), а затем вызвать его из класса Cat или Dog .
function Animal(name){
this.name = name;
this.species = 'Animal';
this.sleep = function(){ alert(this.name+' спит: Хрррр'); }
}
function Cat(name){
Animal.call(this, name);
this.talk = function(){ alert('Мяу!'); }
}
function Dog(name){
Animal.call(this, name);
this.talk = function(){ alert('Гав!'); }
}
var sam = new Cat('Sam');
var joe = new Dog('Joe');
sam.sleep(); // Sam спит: Хрррр
joe.sleep(); // Joe спит: Хрррр
sam.talk(); // Мяу!
joe.talk(); // Гав!
Хотя это работает, мы немного ограничены в своих возможностях. Например, прототипирование не действует при использовании этого метода: все прототипы, заданные на Animal , не будут переноситься в функции Cat или Dog . Как мы знаем из предыдущей лекции, определенные внутренне с помощью " this. " функции создают новый экземпляр всякий раз при создании новой копии предка. В этом случае всякий раз при создании функции Animal , Cat или Dog появляется новая копия функций species и sleep . Как можно догадаться, это не самый эффективный способ.
Лучшим подходом является прототипирование всего родительского класса на классе-потомке. Это предоставляет доступ ко всем свойствам и методам класса предка:
function Animal(name){
this.name = name;
}
Animal.prototype = {
species: 'Animal',
sleep : function(){ alert(this.name+' спит: Хрррр'); }
}
function Cat(name){
Animal.apply(this, arguments);
}
Cat.prototype = new Animal;
Cat.prototype.species = 'Cat';
Cat.prototype.talk = function(){ alert('Мяу!'); }
function Dog(name){
Animal.apply(this, arguments);
}
Dog.prototype = new Animal;
Dog.prototype.talk = function(){ alert('Гав!'); }
var sam = new Cat('Sam');
var joe = new Dog('Joe');
sam.sleep(); // Sam спит : Хрррр
joe.sleep(); // Joe спит: Хрррр
alert(sam.species); // Cat
alert(joe.species); // Animal - для Dog функция species не определена
Можно продолжить это дальше и создать отдельные функции для различных пород собак или кошек и т.д.
Замыкание ( сlosure ) является одним из наиболее мощных средств JavaScript . Если воспользоваться простым объяснением, то замыкание связывает внутренние и внешние переменные в функции. Почему это так важно? Потому что замыкание позволяет эмулировать почти любое свойство любого языка программирования, даже если оно не существует в JavaScript . Это звучит немного непонятно, поэтому лучше начать с более простого примера:
function beginAdding(a){
a *= 5;
return function finishAdding(b){ alert(a+b); }
}
var add = beginAdding(10);
add(20); // 70
Можно видеть, что в приведенном коде переменной 'a' присваивается значение 10 и передается в функцию beginAdding , в то время как переменной 'b' присваивается значение 20 и передается в функцию finishAdding .
А что содержится в переменной 'add' ? Она содержит функцию finishAdding с копией связанной с ней всей функции beginAdding . Копия переменной 'a' из beginAdding сохраняется в памяти для дальнейшего использования.
Теперь, имея представление о замыкании, прежде чем продолжать, необходимо обсудить проблему утечки памяти. Internet Explorer , в частности, подвержен достаточно неприятным утечкам памяти при использовании замыкания. Чтобы понять, почему необходимо знать о сборке мусора в Internet Explorer , посмотрим, как в этом браузере выполняется очистка памяти от ненужных объектов.
Читать дальше