【问题标题】:The prototype property and inheritance in JavaScriptJavaScript 中的原型属性和继承
【发布时间】:2015-11-03 18:54:07
【问题描述】:

我不清楚 JavaScript 中的原型属性和继承。

我有一个函数:

function User (name) {
    this.name = name;
}

问题:

1) 为什么以下一项为假,另一项为真?

User.prototype.hasOwnProperty('name'); // false
User.hasOwnProperty('name'); // true

2) 以下有什么区别:

User.constructor;
User.prototype.constructor;

3) 如果我像这样覆盖原型属性,User.constructor 和 User.prototype.constructor 会发生什么:

User.prototype = {
    changeName: function(newName) {
        this.name = newName;
    }
};

如果我这样覆盖它会怎样:

User.prototype = {
    constructor: User,
    changeName: function(newName) {
        this.name = newName;
    }
};

4) User 是函数还是原型还是什么?以下站点将其称为原型:'以下示例创建原型......' https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor

非常感谢您的回答。

【问题讨论】:

标签: javascript prototype


【解决方案1】:

首先您需要了解原型是什么。

在 Javascript 中,几乎一切都是对象。


编辑(感谢@Mathletics):

原始布尔值、数字和字符串不是对象


当您在 Javascript 中创建对象时,默认情况下,其原型将是 Object

所以,如果我这样做:

var obj = {};
console.log(obj.__proto__)

这会产生:

对象{}


暂停:

要了解obj.prototypeobj.__proto__ 之间的区别,请参阅此thread


接下来要了解prototype chaining的概念。

这就是 Javascript 中继承的结构。基本上,原型是另一个对象的“父对象”,它从中派生出方法和属性。

想象一个Rabbit 继承自Mammal 的方案,Mammal 继承自Animal。在 Javascript 原型链中表示它就像:

(Rabbit) -> (Mammal) -> (Animal) -> (Object) -> null

带代码:

function Animal(){}
function Mammal(){}
function Rabbit(){}

Mammal.prototype = new Animal();
Rabit.prototype = new Mammal();

如果您能够理解这些概念,您可能会自己找到问题的答案,但我会尽量说得更清楚:

1)为什么以下一个是假的,另一个是真的?

User.prototype.hasOwnProperty('name'); // false
User.hasOwnProperty('name'); // true

你应该阅读hasOwnProperty method reference

从 Object 派生的每个对象都继承了 hasOwnProperty 方法。该方法可用于确定一个对象是否具有指定属性作为该对象的直接属性[...]

这是一种确保您要查找的属性是在对象本身中定义的方法,而不是在原型链的更深处。

一个相反的例子是:

console.log(User.hasOwnProperty('constructor'));

由于constructor 属性在原型链中定义得更深,它不是User 对象的“自己的属性”。

在您的情况下,name 是在您的 User 对象中定义的属性,但未在全局 Object 的上下文中定义。

2) 以下有什么区别:

User.constructor;
User.prototype.constructor;

第一个like取User的构造函数,第二个取其原型的构造函数(在本例中为全局Object)。

3) User.constructor 和 User.prototype.constructor 如果我覆盖原型属性,例如 这个:

User.prototype = {
    changeName: function(newName) {
        this.name = newName;
    }
};

请阅读此thread

TL;DR:当你直接使用原型时,你正在打破原型链并创建一个新的。在Rabbit 示例中,如果您在某个时候这样做了:

Rabbit.prototype = {
   // ...
};

您的新原型链将是:

(Rabbit) -> (Object) -> null

因此,在您的第一个示例中,User.prototype.constructor 会产生:

函数对象{}

在你的第二个例子中,它会产生:

函数用户(名称)

知道了吗?

4) User 是函数还是原型还是什么?

User 是一个对象,其原型为Function 对象,其原型为Object 对象。

画出来:

(User) -> (Function) -> (Object) -> null

希望我已经说清楚了。

【讨论】:

  • 如果您要深入研究,请务必注意 primitive 布尔值、数字和字符串是 not 对象。它们每个都有对象包装器,但默认情况下它们不是对象。
  • 这就是我错过的:'基本上,原型......'。但是有些东西我还是不明白:console.log(User.prototype); // User { } // 但这应该是 Object { } 和 console.log(User.prototype.constructor); // User(name) 但这应该类似于 Object(),因为你在上面解释了“父”的东西。与您的示例 console.log(Mammal.prototype) 完美配合且易于理解; // Animal { } 给出了我期望的结果。
【解决方案2】:

JS 中的“Everything”是一个对象。

因此,函数是一种特殊类型的对象,并且该类型的对象具有称为name 的属性,这就是User.hasOwnProperty("name") 返回true 的原因。它与同一函数可以创建的 User 对象的属性无关。尝试将其称为其他名称,您会发现情况确实如此。

