什么是 "The" 原型?

What is "The" prototype?

我正在努力研究原型,很想知道 原型到底是什么。许多混淆源于不理解用于描述的元语言 原型。

这是我所知道的:

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

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

当我查看控制台中的对象实例并跟踪原型链时,我在两个不同的地方找到了它。它是 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 方法时,它 returns 在我看来像原型对象。它有 3 个属性——构造函数、我添加的 属性 和隐式 dunder proto 对象。

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

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

在此先感谢您的帮助。

说你已经为一个 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();

现在您创建的每个人都有不同的名字,而且他们都可以说出来,因为他们共享相同的 属性 来自某些 parent object.

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

        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 !

如果后来您意识到您的员工不仅需要说话,还需要走路,那该多好啊。您需要引用他们的公共 parent 以告诉他们全部走在一条线上。这就是原型。玛丽和约翰都有一个共同的原型,并且在内心深处都有对该原型的引用(即 __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__,有就用。

希望对您有所帮助!

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

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

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

现在回到构造函数。首先要注意的是,这些只是普通函数——实例的实际 "creator" 是关键字 new。现在每个函数都有一个 prototype 属性 告诉你:使用这个函数创建的每个对象都将原型连接到函数 prototype 属性 中的任何对象。默认情况下,每个函数都包含此 属性 中的对象实例,这意味着您从该函数创建的每个对象都将原型连接到此实例,因此将具有 "inherit" 属性,例如 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中进行继承的方式。

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

用非常简单的语言来说,每个函数都是 javascript 中的一种特殊对象,每个函数都有自己的 "prototype" 对象容器。 所以每个构造函数都会有自己的 "prototype" 对象,它会与使用 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。

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

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

现在你可以把一个原型想象成一个普通的space,就像所有家庭成员的房子,如果任何家庭成员对那个房子做出改变,它将是每个人都改变了,每个家庭成员都可以进入房子,无论他多大年纪或他只是一个新生儿。每个家庭成员都一样,楼梯一样,房间一样,墙壁的颜色一样。

我希望这能帮助您理解原型链,我尝试用不同的方式来解释它,我认为这是更好的方式。

当你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;