我想把头放在原型上,并且想知道原型到底是什么。许多困惑源于不了解用来描述的元语言 原型。
这是我所知道的:
当我们创建一个内部包含属性的命名构造函数时,该构造函数主体内部的属性将被该构造函数创建的对象实例继承。在这里,我从名为Person的构造函数中创建了一个名为person001的实例。
function Person(firstName,lastName) {
this.firstName = firstName;
this.lastName = lastName
}
undefined
var person001 = new Person("John","Doe");
当我在控制台中查看对象实例并跟踪原型链时,在2个不同的地方找到了它。它是dunder原型对象的构造函数对象。
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的prototype属性添加了一个名为age的属性。
Person.prototype.age = 0;
现在我添加了一个附加属性,原型到底是什么?
当我在对象实例person001上运行Object.getPrototypeOf方法时,它会返回看起来像原型对象的对象。它具有3个属性-构造函数,我添加的属性和隐式dunder原型对象。
Object.getPrototypeOf(person001);
{age: 0, constructor: ƒ}
age: 0
constructor: ƒ Person(firstName,lastName)
__proto__: Object
那么原型是什么?它是原型对象{构造函数,其他属性}吗?还是仅仅是原型对象的构造函数?
提前感谢您的帮助。
答案 0 :(得分:2)
当您进行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;
答案 1 :(得分:0)
表示您已经为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 !
如果以后您意识到您的人民不仅需要讲话,还需要步行,该怎么办?您需要参考他们的共同父母,以便告诉他们全部走在一起。那就是原型。玛丽和约翰都拥有一个共同的原型,并且在内部有一个对该原型的引用(对于朋友和家人来说,就是__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__
,并使用它(如果存在)。
希望这会有所帮助!
答案 2 :(得分:0)
首先,原型只是一个对象。 JS中的几乎每个对象(例如,当您使用Object.create(null)
时除外)都具有一些原型,并且可以链接这些原型。
示例:当您使用文字[]
创建数组时,您的数组实例将连接到对象(也是数组实例btw。),该对象定义了数组属性,例如map
等,该对象本身也已连接到另一个定义toString
之类的对象属性的原型(对象实例)。这些属性通常是函数。现在,当您访问诸如[].hasOwnProperty
之类的对象的属性之一时,引擎将查找原型链以查找它。它从您的[]
实例开始,没有找到它,继续到原型(数组),也没有成功,因此移到最后找到它的最后一个原型(对象实例)。
如您所见,原型链总是在某处结束,因此,如果您尝试在链中的最后一个原型上检索原型,则会得到null
。
现在回到构造函数。首先要注意的是,这些只是正常功能-实例的实际“创建者”是关键字new
。现在,每个函数都有一个prototype
属性,该属性告诉您:使用此函数创建的每个对象都将通过原型连接到该函数的prototype
属性中。默认情况下,每个函数都在此属性中包含Object的实例,这意味着从该函数创建的每个对象都将通过原型连接到该实例,因此将“继承”属性,例如toString
。
在此示例中,您可以看到函数的prototype
属性与实例原型之间的联系。
function A() {}
var a = new A();
a.__proto__ == A.prototype; // is true
最后,该函数的constructor
属性中的prototype
属性告诉您,当将该函数与new
关键字一起使用时,哪个函数将用于创建新实例。这满口归结为:
function A() {}
A == A.prototype.constructor; // is true
它是对自身的引用。在ES6之前创建自己的继承链时,可以设置此属性为可选属性,但是出于正确性的考虑,还是这么做了。
那么原型是什么?它只是一个对象,通过特殊的原型连接连接到其他对象实例,从而可以访问预定义的属性。这是在JS中进行继承的方法。
答案 3 :(得分:0)
编辑:我的朋友@teemu添加到我的答案:每个内置对象都有其原型。
用非常简单的语言,每个函数都是javascript中的一种特殊对象,每个函数都有自己的“原型”对象容器。
因此,每个构造函数都将具有其自己的“原型”对象,该对象将与使用该对象构造的__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;
在原型
中添加属性时也是一样但这会在所有地方得到体现
console.log(tony.__proto__.isSuperHero ) // true
console.log(Person.prototype.isSuperHero) //true
现在,您可以将原型想象成一个供所有家庭成员使用的房屋之类的通用空间,如果任何家庭成员在该房屋中进行了更改,那么每个人都可以使用它,并且每个家庭成员都可以使用房子,无论他多大或刚出生。每个家庭成员都将相同,楼梯将相同,房间将相同,墙壁的颜色将相同。
我希望这将有助于您理解原型链,我尝试以不同的方式进行解释,这是我认为更好的方法。