свойства объекта-прототипа и конструктора - PullRequest
10 голосов
/ 12 февраля 2009

Я:

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 ]

25 голосов
/ 12 февраля 2009

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() {};

Это также делает тривиальное наследование тривиальным.

9 голосов
/ 12 февраля 2009

Проверьте ООП Тома Тренки с ECMAscript , страница "Наследование". Все от прототипа наследуется, включая свойство constructor. Таким образом, мы должны разобраться сами:

Obj2.prototype = new Obj1(42);
Obj2.prototype.constructor = Obj2;
3 голосов
/ 12 февраля 2009

Короткая версия: «конструктор» не делает то, что вы думаете, и не совместим с разными браузерами. Никогда не используйте его.

Длинная версия: Соглашение о наследовании прототипов в 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);
1 голос
/ 12 февраля 2009

Ну, свойство конструктора является свойством, как и любое другое, в прототипе (свойство) 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()

Aha! Так что у obj нет конструктора, JS идет, чтобы получить его по цепочке [[Prototype]], начиная с Obj1.prototype.constructor

Я не уверен, почему свойство конструктора не просто устанавливается для объекта, когда вы используете `new '. Может быть причина, или это может быть просто недосмотр. В любом случае, я склонен избегать этого.

...