【问题标题】:What is "The" prototype?什么是“The”原型?
【发布时间】:2019-10-16 06:12:20
【问题描述】:

我正试图围绕原型展开思考,并想知道 原型到底是什么。许多困惑源于不理解用于描述的元语言 原型。

这是我所知道的:

当我们创建一个带有属性的命名构造函数时,该构造函数体内的属性被该构造函数创建的对象实例继承。在这里,我从名为 Person 的构造函数创建了一个名为 person001 的实例。

function Person(firstName,lastName) {
    this.firstName = firstName;
    this.lastName = lastName
}
undefined
var person001 = new Person("John","Doe");

当我在控制台中查看对象实例并跟踪原型链时,我发现它位于 2 个不同的地方。它是 dunder proto 对象的构造函数对象...

Person {firstName: "John", lastName: "Doe"}
firstName: "John"
lastName: "Doe"
__proto__:
constructor: ƒ Person(firstName,lastName)
__proto__: Object

以及同一个构造函数对象内部的原型对象的属性。

Person {firstName: "John", lastName: "Doe"}
firstName: "John"
lastName: "Doe"
__proto__:
constructor: ƒ Person(firstName,lastName)
arguments: null
caller: null
length: 2
name: "Person"
prototype: 
constructor: ƒ Person(firstName,lastName)
__proto__: Object
__proto__: ƒ ()
[[FunctionLocation]]: script.js:76
[[Scopes]]: Scopes[1]
__proto__: Object

当我使用命名构造函数的 .prototype 属性添加属性时,我将该属性添加到原型对象,而不是构造函数。添加的属性将位于原型属性对象中的构造函数旁边。这里我使用构造函数 Person 的原型属性添加了一个名为 age 的属性。

Person.prototype.age = 0;  

既然我已经添加了一个额外的属性,那么原型到底是什么?

当我在对象实例 person001 上运行 Object.getPrototypeOf 方法时,它会返回在我看来类似于原型对象的内容。它有 3 个属性——一个构造函数、我添加的属性和隐式 dunder proto 对象。

Object.getPrototypeOf(person001);
{age: 0, constructor: ƒ}
age: 0
constructor: ƒ Person(firstName,lastName)
__proto__: Object 

那么原型是什么?是原型对象{构造函数,附加属性}吗?还是只是原型对象的构造函数?

提前感谢您的帮助。

【问题讨论】:

标签: javascript oop prototype prototype-chain


【解决方案1】:

当你做obj = new Person时,游戏中有三个玩家:

  • 新创建的对象obj
  • 构造函数Person
  • 原型,存储在Person.prototype下的特殊隐藏对象

它们之间的关系如下:

obj.__proto__ === Person.prototype

Person.prototype.constructor === Person

插图:

function Person(firstName,lastName) {
    this.firstName = firstName;
    this.lastName = lastName
}

var person1 = new Person("John","Doe");
var person2 = new Person("Ann","Smith");

Person.prototype.age = 42;

【讨论】:

  • 这是一个绝妙的插图。所以参考插图,现在我的理解是,在这种情况下,“The”原型是 Person 对象和属于它的所有属性,包括构造函数对象。使用 Person.prototype.age 向 Person 对象添加属性时,该属性会保存为构造函数原型对象中的对象,并设置为 Person 对象中的属性。
  • Georg,你介意告诉我你用什么应用程序来创建图形吗?
  • 把它放在我的书签顶部。绝对的天才。谢谢,乔治!
【解决方案2】:

说你已经为 Person 创建了一个构造函数,然后是它的两个实例:

const Person = function(name) {
  this.name = name;
  this.speak = () => console.log('My name is ' + this.name)
};
const john = new Person('John');
const mary = new Person('Mary');

john.speak();
mary.speak();

现在您创建的每个人都有一个不同的名字,他们都可以说这个名字,因为他们共享来自某个父对象的相同属性。

但玛丽不仅会说话。她可以唱歌。但约翰不能。

        const Person = function(name) {
          this.name = name;
          this.speak = () => console.log('My name is ' + this.name)
        };
        const john = new Person('John');
        const mary = new Person('Mary');

        john.speak();
        mary.speak();
        
        mary.sing = () => console.log('♪♪♪ Lalalalalala ♪♪♪');
        
        mary.sing();
        john.sing(); // John is such a bad singer, that this throws an error !

如果后来你意识到你的人不仅需要说话,还需要走路。您需要参考他们共同的父母,以便告诉他们走在一条线上。这就是原型。 Mary 和 John 都有一个共同的原型,并且在内心深处都有对该原型的引用(即__proto__,供朋友和家人使用)。

const Person = function(name) {
      this.name = name;
      this.speak = () => console.log('My name is ' + this.name)
    };
    const john = new Person('John');
    const mary = new Person('Mary');

    john.speak();
    mary.speak();
    
    Person.prototype.walk = () => console.log('I am walking alright');
    
    john.walk();
    mary.walk();
    
    // That is the same as:
    john.__proto__.walk()
    mary.__proto__.walk()

