JavaScript对象使用构造函数找到它的原型?

时间:2013-06-21 13:39:10

标签: javascript prototype

在第125页的Secrets of the JavaScript Ninja, 2013一书中,它说:

  

JavaScript中的每个对象都有一个名为constructor的隐式属性   它引用了用于创建对象的构造函数。   而且因为原型是属性的   构造函数,每个对象都有办法找到它的原型。

它实际上可能是我听到的关于JavaScript的最有缺陷的事情之一,它来自一个所谓的JavaScript专家。

是不是真的
  1. 任何JavaScript对象“都可以使用内部[[prototype]]属性查找其原型”(如ECMA-262 specs,第32页)。可以使用__proto__在Chrome和Firefox上访问它,在最近的IE版本中,使用Object.getPrototypeOf

  2. 任何对象都可以通过constructor指向的原型对象获取__proto__属性。 constructor属性有时甚至没有正确设置,因为某些JavaScript库或框架根本不使用它。 constructor是原型对象的属性,而不是对象本身的属性:

  3. (如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)是否属实,而不是引用的文本是什么。

3 个答案:

答案 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)

John Resig错了

是的,jQuery的作者也会犯错误。这就是他所说的:

  

JavaScript中的每个对象都有一个名为constructor的隐式属性,它引用用于创建对象的构造函数。并且因为原型是构造函数的属性,所以每个对象都有一种方法可以找到它的原型。

以下是他的陈述是错误的原因:

  1. 并非每个对象都有原型。因此,这些对象也没有任何隐式属性。
  2. 并非每个具有原型的对象都是由构造函数创建的。但是,这不包括对象,数组和正则表达式文字和函数,因为这些对象是由构造函数隐式创建的。
  3. 并非每个具有原型且由构造函数创建的对象都具有名为constructor的隐式属性。
  4. 并非每个拥有原型的对象都是由构造函数创建的,并且具有名为constructor的隐式属性,该属性指向创建该对象的构造函数。
  5. 并非每个拥有原型的对象都是由构造函数创建的,并且具有一个名为constructor的隐式属性,该属性指向创建该对象的构造函数,在其构造函数上有一个名为prototype的属性功能
  6. 并非每个拥有原型的对象都是由构造函数创建的,它有一个名为constructor的隐式属性,它指向创建该对象的构造函数,并在其构造函数上有一个名为prototype的属性。 function,具有该属性指向该对象的原型。
  7. 让我们通过例子来证明这些陈述,以证明John Resig的陈述是错误的。由于所有这些语句都以断言“并非每个对象”开头,我们只需要找到每个语句的一个例子来证明John Resig的陈述是错误的。

    声明1的证据

    Object.create方法可用于创建新对象并设置其内部[[prototype]]属性。因此,它可以用于创建一个没有原型的对象:

    var o = Object.create(null); // o has no prototype
    

    上例中的对象没有原型 - 它的内部[[prototype]]属性设置为null。因此它也没有任何隐式属性

    声明2的证据

    现在让我们创建另一个对象p,它继承自对象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的{​​{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

    声明5的证据

    也许您认为问题是继承?好吧,让我们完全废除继承。从头开始:

    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)。

    声明6的证据

    让我们稍微修改最后一个例子。我们不会删除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说什么

以下是ECMA Spec的一些摘录:

  

所有对象都有一个名为[[Prototype]]的内部属性。价值   此属性的值为null或对象,用于   实现继承。本机对象是否可以拥有   host对象作为[[Prototype]]取决于实现。一切   [[Prototype]]链必须具有有限长度(即从...开始)   任何对象,递归访问[[Prototype]]内部属性   必须最终导致空值)。命名数据属性   [[Prototype]]对象是继承的(作为属性可见   child object)用于获取访问权限,但不用于put访问权限。   为get access和put继承了命名的访问器属性   访问

  

8.12.2 [[GetProperty]](P)

     

当使用属性名称P调用O的[[GetProperty]]内部方法时,将执行以下步骤:

     
      
  1. 让prop成为调用属性名为P的O的[[GetOwnProperty]]内部方法的结果。

  2.   
  3. 如果prop未定义,请返回prop。

  4.   
  5. 让proto成为O的[[Prototype]]内部属性的值。

  6.   
  7. 如果proto为null,则返回undefined。

  8.   
  9. 返回用参数P调用proto的[[GetProperty]]内部方法的结果。

  10.   

我的结论

ECMA明确指出,有一个内部原型属性,如果JavaScript仍然首先访问某些内部构造函数属性,访问已经存在的内容,那么它们就没有意义。 此外,可以测试的所有内容都指向构造函数是原型属性的想法,而不是相反。

所以我想可以说,这就是它的工作方式。

我还重读了Jon Resig的引文。我认为他对the prototype is a property of the constructor的意思是我们可以直接访问的原型属性,并且每个函数都有。他在这里也不是特别具体。我猜他只是想要一个简单的解释,并且不想让人们立刻迷惑。