我不会从问题的答案开始,而是从原型和构造函数的工作方式开始,以避免在尝试以部分理解的方式解释这些答案时出现混乱。那么,原型回顾一下:
-
JavaScript 中的每个值,除了null
and undefined
,有一个关联值:它的原型。
-
原型用于查找属性。当你评价时x.foo
,您检查该值是否x
has an 自有财产– 其自身的属性 – 名为“foo”。如果是的话,x.foo
是那个的值自有财产。如果没有,则继续查找x
的原型。
-
值的原型可以是null
,意味着任何未找到自己财产的财产查找都会导致undefined
.
-
您可以使用该函数获取值的原型Object.getPrototypeOf https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf.
-
您可以使用以下函数创建具有特定原型的新对象Object.create https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create.
构造函数在 JavaScript 中有一个名为“原型”的属性。该属性的值不是构造函数的原型;而是构造函数的原型。它是用构造函数创建的值的原型。以您的示例构造函数为例:
function Food() {}
如果你跑new Food()
,将创建一个新对象,其原型设置为Food.prototype
, and Food
将被执行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
and y
没有原型,x.foo
在此链上查找:
x -> y -> null
- If
x
有一个自己的属性“foo”,x.foo
评估其价值
- If
y
有一个自己的属性“foo”,x.foo
评估其价值
- 我们已经达到了
null
,链的末端,所以x.foo
is undefined
默认值prototype
构造函数的属性是一个新的Object
实例,所以a的原型链new Food()
看起来像这样:
f -> Food.prototype -> Object.prototype -> null
你可以说一个值x
是一个构造函数的实例C
if x
的原型是C.prototype
or x
的原型不为 null 并且是一个实例C
. (If x
is C.prototype
, x
不是一个实例C
.) 这就是instanceof
操作员工作原理²:
console.log({} instanceof Object); // true
console.log(Object.prototype instanceof Object); // false
你也可以这样说C
继承自D
if C.prototype
是一个实例D
.
JavaScript 中内置的所有内容都具有Object.prototype
在其原型链上。功能有Object
实例:
function f() {}
f instanceof Object // true
所以构造函数也是:
function Food() {}
Food instanceof Object // true
值得注意的是,这并没有说明实例之间的关系Food
and Object
。您可以设置Food.prototype = null
to get new Food() instanceof Object === false
,但情况仍然会是这样Food instanceof Object
.
我希望这个框架足以解决您的问题。无论如何,这就是想法。仍然会明确地使用它来回应他们:
手头的问题
假设我们创建以下构造函数 Food。此时,函数 Food() 具有 Food.prototype 属性。由于 Food 是 Object 的实例,因此这意味着 Obect.prototype 是使用 Food() 创建的所有对象的原型属性。
所有创建的对象new Food()
有一个原型Food.prototype
。原型为Food.prototype
is Object.prototype
. Food
是一个函数,这意味着它确实是一个实例Object
,但相关实例Object
这是Food.prototype
.
然后我们通过将Pizza 的prototype 属性设置为Food 的实例来使Pizza 继承自Food。 Pizza 的原型属性现在是 Food.prototype,因为 Food 是 Pizza 的父级。
原型property函数的Pizza
is now 一个物体与Food.prototype
作为它的原型.
myPizza 是否也有一个从 Pizza 继承的原型属性?
myPizza
不继承任何东西Pizza
。它继承了对象的一切Pizza.prototype
. Since Pizza.prototype
没有名为“prototype”的属性,myPizza
不继承名为“prototype”的属性。
什么是对象原型?它是 Object() 的属性吗?
Object.prototype
字面意思是“原型”属性Object
”,所以是的。这个值位于所有Object实例的原型链上,这很重要,因为JavaScript中的大多数东西都是Object的实例。除此之外,它就像其他任何东西一样prototype
构造函数的属性。
只有函数才有原型属性吗?
使用定义的函数function
or class
关键字以名为的属性开头prototype
。 (箭头函数没有,也不能用作构造函数。)您可以放置一个名为prototype
在任何事情上。它仅在构造函数上才有意义——与 一起使用的函数new
or instanceof
.
Object.prototype 是一个对象吗?
当使用“object”来引用任何非原始值时,它是一个带有小写“o”的对象。它不是一个实例Object
- 它有一个null
原型。
这个函数本身是一个对象吗?
是的,函数就是对象。
Pizza.prototype 是否指的是创建 Pizza 构造函数的整个函数?
No. Pizza.prototype
不是一个函数。它由以下人员使用Pizza
构造函数但是Pizza
构造函数不是它的实例,也不是由它创建的。
或者 Pizza.prototype 只是指 Pizza() 范围内的内容?
也与范围无关。当你评价时new Pizza()
, Pizza
is called与一个新实例Pizza
as its this
value. 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()
指的是函数,而不是调用函数得到的值。一定要准确!)
toppings 仅仅是用 Pizza 构造函数创建的对象的属性吗?
Right!
Pizza.prototype 是 Pizza 构造函数的属性吗?
Yes.
目前原型链如下:
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 中很常见。添加了 ES5Object.create
。添加ES6class
and extends
,这就是您在实践中想要使用的。
¹ Primitives don’t have a [[Prototype]] in the spec but that doesn’t really matter because their property lookup and post-ES5 Object.getPrototypeOf
work like they do.
² Primitives are strings, booleans, numbers, symbols, null
, and undefined
. Primitives are immutable – they don’t have any own properties. The distinction between objects and primitives isn’t that important in JavaScript, but because they don’t have any own properties it doesn’t make sense to use them as prototypes. They also don’t count as instanceof
anything for what I’m going to claim are historical reasons.
³ By default, that is. You can ask one or all of them not to be with Object.setPrototypeOf
. There’s no reason to do this.