Свойства объекта-прототипа и конструктора
Я:
function Obj1(param)
{
this.test1 = param || 1;
}
function Obj2(param, par)
{
this.test2 = param;
}
теперь, когда я делаю:
Obj2.prototype = new Obj1(44);
var obj = new Obj2(55);
alert(obj.constructor)
Я имею:
function Obj1(param) {
this.test1 = param || 1;
}
но функция конструктора была Obj2... почему это? Obj1 стал прототипом Obj2...
Может кто-нибудь подробно объяснить мне цепочку прототипов и свойство конструктора
Спасибо
4 ответа
constructor
является обычным свойством объекта-прототипа (с DontEnum
флаг установлен, поэтому он не отображается в for..in
петли). Если вы замените объект-прототип, constructor
свойство будет также заменено - см. это объяснение для получения дополнительной информации.
Вы можете обойти проблему, установив вручную Obj2.prototype.constructor = Obj2
но таким образом, DontEnum
флаг не будет установлен.
Из-за этих проблем не стоит полагаться на constructor
для проверки типа: используйте instanceof
или же isPrototypeOf()
вместо.
Андрей Федоров поднял вопрос, почему new
не назначает constructor
свойство объекта экземпляра вместо. Я предполагаю, что причина этого заключается в следующем:
Все объекты, созданные из одной и той же функции конструктора, имеют общее свойство конструктора, а общие свойства находятся в прототипе.
Реальная проблема заключается в том, что JavaScript не имеет встроенной поддержки иерархий наследования. Есть несколько способов решения этой проблемы (один из них у вас), еще один "в духе" JavaScript будет следующим:
function addOwnProperties(obj /*, ...*/) {
for(var i = 1; i < arguments.length; ++i) {
var current = arguments[i];
for(var prop in current) {
if(current.hasOwnProperty(prop))
obj[prop] = current[prop];
}
}
}
function Obj1(arg1) {
this.prop1 = arg1 || 1;
}
Obj1.prototype.method1 = function() {};
function Obj2(arg1, arg2) {
Obj1.call(this, arg1);
this.test2 = arg2 || 2;
}
addOwnProperties(Obj2.prototype, Obj1.prototype);
Obj2.prototype.method2 = function() {};
Это делает множественное наследование также тривиальным.
Проверьте ООП Тома Тренки с ECMAscript, страницей "Наследование". Все от прототипа наследуется, включая constructor
имущество. Таким образом, мы должны разобраться с этим сами:
Obj2.prototype = new Obj1(42);
Obj2.prototype.constructor = Obj2;
Короткая версия: "конструктор" не делает то, что вы думаете, и не совместим с разными браузерами. Никогда не используйте это.
Длинная версия: Соглашение о наследовании прототипов в JavaScript
В целом: вы сбиты с толку из-за (а) несоответствия импедансов между ОО на основе классов и на основе прототипов, и (б) странности специфической довольно слабой интерпретации JavaScript ОО на основе прототипов.
Вы, вероятно, будете счастливее, если найдете подходящую реализацию классов в прототипах и будете придерживаться ее. У многих библиотек есть один. Вот произвольный, который я использую:
Function.prototype.subclass= function() {
var c= new Function(
'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
'if (arguments[0]!==Function.prototype.subclass.FLAG && this._init) this._init.apply(this, arguments); '
);
if (this!==Object)
c.prototype= new this(Function.prototype.subclass.FLAG);
return c;
}
Function.prototype.subclass.FLAG= new Object();
И вот пример того, как его можно использовать:
// make a new class
var Employee= Object.subclass();
// add members to it
Employee.prototype._LEGS= 2;
Employee.prototype.getLegs= function() {
return this._LEGS;
};
// optional initialiser, takes arguments from constructor
Employee.prototype._init= function(name) {
this.name= name;
};
// make a subclass
Manager= Employee.subclass();
// extend subclass method
Manager.prototype._init= function(name, importance) {
// call base class's method
Employee.prototype._init.call(this, name);
this.importance= importance;
}
// all managers are well-known to have three legs
Manager.prototype._LEGS= 3;
// create one
var jake= new Manager('Jake the Peg', 100);
Ну, свойство конструктора является свойством, как и любое другое, в прототипе (свойство) Obj1. Если вы понимаете, как работают прототипы, это может помочь:
>>> obj.hasOwnProperty("constructor")
false
// obj's [[Prototype]] is Obj2.prototype
>>> Obj2.prototype.hasOwnProperty("constructor")
false
// Obj2.prototype's [[Prototype]] is Obj1.prototype
>>> Obj1.prototype.hasOwnProperty("constructor")
true
// Oh?
>>> Obj1.prototype.constructor
Obj1()
Ага! Так что у obj нет конструктора, JS идет, чтобы получить его вверх по цепочке [[Prototype]], начиная с Obj1.prototype.constructor
Я не уверен, почему свойство конструктора не просто устанавливается для объекта, когда вы используете `new'. Может быть причина, или это может быть просто недосмотр. В любом случае, я склонен избегать этого.