约翰·雷西格错了
是的,jQuery 的作者也会犯错。他是这样说的:
JavaScript 中的每个对象都有一个名为的隐式属性,该属性constructor
引用用于创建对象的构造函数。而且因为原型是构造函数的一个属性,所以每个对象都有办法找到它的原型。
以下是他的说法是错误的原因:
- 不是每个对象都有原型。因此,这些对象也没有任何隐式属性。
- 并非每个具有原型的对象都是由构造函数创建的。然而,这不包括对象、数组和正则表达式文字和函数,因为这些对象是由构造函数隐式创建的。
- 并非每个具有原型并由构造函数创建的对象都具有名为 的隐式属性
constructor
。
- 并非每个具有原型、由构造函数创建并具有名为的隐式属性
constructor
的对象都指向创建该对象的构造函数。
- 并非每个具有原型、由构造函数创建并具有
constructor
指向创建该对象的构造函数的隐式属性的对象,都具有prototype
在其构造函数上调用的属性。
- 并非每个具有原型的对象都由构造函数创建,具有名为的隐式属性,该属性
constructor
指向创建该对象的构造函数,并且具有prototype
在其构造函数上调用的属性,并且该属性指向该对象的原型。
让我们通过例子来证明这些陈述,以证明 John Resig 的陈述是错误的。由于所有这些陈述都以断言“不是每个对象”开头,我们只需要找到每个陈述的一个例子来证明 John Resig 的陈述是错误的。
声明 1 的证明
该Object.create
方法可用于创建新对象并设置其内部[[prototype]]
属性。因此它可以用来创建一个没有原型的对象:
var o = Object.create(null); // o has no prototype
上例中的对象没有原型 - 它的内部[[prototype]]
属性设置为null
. 因此它也没有任何隐式属性。
陈述 2 的证明
现在让我们创建另一个p
继承自 object 的对象o
,如下所示:
var p = Object.create(o); // the prototype of p is o
因此该对象p
有一个原型,但它不是由构造函数创建的。
陈述 3 的证明
好吧,让我们p
从构造函数创建对象(实际上这正是Object.create
函数的实现方式):
function F() {} // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F; // p is an object which is constructed by F
这里的对象p
是由构造函数创建的F
。但是它没有任何名为constructor
.
陈述 4 的证明
如果变量o
被分配了一个对象字面量然后用作prototype
构造函数的属性F
怎么办?
var o = {}; // object literals inherit from Object.prototype
function F() {} // F is a constructor
F.prototype = o; // objects constructed by F inherit from o
var p = new F; // p is an object which is constructed by F
现在该对象p
有一个名为的隐式属性constructor
,但它指向Object
而不是F
. 因此p.constructor.prototype
指向Object.prototype
而不是o
。
陈述 5 的证明
也许您认为问题在于继承?好吧,让我们完全取消继承。从头开始:
var p = new F; // p is constructed by F, it inherits from F.prototype
delete F.prototype; // devious isn't it? I love being naughty
function F() {} // declarations are hoisted
好吧,现在对象p
继承自F.prototype
,并且它有一个名为的隐式属性constructor
,它指向F
它自己。但是,由于我们删除了该prototype
属性,F
我们无法访问p
via的原型p.constructor.prototype
(它现在将返回undefined
)。
陈述 6 的证明
让我们稍微修改一下最后一个例子。我们不会删除F.prototype
,而是将其设置为其他内容。例如:
var o = {}; // object literals inherit from Object.prototype
var p = new F; // p is constructed by F, it inherits from F.prototype
F.prototype = o; // oops, what will happen now?
function F() {} // declarations are hoisted
现在该对象p
继承自F.prototype
,并且它有一个名为的隐式属性constructor
,该属性指向F
它自己。但是,由于我们设置F.prototype
为何o
时访问p.constructor.prototype
,我们将获得o
而不是原始的F.prototype
.
结论
如您所见,John Resig 的说法是完全错误的。我们不需要 6 个例子来证明这一点。任何一个例子都足够了。但是,我想表明他的说法是多么错误。因此,我写了我能想到的所有可能的例子来反驳他的说法。
JavaScript 是一种原型面向对象的编程语言,这意味着对象继承自其他对象。构造函数不是严格要求创建对象的。然而,它们被赋予了过度的重要性,因为不幸的是,这就是原型继承在 JavaScript 中的工作方式。
原型继承的构造函数模式经常令人困惑和误导。此外,它隐藏了不使用构造函数的原型继承的真实方式(原型继承的原型模式)。要了解更多信息,请阅读以下答案:https ://stackoverflow.com/a/17008403/783743