JS中原型构造函数的使用

10

有人能够解释一下Me.prototype.constructor = Me;的用途以及为什么需要它吗?即使没有这行代码也可以正常工作,为什么还要这样做呢?

在代码中,原型对象被创建在Me对象上并实例化,取代了旧的原型对象。那么,在给定的代码中,为什么需要指向Me构造函数呢?

function Me(){
    this.name = 'Dejan';

}

function You(){
    this.name = 'Ivan';
}

Me.prototype = new You();

somebody = new Me();

Me.prototype.constructor = Me; // Why?

Me.prototype.foo = function(){
    alert('Proto Me!'); // It always fire up this alert, ether constructor is pointing to Me or not... !
}

You.prototype.foo = function(){
    alert('Proto You!');
}

somebody.foo();
alert(somebody.name); // Alert 'Dejan'

我相信旧版浏览器会检查 .constructor 属性以使用 instanceof 关键字。 - Raynos
2个回答

11

这不是必要的,而且与流行的观点相反,instanceof 甚至不需要它(instanceof 内部检查原型链并不需要构造函数属性)。通常情况下,constructor 是构造函数的 prototype 上天然的不可枚举属性。这样,任何由该构造函数实例化的对象都会拥有一个非枚举的 constructor 属性,指向该构造函数。

如果需要的话最好将其设置为非枚举属性。某些代码会假定对象存在 .constructor 属性。

在您发布的代码中,是的,在进行继承时,如果你希望它存在,重置构造函数就是必要的,因为作为子级原型的实例化对象具有指向错误构造函数的构造函数属性(即其自身构造函数)。

在 ES5 中,您可以这样写:

Child.prototype = Object.create(Parent.prototype, {
  constructor: { value: Child, enumerable: false }
});

编辑:另外需要提到的是,使用非标准的__proto__进行继承时,并不需要重置构造函数,因为__proto__仅指定了一个对象的原型,也就是说,在自有属性不存在时查找的对象。一个新的prototype总是会有一个名为constructor的属性。

所以在执行以下操作时:

var child = function() {};
child.prototype.__proto__ = parent.prototype;

您无需设置构造函数,因为子类的原型(child.prototype)仍然存在基础构造函数属性。如果访问该属性,则不需要执行原型链查找。


2
你确定IE6不检查constructor属性吗?而且Object.create会为你设置constructor属性,你不必手动设置。 - Raynos
4
Raynos,不,我对IE6不确定,因为我并不真的关心IE6。此外,Object.create不会为您设置构造函数,它没有构造函数的概念,事实上,这就是Object.create的重点。您只会得到一个等于父构造函数的构造函数属性,这对于继承来说是不正确的。因此,您需要进行更改。 - chjj
1
你是对的,我的错,关于“constructor”。Object.create没有构造函数的概念。 - Raynos

5
如果你替换这行代码
Me.prototype.constructor = Me; // Why?

使用

console.log(Me.prototype.constructor);
Me.prototype.constructor = Me; // Why?

你会发现,在进行设置之前,Me.prototype.constructorYou,因为Me.prototypeYou的实例,这是由于这行代码导致的。
Me.prototype = new You();

因此,带有// Why?注释的行是必要的,以“修复”通过这种方式进行继承时给JavaScript带来的错误印象。
基本上,问题出现在您试图使用原型继承来实现经典继承。原型继承适用于对象实例,并且没有“类”或甚至真正的“类型”的概念,但是JavaScript通过整个new.constructorinstanceof使事情变得更加混乱。
这种情况下更具原型特征的做法是放弃构造函数,而是使用强大的构造函数,即返回所需形式对象的函数:
function begetPart(partNumber,  description) {
    return Object.create({}, {
        number: { value: partNumber },
        description: { value: description },
        describe: {
            value: function () {
                alert(this.description);
            }
        }
    });
}

function begetTire(partNumber, speed) {
    return Object.create(
        begetPart(partNumber, "A tire"),
        {
            speed: { value: speed },
            describe: {
                value: function () {
                    alert(this.description + "; speed = " + this.speed);
                }
            }
        }
    );
}

var genericPart = begetPart(1234, "A widget");
genericPart.describe(); // alerts "A widget"

var tire = begetTire(4567, "fast");
tire.describe(); // alerts "A tire; speed = fast"

在这里,我们使用 Object.create 来表示“基于另一个对象实例创建一个对象实例”。对于 begetPart,另一个实例是一个新的空对象;对于 begetTire,则是一个预先填充了一些属性的新“部件实例”。
这种方式更好地反映了 JavaScript 和原型继承的实际工作方式,因为在原型继承中,对象实例继承自其他对象实例,而不会被 “类型” 或 “类” 的概念所束缚。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接