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

时间:2010-10-25 08:42:10

标签: javascript prototype constructor

试图绕过Javascript对OO的看法......和许多其他人一样,对constructor属性感到困惑。特别是constructor属性的重要性,因为我似乎无法使其产生任何影响。 E.g:

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构造函数,那么这个原型属性有什么影响呢?我很想知道它实际上使构造函数属性设置'正确'有什么实际区别 - 因为我看不出它对创建对象后实际调用哪个构造函数有任何影响。

6 个答案:

答案 0 :(得分:98)

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

构造函数

constructor属性不会导致程序中的任何特定效果,除了您可以查看它以查看与运算符new一起使用的功能创建你的对象。如果您输入new Bar(),则会Bar,而您输入new Foo则会Foo

原型

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

那么__proto__是什么以及它与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。

答案 1 :(得分:70)

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

答案 2 :(得分: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);
    };
    
  3. 所以构造函数属性在这里有意义。

答案 3 :(得分:4)

在此之前的回答(以各种方式)表明constructor属性的值没有被JavaScript本身使用。写下这些答案后确实如此,但ES2015及更高版本已开始使用constructor做事。

函数的constructor属性的prototype属性旨在指向该函数,以便您可以询问对象构造它的原因。它是在创建传统函数对象或类构造函数对象(details)的过程中自动设置的。

function TraditionalFunction() {
}

console.log(TraditionalFunction.prototype.constructor === TraditionalFunction); // true

class ExampleClass {
}

console.log(ExampleClass.prototype.constructor === ExampleClass); // true

箭头功能没有prototype属性,因此它们没有prototype.constructor

多年来,JavaScript规范仅声明constructor属性在那里,并且默认情况下具有该值(指向该函数的链接)。但是从ES2015开始,情况发生了变化,并且规范中的各种操作现在实际上都使用constructor属性,例如thisthisthis和{{3} }。

因此,在设置构建继承链的构造函数时,最好确保constructor属性引用了适当的函数。有关示例,请参见this

答案 4 :(得分: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.

答案 5 :(得分:0)

构造函数属性指向用于创建对象实例的构造函数。如果你输入了新的Bar()'它将是' Bar'然后你输入了新的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'