Javascript构造函数属性的意义是什么?

问题描述 投票:117回答:5

试图围绕Javascript对OO的看法......和许多其他人一样,对constructor属性感到困惑。特别是,constructor属性的意义,因为我似乎无法使它有任何影响。例如。:

function Foo(age) {
    this.age = age;
}

function Bar() {
    Foo.call(this, 42);
    this.name = "baz"; 
}

Bar.prototype = Object.create(Foo.prototype); 
var b = new Bar;    

alert(b.constructor); // "Foo". That's OK because we inherit `Foo`'s prototype.
alert(b.name);        // "baz". Shows that Bar() was called as constructor.
alert(b.age);         // "42", inherited from `Foo`.

在上面的例子中,对象b似乎有正确的构造函数(Bar) - 它继承了Foo的age属性。那么为什么许多人认为这是必要的步骤:

Bar.prototype.constructor = Bar;

很明显,在构建Bar时调用了正确的b构造函数,那么这个原型属性有什么影响呢?我很想知道它实际上使构造函数属性设置'正确'有什么实际区别 - 因为我无法看到它对创建对象后实际调用的构造函数有任何影响。

javascript prototype constructor
5个回答
70
投票

constructor属性对内部任何事物都没有实际的区别。只有在您的代码明确使用它时,它才有用。例如,您可能决定需要每个对象都具有对创建它的实际构造函数的引用;如果是这样,您需要在设置继承时通过将对象分配给构造函数的constructor属性来显式设置prototype属性,如示例所示。


98
投票

第一步是了解constructorprototype的全部内容。这并不困难,但人们不得不放弃传统意义上的“继承”。

构造函数

constructor属性不会在程序中产生任何特殊效果,除了您可以查看它以查看与运算符new一起使用哪个函数来创建对象。如果你输入new Bar()它将是Bar你输入new Fooit将是Foo

原型

prototype属性用于查找,以防有问题的对象没有要求的属性。如果你写x.attr,JavaScript将尝试在attr的属性中找到x。如果它找不到它,它将在x.__proto__看。如果它不存在,只要x.__proto__.__proto__被定义,它将在__proto__等等。

那么什么是__proto__and与prototype有什么关系呢?简而言之,prototype用于“类型”,而__proto__用于“实例”。 (我用引号说,因为类型和实例之间没有任何区别)。当你写x = new MyType()时,发生的事情(除其他外)是x.__proto___设置为MyType.prototype

这个问题

现在,上面应该是你需要得出你自己的例子意味着什么,但试图回答你的实际问题; “为什么要这样写”:

Bar.prototype.constructor = Bar;

我个人从来没有见过它,我发现它有点傻,但在你给它的上下文中意味着Bar.prototype对象(使用new Foo(42)创建)将构成由Bar而不是Foo创建。我想这个想法是有些类似于C ++ / Java / C#类似的语言,其中类型查找(constructor属性)将始终产生最具体的类型,而不是原型中更通用的对象的类型 - 链。

我的建议:不要过多考虑JavaScript中的“继承”。接口和mixin的概念更有意义。并且不要检查对象的类型。检查所需的属性(“如果它像鸭子一样走路,像鸭子一样呱呱叫,它是一只鸭子”)。

试图强制JavaScript进入经典的继承模型,当它具有如上所述的原型机制时,就会引起混淆。建议手动设置constructor属性的许多人可能试图这样做。抽象很好,但是构造函数属性的这种手动赋值并不是非常惯用的JavaScript。


10
投票

一个使用构造函数的情况:

  1. 这是继承的常见实现之一: Function.prototype.extend = function(superClass,override) { var f = new Function(); f.prototype = superClass.prototype; var p = this.prototype = new f(); p.constructor = this; this.superclass = superClass.prototype; ... };
  2. 这个new f()不会调用superClass的构造函数,所以当你创建一个subClass时,你可能需要先调用superClass,如下所示: SubClass = function() { SubClass.superClass.constructor.call(this); };

所以构造函数属性在这里有意义。


2
投票

当您希望prototype.constructor属性在prototype属性重新分配中存活时,其中一个用例是在prototype上定义一个方法,该方法生成与给定实例相同类型的新实例。例:

function Car() { }
Car.prototype.orderOneLikeThis = function() {  // Clone producing function
    return new this.constructor();
}
Car.prototype.advertise = function () {
    console.log("I am a generic car.");
}

function BMW() { }
BMW.prototype = Object.create(Car.prototype);
BMW.prototype.constructor = BMW;              // Resetting the constructor property
BMW.prototype.advertise = function () {
    console.log("I am BMW with lots of uber features.");
}

var x5 = new BMW();

var myNewToy = x5.orderOneLikeThis();

myNewToy.advertise(); // => "I am BMW ..." if `BMW.prototype.constructor = BMW;` is not 
                      // commented; "I am a generic car." otherwise.

0
投票

构造函数属性指向用于创建对象实例的构造函数。如果您输入'new Bar()',它将是'Bar',您输入'new Foo()'它将是'Foo'。

但是如果你在没有设置构造函数的情况下设置原型,你会得到这样的东西:

function Foo(age) {
    this.age = age;
}

function Bar() {
    this.name = "baz"; 
}

Bar.prototype = new Foo(42); 
var one = new Bar();
console.log(one.constructor);   // 'Foo'
var two = new Foo();
console.log(two.constructor);   // 'Foo'

要将构造函数实际设置为用于创建对象的构造函数,我们还需要在设置原型时设置构造函数,如下所示:

function Foo(age) {
    this.age = age;
}

function Bar() {
    this.name = "baz"; 
}

Bar.prototype = new Foo(42); 
Bar.prototype.constructor = Bar;
var one = new Bar();
console.log(one.constructor);   // 'Bar'
var two = new Foo();
console.log(two.constructor);   // 'Foo'
© www.soinside.com 2019 - 2024. All rights reserved.