JavaScript原型 - 请澄清

问题描述 投票:2回答:1

有人可以帮我理解原型属性吗?我不明白prototype属性是函数的属性还是函数内部的属性。

假设我们创建了以下构造函数Food。此时,函数Food()具有属性Food.prototype。由于Food是Object的一个实例,因此这意味着Obect.prototype是使用Food()创建的所有对象的prototype属性。

function Food() {}

然后创建另一个构造函数Pizza。 Pizza有Pizza.prototype属性。

function Pizza(toppings) {
    this.toppings = toppings;
}

然后我们通过将Pizza的原型属性设置为Food的实例来使Pizza继承自Food。 Pizza的原型属性现在是Food.prototype,因为Food是披萨的父母。

Pizza.prototype = new Food();

然后我们创建一个Pizza实例

var myPizza = new Pizza("pepperoni");

myPizza还有一个继承自Pizza的原型属性吗?如果是这样,myPizza.prototype == Object.prototype?什么是Obejct.prototype?它是Object()的属性吗?只有函数有原型属性吗? Object.prototype是一个对象吗? Pizza.prototype是否指的是创建Pizza构造函数的整个函数?这个函数本身就是一个对象吗?

function Pizza(toppings) {
    this.toppings = toppings;
}

或者Pizza.prototype只是引用Pizza()范围内的内容?

this.toppings = toppings;

Pizza.toppings是Pizza.prototype的属性吗?但是Pizza.prototype不是Pizza()的属性吗?浇头只是使用Pizza构造函数创建的对象的属性吗?而Pizza.prototype是Pizza构造函数的属性?

目前的原型链如下:

myPizza - > Pizza.prototype - > Food.prototype - > Object.prototype

javascript inheritance prototype
1个回答
3
投票

我不是从问题的答案开始,而是从原型和构造函数的工作方式开始,以避免在尝试用部分理解来解释这些答案时产生混淆。那么,原型回顾:

  • JavaScript中的每个值,除了nullundefined,都有一个相关的值:它的原型.¹
  • 原型用于查找属性。当你评估x.foo时,你会检查值x是否有自己的属性 - 一个属于它自己的属性 - 名为“foo”。如果是这样,x.foo就是那个属性的价值。如果没有,查找继续在x的原型。
  • 值的原型可以是null,这意味着任何找不到自己的属性的属性查找都会导致undefined
  • 您可以使用Object.getPrototypeOf函数获取值的原型。
  • 您可以使用功能Object.create创建具有特定原型的新对象。

JavaScript中的构造函数具有名为“prototype”的属性。此属性的值不是构造函数的原型;它是使用构造函数创建的值的原型。以您的示例构造函数为例:

function Food() {}

如果运行new Food(),将创建一个新对象,其原型设置为Food.prototypeFood将在this设置为该新对象的情况下执行。换句话说,这个:

// create a new instance of Food
let f = new Food();

意思是这样的:

// create a new object with Food.prototype as its prototype
let f = Object.create(Food.prototype);

// initialize it using the constructor
Food.call(f);

现在,上面总结的属性查找工作方式产生了一个原型链。如果x有一个原型y而且y没有原型,那么x.foo会在这个链上查找:

x -> y -> null
  1. 如果x拥有自己的属性“foo”,则x.foo会评估其值
  2. 如果y拥有自己的属性“foo”,则x.foo会评估其值
  3. 我们已到达null,链的末端,所以x.fooundefined

构造函数的prototype属性的默认值是一个新的Object实例,因此new Food()的原型链如下所示:

f -> Food.prototype -> Object.prototype -> null

并且您可以说值x是构造函数C的一个实例,如果x的原型是C.prototypex的原型不是null并且是C的实例。 (如果xC.prototypex不是C的一个例子。)这就是instanceof算子的工作方式²:

console.log({} instanceof Object);  // true
console.log(Object.prototype instanceof Object);  // false

如果CD的一个例子,你也可以说C.prototype继承自D

JavaScript内置的所有东西都在其原型链上有Object.prototype。函数是Object实例:

function f() {}
f instanceof Object  // true

所以构造函数也是如此:

function Food() {}
Food instanceof Object  // true

值得注意的是,这并没有说明FoodObject实例之间的关系。你可以设置Food.prototype = null来获得new Food() instanceof Object === false,但它仍然是Food instanceof Object的情况。

我希望这个框架足以解决你的问题。无论如何,这就是这个想法。仍然会明确地使用它来回应它们:

The questions at hand

假设我们创建了以下构造函数Food。此时,函数Food()具有属性Food.prototype。由于Food是Object的一个实例,因此这意味着Obect.prototype是使用Food()创建的所有对象的prototype属性。

