在第125页的Secrets of the JavaScript Ninja, 2013一书中,它说:
JavaScript中的每个对象都有一个名为
constructor
的隐式属性 它引用了用于创建对象的构造函数。 而且因为原型是属性的 构造函数,每个对象都有办法找到它的原型。
它实际上可能是我听到的关于JavaScript的最有缺陷的事情之一,它来自一个所谓的JavaScript专家。
是不是真的任何JavaScript对象“都可以使用内部[[prototype]]
属性查找其原型”(如ECMA-262 specs,第32页)。可以使用__proto__
在Chrome和Firefox上访问它,在最近的IE版本中,使用Object.getPrototypeOf
任何对象都可以通过constructor
指向的原型对象获取__proto__
属性。 constructor
属性有时甚至没有正确设置,因为某些JavaScript库或框架根本不使用它。 constructor
是原型对象的属性,而不是对象本身的属性:
(如Chrome的开发者工具中所示):
> function Foo() {}
undefined
> var foo = new Foo()
undefined
> foo.hasOwnProperty("constructor")
false
> foo.__proto__.hasOwnProperty("constructor")
true
> foo.__proto__.constructor === Foo
true
以上(1)和(2)是真的吗?什么是JavaScript中的“constructor
隐式属性”,如引用文本中所示?它是否试图表示[[prototype]]
这样的内部属性?但更重要的是,我想知道上面的(1)和(2)是否属实,而不是引用的文本是什么。
答案 0 :(得分:2)
引用的文字非常准确,非常简单地解释了这个机制。
“JavaScript中的每个对象都有一个名为constructor的隐式属性,它引用了用于创建对象的构造函数。”
这是绝对正确的,正如@Mathletics所指出的那样:
foo.constructor === Foo // true
...“因为原型是构造函数的一个属性,每个对象都有办法找到它的原型。”
这也可以清楚地理解为阅读。 从构造函数中获取原型是实例查找其原型的有效方法。
foo.constructor.prototype // Foo {}
还
foo.constructor.prototype === foo.__proto__ // true
我认为这本书描述的方式是最合适的。 出于某种原因,“ __ proto __ ”属性在每一侧都以双下划线命名。正如您所指出的,它是一个内部属性,双下划线是一种广泛使用的命名内部属性的约定。它与 hasOwnProperty 不是“可见的”。不是特别因为它是内部属性,而是因为它没有在对象本身上设置直接。这可能更好地解释 hasOwnPropery 意味着更清楚:
foo.a = 4;
foo.a; // 4
foo.hasOwnProperty("a"); // true
foo.constructor.prototype.b = 5;
foo.b; // 5
foo.hasOwnProperty("b"); // false
答案 1 :(得分:1)
是的,jQuery的作者也会犯错误。这就是他所说的:
JavaScript中的每个对象都有一个名为
constructor
的隐式属性,它引用用于创建对象的构造函数。并且因为原型是构造函数的属性,所以每个对象都有一种方法可以找到它的原型。
以下是他的陈述是错误的原因:
constructor
的隐式属性。constructor
的隐式属性,该属性指向创建该对象的构造函数。constructor
的隐式属性,该属性指向创建该对象的构造函数,在其构造函数上有一个名为prototype
的属性功能constructor
的隐式属性,它指向创建该对象的构造函数,并在其构造函数上有一个名为prototype
的属性。 function,具有该属性指向该对象的原型。让我们通过例子来证明这些陈述,以证明John Resig的陈述是错误的。由于所有这些语句都以断言“并非每个对象”开头,我们只需要找到每个语句的一个例子来证明John Resig的陈述是错误的。
Object.create
方法可用于创建新对象并设置其内部[[prototype]]
属性。因此,它可以用于创建一个没有原型的对象:
var o = Object.create(null); // o has no prototype
上例中的对象没有原型 - 它的内部[[prototype]]
属性设置为null
。因此它也没有任何隐式属性。
现在让我们创建另一个对象p
,它继承自对象o
,如下所示:
var p = Object.create(o); // the prototype of p is o
因此,对象p
有一个原型,但它不是由构造函数创建的。
好吧让我们从构造函数创建对象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
的隐式属性。
如果为变量o
分配了一个对象文字,然后将其用作构造函数prototype
的{{1}}属性,该怎么办?
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
好吧,现在对象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
的原型(它现在将返回p.constructor.prototype
)。
让我们稍微修改最后一个例子。我们不会删除undefined
,而是将其设置为其他内容。例如:
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
。
正如你所看到的,John Resig的陈述是完全错误的。我们不需要6个例子来证明这一点。任何一个例子就足够了。但是我想表明他的陈述是多么错误。因此,我写下了我能想到的每一个可能的例子,反驳了他的陈述。
JavaScript是一种原型面向对象的编程语言,这意味着对象继承自其他对象。构造函数不是严格要求创建对象的。然而,它们被赋予了不正当的重要性,因为不幸的是,这是原型继承在JavaScript中的工作方式。
原型继承的构造函数模式经常令人困惑和误导。此外,它隐藏了原型继承的真正方式,它不使用构造函数(原型继承的原型模式)。要了解更多信息,请阅读以下答案:https://stackoverflow.com/a/17008403/783743
答案 2 :(得分:0)
每个对象都有__proto__
属性,每个函数都有一个额外的prototype
属性。 prototype
是一个对象本身,并且有一个名为constructor
的属性,指向原始函数:
function C(){}
console.log(C.hasOwnProperty("prototype")); //true
console.log(C.prototype.hasOwnProperty("constructor")); //true
console.log(C.prototype.constructor === C); //true
创建对象时,它的__proto__
属性设置为指向其构造函数的prototype
属性:
var c = new C();
console.log(c.__proto__ === C.prototype) //true
有关c
是什么的所有信息,是否知道其__proto__
属性。只需手动更改__proto__
属性即可观察到:
function D(){}
c.__proto__ = D.prototype;
console.log(c.constructor === D) //true
console.log(c instanceof D) //true
因此,考虑到这一切,很难相信存在一个构造函数属性,从中可以得到所有东西。但是仍然可能存在内部构造函数属性(即__constr__
),当请求原型时,我们无法访问但访问的属性。至少我猜是可能的。
有一件事也让我感到困惑,是:
console.log(c.hasOwnProperty("__proto__")) //false
我的猜测是__proto__
只是没有被hasOwnProperty
方法抓住,但也许其他人可以对此有所了解。
<强> FIDDLE 强>
以下是ECMA Spec的一些摘录:
所有对象都有一个名为[[Prototype]]的内部属性。价值 此属性的值为null或对象,用于 实现继承。本机对象是否可以拥有 host对象作为[[Prototype]]取决于实现。一切 [[Prototype]]链必须具有有限长度(即从...开始) 任何对象,递归访问[[Prototype]]内部属性 必须最终导致空值)。命名数据属性 [[Prototype]]对象是继承的(作为属性可见 child object)用于获取访问权限,但不用于put访问权限。 为get access和put继承了命名的访问器属性 访问
和
8.12.2 [[GetProperty]](P)
当使用属性名称P调用O的[[GetProperty]]内部方法时,将执行以下步骤:
让prop成为调用属性名为P的O的[[GetOwnProperty]]内部方法的结果。
如果prop未定义,请返回prop。
让proto成为O的[[Prototype]]内部属性的值。
如果proto为null,则返回undefined。
- 醇>
返回用参数P调用proto的[[GetProperty]]内部方法的结果。
ECMA明确指出,有一个内部原型属性,如果JavaScript仍然首先访问某些内部构造函数属性,访问已经存在的内容,那么它们就没有意义。 此外,可以测试的所有内容都指向构造函数是原型属性的想法,而不是相反。
所以我想可以说,这就是它的工作方式。
我还重读了Jon Resig的引文。我认为他对the prototype is a property of the constructor
的意思是我们可以直接访问的原型属性,并且每个函数都有。他在这里也不是特别具体。我猜他只是想要一个简单的解释,并且不想让人们立刻迷惑。