任何对象都派生自另一个对象,即它的原型。函数的原型具有constructor 属性和__proto__ 属性,但没有name 属性。 所以 Ùser.prototype.hasOwnProperty("name")` 应该返回 false

由于原型的所有属性都是派生的,即使User.hasOwnProperty("constructor") 会返回 false,您也可以这样做 User.constructor

【讨论】:

    【解决方案3】:

    1)为什么以下一个是假的,另一个是真的?

    User.prototype.hasOwnProperty('name'); // false, A
    User.hasOwnProperty('name'); // true, B
    
    • 原型对象没有被赋予 name 属性,并且默认情况下它没有属性(因为 User 不是本机的),所以这是@987654322 @ 代表原型。
    • 构造函数User 是一个函数,您已将函数的名称定义为"User"。这是在解释函数对象时设置的。

    您确定不会将其与 (new User('foo')).hasOwnProperty('name'); // true 混淆吗?

    最后一种情况,Userinstance 有一个 name 属性,它是 "foo",由构造函数 设置用户


    2) 以下有什么区别:

    User.constructor;
    User.prototype.constructor;
    
    • User.constructorFunction,因为 User 是一个 函数
    • User.prototype.constructor未定义,但您通常会在 User 引用它。这就是后来作为(new User).constructor; // User 访问的内容

    3) 如果我覆盖原型,User.constructor 和 User.prototype.constructor 会发生什么[...]

    在这两种情况下,User.constructor 都没有发生任何事情。

    User.prototype.constructor之前没有定义

    • 所以在第一种情况下仍然是 undefined
    • 在第二种情况下设置为User

    请注意,在这两种情况下,整个 prototype 现在都指向不同的 Object,因此现有实例将看不到这些更改,并且之前的原型链没有新实例可以更长时间地访问。


    4) User 是函数还是原型还是什么?

    User 是一个函数,也是一个构造函数。在 JavaScript 中,如果某些函数将用于构建 (construct) 实例,我们将它们称为 constructors


    看一个更抽象的例子可能会对你有所帮助,

    // A function `Foo` which will be our Constructor
    function Foo(baz) {
        this.bar = baz;
    }
    
    // notice here we have `Foo.name; // "Foo"`
    
    // Setting up reference to the constructor through the prototype
    Foo.prototype.constructor = Foo;
    // Setting up reference to some other shared property through the prototype
    Foo.prototype.fizz = ['buzz'];
    

    然后是用法

    var foo = new Foo('hello world');
    foo.bar; // "hello world" (own)
    foo.fizz; // ["buzz"] (inherited)
    foo.constructor; // Foo (inherited)
    
    var bar = new Foo();
    foo.fizz === bar.fizz; // true, this is the same Object reference - it is shared
    

    不要害怕在您的控制台中尝试一下!

    【讨论】:

    • User.prototype.constructorUser,至少在 Chrome 46 中。
    【解决方案4】:

    Javascript,不像其他语言(Java,ecc。),不是基于类的,它不提供任何方式来定义一个 (实际上,下一个Javascript, ES6-Harmony,引入了 class 关键字,但它只是原型继承之上的语法糖,目前在任何浏览器中都不可用)...

    因此,Javascript 通过原型继承实现(又名)OOP(面向对象编程)。 如果你想创建一个自定义对象,你需要使用一个定义它的函数。示例:

    var Person = (function() {
      /**
       * this is the Constructor (if you have in mind a class-based language)
      **/
      function Person() {}
      
      return Person;
    })(window);

    为此,我们定义了一个New Object Person,并且我们可以使用new 运算符来拥有它的多个实例:

    var me = new Person();
    var you = new Person();
    //...

    和所有语言一样,javascript对象可以有方法属性,方法和属性可以是InstanceClass

    var Person = (function() {
      function Person() {}
          
      /**
       * Static Method
      **/
      Person.sayCiao = function() {};   
          
      /**
       * Static Property
      **/
      Person.ciao = 'Ciao';  
      
      
    
      /**
       * Instance Method
      **/
      Person.prototype.sayHello = function() {};  
    
      /**
       * Instance Property
      **/
      Person.prototype.hello = 'Hello';
      
      
      return Person;
    })(window);

    如您所见(在您的控制台中尝试),使用 prototype 属性,我们可以定义所有通过调用 new Person 创建的对象继承的方法或属性。

    1)为什么以下一个是假的,另一个是真的?

    当然,静态方法或属性不属于实例,hasOwnProperty 只有在使用构造函数调用时才返回 true。反之亦然,实例方法或属性仅属于它们的实例,hasOwnProperty 仅在使用 new 运算符 调用时返回 true。这是您在其他编程语言中可以遇到的正常行为。

    注意:in ES6 the Function Object has a property name 这就是您的测试返回 true 的原因,但没有引用您在 Person 构造函数 (read more) 中定义的 name 属性。

    1. 使用示例:

    var Person = (function() {
      function Person(name) {
        this.firstname = name
      }
      
      Person.prototype.firstname = '';
      
      Person.factory = function(name) {
        return new Person(name);
      };
      
      return Person;
    })(window);
    
    var hitmands = Person.factory('Giuseppe');
    console.log('you are', hitmands);
    
    var you = Person.factory('BatMan');
    console.log('hitmands is', you);
    
    console.info('Person has a name?', Person.hasOwnProperty('firstname'));
    console.info('You have a name?', you.hasOwnProperty('firstname'));

    属性prototype属于原型链,这就是你的测试返回false的原因...({}).hasOwnProperty('prototype')...You can read more following this link。 只需prototype,它所包含的并不是您的对象的 own 属性。

    2) 以下内容有什么区别(ecc.)

    正如有人所说,一个属于函数,另一个属于原型属性。

    3) 如果我像这样重写原型属性,User.constructor 和 User.prototype.constructor 会发生什么 (ecc.)

    您可以使用这种方式将行为从一个类扩展到另一个类... Read more about this

    【讨论】:

      猜你喜欢
      • 2023-03-16
      • 1970-01-01
      • 2013-03-05
      • 2015-01-17
      • 2016-02-15
      • 2019-11-18
      • 2013-07-17
      • 1970-01-01
      • 2018-05-25
      相关资源
      最近更新 更多