使用new Food()创建的所有对象都有Food.prototype的原型。 Food.prototype的原型是Object.prototypeFood是一个函数,这意味着它是Object的一个实例,但Object的相关实例是Food.prototype

然后我们通过将Pizza的原型属性设置为Food的实例来使Pizza继承自Food。 Pizza的原型属性现在是Food.prototype,因为Food是披萨的父母。

函数Pizza的原型属性现在是以Food.prototype为原型的对象。

myPizza还有一个继承自Pizza的原型属性吗?

myPizza不继承Pizza的任何东西。它继承了对象Pizza.prototype的所有内容。由于Pizza.prototype没有名为“prototype”的属性,因此myPizza不会继承名为“prototype”的属性。

什么是Object.prototype?它是Object()的属性吗?

Object.prototype字面意思是“Object的'原型'属性”,所以是的。此值位于Object的所有实例的原型链上,这只是重要的,因为JavaScript中的大多数内容都是Object的实例。除此之外,它就像构造函数的任何其他prototype属性。

只有函数有原型属性吗?

使用functionclass关键字定义的函数以名为prototype的属性开头。 (箭头函数不能,也不能用作构造函数。)您可以将名为prototype的属性放在任何内容上。它只在构造函数上有意义 - 与newinstanceof一起使用的函数。

Object.prototype是一个对象吗?

当使用“object”来引用任何非原始值时,它是一个小写“o”的对象。它不是Object的一个实例 - 它有一个null原型。

这个函数本身就是一个对象吗?

是的,功能是对象³。

Pizza.prototype是否指的是创建Pizza构造函数的整个函数?

没有.Pizza.prototype不是一个功能。它由Pizza构造函数使用,但Pizza构造函数不是它的实例,并且不是由它创建的。

或者Pizza.prototype只是引用Pizza()范围内的内容?

与范围无关。当你评估new Pizza()时,Pizza被调用Pizza的新实例作为其this值。 this不是函数的范围。 “范围”是可以看到一些变量集的区域。

function Foo() {
    let x = 5;  // a variable in scope. unrelated to `this`.
}
function Foo() {
    this.x = 5;  // assigning to a property of the value `this`.
                 // unrelated to variables.
}

Pizza.toppings是Pizza.prototype的属性吗?

这不是Pizza.toppings。有一个新对象 - this - 你将一个参数的值赋给Pizza函数,名为toppings,该属性是新对象的属性,也称为toppings。新对象的原型是Pizza.prototype,但新对象不是Pizza.prototype本身,所以答案是“不”,toppings不是Pizza.prototype的属性。

但是Pizza.prototype不是Pizza()的属性吗?

这是Pizza的财产。 (只是确保Pizza()指的是函数,而不是通过调用函数得到的值。要准确!)

浇头只是使用Pizza构造函数创建的对象的属性吗?

对!

而Pizza.prototype是Pizza构造函数的属性?

是。

目前的原型链如下:

myPizza - > Pizza.prototype - > Food.prototype - > Object.prototype

也正确。您可以使用前面提到的getPrototypeOf进行确认。

Object.prototype.toString = function () { return 'Object.prototype'; };

function Food() {}
Food.prototype.toString = function () { return 'Food.prototype'; };

function Pizza(toppings) {
    this.toppings = toppings;
}

Pizza.prototype = Object.create(Food.prototype);
Pizza.prototype.toString = function () { return 'Pizza.prototype'; };

let myPizza = new Pizza();
myPizza.toString = function () { return 'myPizza'; };

let chainLink = myPizza;

while (true) {
    console.log(String(chainLink));
    
    if (chainLink === null) {
        break;
    }
    
    chainLink = Object.getPrototypeOf(chainLink);
}

请注意,我在这里写了Object.create(Food.prototype)而不是new Food()。您不希望在子构造函数外部运行父构造函数,尽管它在ES3中很常见。 ES5添加了Object.create。 ES6添加了classextends,这是你想要在实践中使用的。

¹原语在规范中没有[[Prototype]],但这并不重要,因为它们的属性查找和后ES5 Object.getPrototypeOf就像它们那样工作。 ²原语是字符串,布尔,数字,符号,nullundefined。基元是不可变的 - 它们没有任何自己的属性。对象和基元之间的区别在JavaScript中并不重要,但由于它们没有任何自己的属性,因此将它们用作原型是没有意义的。他们也不算instanceof任何我要说的是历史原因。 ³默认情况下,即。您可以要求其中一个或所有人不要使用Object.setPrototypeOf。没有理由这样做。

© www.soinside.com 2019 - 2024. All rights reserved.