Лучшим подходом является прототипирование всего родительского класса на классе-потомке. Это предоставляет доступ ко всем свойствам и методам класса предка:
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, посмотрим, как в этом браузере выполняется очистка памяти от ненужных объектов.
Internet Explorer имеет два отдельных сборщика мусора: один для JavaScript и другой для объектов DOM. При выгрузке страницы браузер просматривает и удаляет весь код JavaScript и все объекты DOM со страницы. Утечка происходит, когда имеются циклические ссылки из объекта DOM в JavaScript и снова на объект DOM или из JavaScript-->Dom-->Javascript. Internet Explorer запутывается и не удаляет объекты при циклической ссылке.
var someInput = document.getElementById('inputbox');
var someFunction = function(){
alert(someInput.value);
}
someInput.onclick = someFunction;
Здесь представлен бесконечный цикл в терминах замыканий. Объект DOM someInput вовлечен в замыкание с функцией someFunction и наоборот. Браузер не может определить, что удалить в первую очередь, поэтому в результате имеем утечку памяти.
Замыкания создают, часто даже не осознавая этого. Возьмем, например, простую функцию:
function Animal(name){
this.sleep = function(){ alert(name+' спит: Хрррр'); }
}
Можно не заметить этого, но переменная 'name' в функции sleep приходит из родительской функции Animal. Это создает замыкание.
Даже определение простейшей функции может создавать замыкание:
var x = 5;
var n = function(){
y=10;
return y;
}
Здесь также создается замыкание, хотя на первый взгляд это не так. Почему? Когда мы создаем функцию, она получает доступ ко всем переменным в своей текущей области действия, поэтому мы создаем новую ссылку на переменную х.
Теперь мы подошли к понятию области действия. Каждая функция имеет свою область действия, в которой она выполняется. Все области действия кода сохраняются во внутреннем стеке памяти. Когда создается замыкание, оно получает доступ к одной из этих областей действия. Если создается несколько замыканий в одной и той же области действия, то каждое замыкание будет в действительности указывать на одинаковые копии каждой переменной области действия. Например:
var x = 5;
var alertX1 = function(){ alert(x); }
x = 10;
var alertX2 = function(){ alert(x); }
alertX1();
alertX2();
Обе функции в этом случае выведут 10. Почему? Потому что они обе указывают на одну и ту же копию х.
Если изменить x в любой точке, то обе функции отразят это. Как это исправить? Проще всего изменить область действия замыкания:
function makeClosure(x){
return function(){ alert(x); }
}
var x = 5;
var alertX1 = makeClosure(x);
x = 10;
var alertX2 = makeClosure(x);
alertX1(); // 5
alertX2(); // 10
Это решает проблему. Если, однако, этот код создавал утечку памяти, то утечка будет существенно больше, чем в предыдущем примере; и также используется больший объем памяти. Оказывается, что в действительности имеется три различных области действия в этом простом примере:
Можно видеть, что переменная х копируется в каждую из двух областей действия. Это связано с тем, что x является строкой или числом. JavaScript всегда передает строки и числа по значению - то есть всегда делается копия переменной. С объектами все происходит иначе. Если х является функцией, массивом или базовым объектом, то в этом случае ссылка в двух наших функциях происходит на одну и ту же копию x и поэтому в результате выводимое сообщение будет одинаковым:
function makeClosure(x){
return function(){ alert(x.val); }
}
var x = {vaclass="underline" 5};
var alertX1 = makeClosure(x);
x.val = 10;
var alertX2 = makeClosure(x);
alertX1(); // 10
alertX2(); // 10
Отличие состоит в том, что x теперь является объектом. Каждая из дополнительных областей действия указывает на одну и ту же копию x. При изменении значения x оно изменяется в каждой области действия, в которой х упоминается.