Javascript构造函数属性的意义是什么?
试图绕过Javascript对OO的理解……和其他许多人一样,对Javascript构造函数属性的意义是什么?,javascript,prototype,constructor,Javascript,Prototype,Constructor,试图绕过Javascript对OO的理解……和其他许多人一样,对构造函数属性感到困惑。特别是构造函数属性的重要性,因为我似乎无法使它产生任何效果。例如: 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
构造函数属性感到困惑。特别是构造函数
属性的重要性,因为我似乎无法使它产生任何效果。例如:
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;
显然,在构造b
时调用了正确的条
构造函数,那么这个原型属性有什么影响呢?我很想知道正确设置构造函数属性实际上会有什么实际区别,因为我看不出它对创建对象后实际调用哪个构造函数有任何影响。2020年9月更新
下面的答案来自ECMAScript 3的时代,第一句话不再正确,因为自ECMAScript 6以来,构造函数
属性在一些地方使用。不过,我认为总的要点仍然适用。感谢T.J.Crowder在评论中指出了这一点,请阅读他的答案,以更全面地了解当前的情况
原始答案
构造函数
属性对内部任何内容都没有实际意义。只有当代码显式使用它时,它才有任何用处。例如,您可能决定需要每个对象都有一个对创建它的实际构造函数的引用;如果是这样,您需要在通过将对象分配给构造函数的prototype
属性来设置继承时显式设置constructor
属性,如您的示例所示。第一步是了解构造函数和prototype
都是关于什么的。这并不难,但我们必须放弃古典意义上的“继承”
构造函数
constructor
属性不会在程序中产生任何特定的影响,除非您可以查看它以查看哪个函数与操作符new
一起用于创建对象。如果您键入newbar()
它将是Bar
,并且您键入newfoo
它将是Foo
原型
prototype
属性用于查找相关对象没有要求的属性的情况。如果您编写x.attr
,JavaScript将尝试在x
的属性中查找attr
。如果找不到它,它将在x.\uu proto\uu
中查找。如果它也不在那里,只要定义了\uuuuuu proto\uuuuuuu,它就会在x.\uuuuuuu proto\uuuuuuuuu
中查找
那么什么是\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?简而言之,prototype
表示“类型”,而表示“实例”。(我用引号表示,因为类型和实例之间实际上没有任何区别)。当您编写x=new MyType()
时,发生的事情(除其他外)是x.\uuuu proto\uuuuuuu
设置为MyType.prototype
问题
现在,以上应该是你所需要的全部来推导你自己的例子的意思,但要尝试并回答你的实际问题;“为什么要写这样的东西”:
Bar.prototype.constructor=Bar代码>
我个人从未见过它,我觉得它有点傻,但在您给出的上下文中,它将意味着Bar.prototype
-对象(使用new Foo(42)
创建)将假装是由Bar
创建的,而不是Foo
。我想这是一种类似于C++/Java/C#类语言的想法,在这种语言中,类型查找(构造函数属性)总是产生最具体的类型,而不是原型链中更高级的更通用对象的类型
我的建议是:不要过多考虑JavaScript中的“继承”。接口和混合的概念更有意义。不要检查对象的类型。检查所需的属性(“如果它走路像鸭子,嘎嘎叫像鸭子,那它就是鸭子”)
试图强迫JavaScript进入一个经典的继承模型,而它所拥有的只是上述的原型机制,这是造成混淆的原因。许多建议手动设置构造函数
-属性的人可能正是这样做的。抽象是很好的,但是构造函数属性的手动赋值不是JavaScript的惯用用法。使用构造函数的一种情况:
这是继承的常见实现之一:
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;
...
};
这个new f()
不会调用超类的构造函数,所以在创建子类时,可能需要先调用超类,如下所示:
SubClass = function() {
SubClass.superClass.constructor.call(this);
};
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'
因此构造函数属性在这里有意义。构造函数属性指向用于创建对象实例的构造函数。如果您键入“new Bar()”,它将是“Bar”,而您键入“new Foo()”,它将是“Foo”
但是,如果您在不设置构造函数的情况下设置原型,则会得到如下结果:
SubClass = function() {
SubClass.superClass.constructor.call(this);
};
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'
要将构造函数实际设置为用于创建对象的构造函数,我们还需要在设置prototype时设置构造函数,如下所示:
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'
当您希望prototype.constructor
属性在prototype
属性重新分配后继续存在时,其中一个用例是在prototype
上定义一个方法,该方法生成与给定实例相同类型的新实例。例子: