Ответ 1
"Prototype" - это то, что играет роль в объектах.
В Javascript все является объектом. Каждый объект имеет вид и, таким образом, наследует prototype
такого типа.
Например, возьмите простой массив: var a = []
. Вы можете выполнять операции с ним, например a.push(10)
. Откуда этот метод push
? Из прототипа объекта Array
, который a
есть.
Вы можете добавить свои собственные методы к объектам Array
, просто указав их в объекте prototype
. Например:
Array.prototype.sortNum = function() {this.sort(function(a, b) {return a - b});};
Таким образом вы можете сделать что-то вроде a.sortNum()
со всеми массивами, даже теми, которые были созданы до того, как вы определили метод sortNum
.
(Примечание: по соображениям совместимости обычно не рекомендуется расширять прототип собственных объектов, таких как Array
s. Но этот конкретный пример обычно является приятным дополнением, а также нормализацией таких методов, как map
и forEach
для старых браузеров.)
(Только никогда никогда не расширяется Object.prototype
! Если вы не хотите испортить операторы for...in
, оператор in
и подобные случаи.)
Если вы хотите определить свои собственные классы, как предлагает название MyConstructor
, вам нужно будет определить его prototype
, чтобы определить методы для всех экземпляров этого класса:
function MyConstructor(name) {this.name = name};
MyConstructor.prototype = {
print: function() {return this.name;}
};
var mc = new MyConstructor("foo");
alert(mc.print()); // alerts "foo"
Вы можете определить больше, чем просто функции в prototype
s:
MyConstructor.prototype.age = 30;
alert(mc.age); // alerts 30
Остерегайтесь, когда вы делаете это, чтобы определить значения объекта по умолчанию, поскольку изменение его может привести к изменению во всех экземплярах этого класса.
Но это удобно при использовании Object.defineProperty
:
Object.defineProperty(MyConstructor.prototype, "wholeString", {
get: function() {return this.name + "=" + this.age;},
set: function(v) {this.name = v.substring(3);}
});
alert(mc.wholeString); // alerts "foo = 30"
(К сожалению, IE < 9 допускает это только для объектов DOM...)
Когда вы определяете MyConstructor.age = 30
вместо этого, то, что вы на самом деле делаете, определяет член функции MyConstructor
, поэтому mc.age
будет undefined. Каждый экземпляр MyConstructor
наследует методы и члены, определенные в MyConstructor.prototype
, а не те функции MyConstructor
.
На самом деле гораздо больше можно сказать. Объекты могут быть подкласса другого класса, таким образом, наследуя prototype
суперкласса. Например, document.body
является экземпляром HTMLBodyElement
, который является подклассом HTMLElement
, который является подклассом Element
и т.д., Пока вы не получите Object
в качестве верхнего суперкласса. Таким образом, document.body
наследует все методы, определенные в прототипе HTMLBodyElement
, HTMLElement
, Element
и Object
. Это называется цепочкой прототипов.
Выполнение этого же с пользовательскими объектами немного сложно:
function Class() {};
Class.prototype.foo = function() {alert("foo");};
function Subclass() {};
Subclass.prototype = new Class();
Subclass.prototype.bar = function() {alert("bar");};
var a = new Class(), b = new Subclass();
a.foo(); // alerts"foo"
a.bar(); // throws an error
b.foo(); // alerts "foo"
b.bar(); // alerts "bar"
a instanceof Class; // true
a instanceof Subclass; // false
b instanceof Class; // true
b instanceof Subclass; // true