现在约翰摔得很厉害,走路也很困难

    const Person = function(name) {
          this.name = name;
          this.speak = () => console.log('My name is ' + this.name)
        };
        const john = new Person('John');
        const mary = new Person('Mary');

        john.speak();
        mary.speak();
        
        Person.prototype.walk = () => console.log('I am walking alright');
        
        // John's infamous accident
        john.walk = () => console.log('My leg hurts so bad...');
        
        john.walk();
        mary.walk();
        
        

实例有它自己的属性,我们使用它。
它没有,我们查看它的__proto__,如果它存在就使用它。

希望这会有所帮助!

【讨论】:

  • 谢谢 Giuseppe,这有帮助。
【解决方案3】:

编辑:我的朋友@teemu 添加到我的答案中:每个内置对象都有它的原型。

在非常简单的语言中,每个函数都是 javascript 中的一种特殊对象,每个函数都有自己的“原型”对象容器。 所以每个构造函数都会有自己的“原型”对象,它将与使用 this 构造的__proto__ 中的所有对象共享。 让我们看看例子:

// constructor function:
    var Person = function(name, age){
      this.name = name;
      this.age = age
    }

var tony = new Person('tony', 21);
var bruce = new Person('bruce', 22);

正如我们所讨论的,

Person.prototype 应与tony.__proto__bruce.__proto__ 相同

注意:您也可以将 Person 替换为内置的 Array、Object 或 String。

为了验证这一点,我们可以这样做:

Person.prototype == tony.__proto__;  //true
Person.prototype == bruce.__proto__; //true
bruce.__proto__ == tony.__proto__;  //true

接下来我们要在bruce.__proto__ 上添加一个属性:

bruce.__proto__.isSuperHero = true;

prototype中添加属性也是一样的

但这会在各处反映出来;

console.log(tony.__proto__.isSuperHero )  // true
console.log(Person.prototype.isSuperHero)   //true

现在您可以将原型想象成一个公共空间,就像所有家庭成员的房子一样房子,无论他多大或他只是一个新生儿。每个家庭成员都一样,楼梯一样,房间一样,墙壁颜色一样。

我希望这将有助于您理解原型链,我试图以不同的方式来解释它,这在我看来是更好的方式。

【讨论】:

  • 不仅仅是函数,JS中的每个对象都有原型(除了那些没有它专门创建的对象)。
【解决方案4】:

首先,原型只是一个对象。几乎 JS 中的每个对象(例如,当您使用 Object.create(null) 时除外)都有一些原型,并且这些原型可以链接起来。

示例:当您使用文字 [] 创建数组时,您的数组实例连接到对象(顺便说一下数组实例)。定义数组的属性,如 map 等,它本身连接到另一个原型(对象instance) 定义对象的属性,例如toString。这些属性通常是函数。现在,当您访问对象的属性之一(例如[].hasOwnProperty)时,引擎会查找原型链以找到它。它从您的 [] 实例开始,没有找到它,继续到原型(数组),也没有成功,所以移动到最后找到它的最后一个原型(对象实例)。

如您所见,原型链总是在某处结束,因此如果您尝试在链中的最后一个原型上检索原型,您将得到null

现在回到构造函数。首先要注意的是,这些只是普通函数——实例的实际“创建者”是关键字new。现在每个函数都有一个prototype 属性,它告诉您:使用此函数创建的每个对象都将原型连接到函数的prototype 属性中的任何内容。默认情况下,每个函数都在此属性中包含 Object 的实例,这意味着您从此函数创建的每个对象都将原型连接到此实例,因此将“继承”诸如toString 之类的属性。

你可以在这个例子中看到函数的prototype属性和实例的原型之间的联系。

function A() {}
var a = new A();
a.__proto__ == A.prototype; // is true

最后,函数prototype 属性中的constructor 属性告诉您,当使用带有new 关键字的函数时,哪个函数将用于创建新实例。这满口归结为:

function A() {}
A == A.prototype.constructor; // is true

它是对自身的引用。在 ES6 之前创建您自己的继承链时,此属性是可选的,但为了正确起见还是这样做了。

那么什么是原型?它只是一个对象,通过特殊的原型连接连接到其他对象实例,使其能够访问预定义的属性。这是在 JS 中进行继承的一种方式。

【讨论】:

  • 不,它没有,考虑这个Object.hasOwnProperty([], hasOwnProperty)返回false,但[].hasOwnProperty给你函数。
  • 啊,是的,但是你的例子没有调用.hasOwnProperty。删除评论。
  • 我们在谈论苹果和橘子。现在我看到你在评论 fn 调用 - 是的,它显然只检查实例。但是关于属性访问,它会上升到原型链。
  • 谢谢你,丹。这很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-18
  • 1970-01-01
  • 1970-01-01
  • 2012-01-16
  • 2019-06-05
相关资源
最近更新 更多