以下对 Javascript 中的“原型”的解释是否有效?
Is the following explanation of `prototypes` in Javascript valid?
我一直在尝试了解原型,因为我需要对它们有相当多的了解。我一直在使用 this MDN 文章及其相关文章作为参考。
经过一些阅读,我对原型有了一些清晰的认识,并尝试用我自己的话来表达它们,想知道它是否正确,喜欢知道是否我哪里错了
解释开始
每个对象都是使用某种构造函数创建的。比如说,我们创建一个对象如下。
let obj = new Object();
这里,Object
是构造函数。关于函数的事情是,所有函数(包括非构造函数)都有一个名为 prototype
的 属性。 prototype
属性 定义了 any 对象的原型,该对象是使用 new
关键字和所述构造函数创建的。你可以检查原型属性如下:
console.log(Object.prototype);
上面的代码片段将 return 一个具有一堆方法的对象,任何使用 new Object()
创建的对象都可以使用这些方法。
在上面的例子中,如果措辞太混乱,你可以用替换所有出现的Object
任何其他构造函数如Array
,Date
甚至自定义构造函数,例如 Person
或您可能已定义的其他内容。
解释结束
我的理解对吗?如果不是,你能指出我哪里做错了吗?
Is my understanding right? If not, can you point me where I went wrong?
大体来说,是的,你的理解大部分是正确的,但是解释的不完整,还有一些细节不正确。
Every object is created using a constructor function of some sort.
这不太正确,JavaScript 也有文字形式({}
[对象]、[]
[数组] 和 //
[正则表达式])不使用构造函数创建对象。这些形式将 Object.prototype
、Array.prototype
和 RegExp.prototype
(分别)分配给它们创建的对象,即使未调用构造函数本身也是如此。
还有其他方法可以完全不通过构造函数来创建对象。例如,有 Object.create
,它创建一个对象并将您提供的原型分配给它:
const p = {};
const obj = Object.create(p);
console.log(Object.getPrototypeOf(obj) === p); // true
(通过隐式转换创建对象还有更晦涩的方法。)您还可以使用Object.setPrototypeOf
更改现有对象的原型。
The thing about functions is that, all of them (including non-constructor functions) have a property called prototype
on them.
不完全是,箭头函数和 class 方法没有 prototype
属性 并且不能用作构造函数:
const arrow = () => {};
class X {
method() {
}
static staticMethod() {
}
}
console.log("prototype" in arrow); // false
console.log("prototype" in X.prototype.method); // false
console.log("prototype" in X.staticMethod); // false
This prototype
property defines what will be the prototype of any object that is created using the new
keyword and said constructor function.
正确。 (构造函数可能会弄乱它们 return,但这是通常的标准行为。)
在解释的这一点上,我可能会指出函数上的 prototype
属性 和对象原型之间的区别。初学者有时会认为在对象上设置 prototype
属性 会改变其原型;不是,这个名字只对函数有意义,它是 不是 函数的原型,它只是一个 属性 ,(如你所说)将用于分配使用 new
和该函数创建的对象的原型。对象的原型保存在对象的内部字段 [[Prototype]]
中。该字段不能 直接 访问,但您可以通过 Object.getPrototypeOf
访问它并通过 Object.setPrototypeOf
更改它(您也可以使用已弃用的 __proto__
访问器 属性,它只是这些函数的包装器——但不要使用 __proto__
,直接使用这些函数)。
但除此之外,您的解释中还有一个未解决的大问题: 的原型是什么?他们在做什么?为什么有它们?
答案是他们提供了JavaScript的继承机制。当您在对象上获得 属性 的值并且对象没有自己的 属性 和给定键时,JavaScript 引擎会查看对象的原型以查看如果它有属性(和原型的原型,等等通过链):
const parent = {
a: "a property on base",
};
const child = Object.create(parent);
child.b = "a property on child";
const grandChild = Object.create(child);
grandChild.c = "a property on grandChild";
console.log(grandChild.a); // "a property on base"
console.log(grandChild.b); // "a property on child"
console.log(grandChild.c); // "a property on grandChild"
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`hasOwn(grandChild, "a")? ${hasOwn(grandChild, "a")}`); // false
console.log(`hasOwn(grandChild, "b")? ${hasOwn(grandChild, "b")}`); // false
console.log(`hasOwn(grandChild, "c")? ${hasOwn(grandChild, "c")}`); // true
这些示例 属性 值是字符串,但这在 属性 值是函数的情况下被广泛使用,提供了一种从父对象继承方法的方法。
虽然属性访问过程是不对称的;它仅适用于 获取 一个 属性 的值。如果你设置一个对象的属性值,它总是将它设置在对象本身,而不是它的原型上:
const parent = {
prop: "parent",
};
const child = Object.create(parent);
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`[Before] child.prop: ${child.prop}`);
// => "[Before] child.prop: parent"
console.log(`[Before] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[Before] hasOwn(child, "prop")? false"
child.prop = "child";
console.log(`[After] child.prop: ${child.prop}`);
// => "child.prop: child"
console.log(`[After] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[After] hasOwn(child, "prop")? true"
(获取和设置 属性 值之间的差异适用于数据属性 [我们主要创建的类型];访问器属性的工作方式不同,因为获取和设置 属性 会导致函数调用,并且访问器的 setter 函数可以做作者想要它做的任何事情。)
我一直在尝试了解原型,因为我需要对它们有相当多的了解。我一直在使用 this MDN 文章及其相关文章作为参考。
经过一些阅读,我对原型有了一些清晰的认识,并尝试用我自己的话来表达它们,想知道它是否正确,喜欢知道是否我哪里错了
解释开始
每个对象都是使用某种构造函数创建的。比如说,我们创建一个对象如下。
let obj = new Object();
这里,Object
是构造函数。关于函数的事情是,所有函数(包括非构造函数)都有一个名为 prototype
的 属性。 prototype
属性 定义了 any 对象的原型,该对象是使用 new
关键字和所述构造函数创建的。你可以检查原型属性如下:
console.log(Object.prototype);
上面的代码片段将 return 一个具有一堆方法的对象,任何使用 new Object()
创建的对象都可以使用这些方法。
在上面的例子中,如果措辞太混乱,你可以用替换所有出现的Object
任何其他构造函数如Array
,Date
甚至自定义构造函数,例如 Person
或您可能已定义的其他内容。
解释结束
我的理解对吗?如果不是,你能指出我哪里做错了吗?
Is my understanding right? If not, can you point me where I went wrong?
大体来说,是的,你的理解大部分是正确的,但是解释的不完整,还有一些细节不正确。
Every object is created using a constructor function of some sort.
这不太正确,JavaScript 也有文字形式({}
[对象]、[]
[数组] 和 //
[正则表达式])不使用构造函数创建对象。这些形式将 Object.prototype
、Array.prototype
和 RegExp.prototype
(分别)分配给它们创建的对象,即使未调用构造函数本身也是如此。
还有其他方法可以完全不通过构造函数来创建对象。例如,有 Object.create
,它创建一个对象并将您提供的原型分配给它:
const p = {};
const obj = Object.create(p);
console.log(Object.getPrototypeOf(obj) === p); // true
(通过隐式转换创建对象还有更晦涩的方法。)您还可以使用Object.setPrototypeOf
更改现有对象的原型。
The thing about functions is that, all of them (including non-constructor functions) have a property called
prototype
on them.
不完全是,箭头函数和 class 方法没有 prototype
属性 并且不能用作构造函数:
const arrow = () => {};
class X {
method() {
}
static staticMethod() {
}
}
console.log("prototype" in arrow); // false
console.log("prototype" in X.prototype.method); // false
console.log("prototype" in X.staticMethod); // false
This
prototype
property defines what will be the prototype of any object that is created using thenew
keyword and said constructor function.
正确。 (构造函数可能会弄乱它们 return,但这是通常的标准行为。)
在解释的这一点上,我可能会指出函数上的 prototype
属性 和对象原型之间的区别。初学者有时会认为在对象上设置 prototype
属性 会改变其原型;不是,这个名字只对函数有意义,它是 不是 函数的原型,它只是一个 属性 ,(如你所说)将用于分配使用 new
和该函数创建的对象的原型。对象的原型保存在对象的内部字段 [[Prototype]]
中。该字段不能 直接 访问,但您可以通过 Object.getPrototypeOf
访问它并通过 Object.setPrototypeOf
更改它(您也可以使用已弃用的 __proto__
访问器 属性,它只是这些函数的包装器——但不要使用 __proto__
,直接使用这些函数)。
但除此之外,您的解释中还有一个未解决的大问题: 的原型是什么?他们在做什么?为什么有它们?
答案是他们提供了JavaScript的继承机制。当您在对象上获得 属性 的值并且对象没有自己的 属性 和给定键时,JavaScript 引擎会查看对象的原型以查看如果它有属性(和原型的原型,等等通过链):
const parent = {
a: "a property on base",
};
const child = Object.create(parent);
child.b = "a property on child";
const grandChild = Object.create(child);
grandChild.c = "a property on grandChild";
console.log(grandChild.a); // "a property on base"
console.log(grandChild.b); // "a property on child"
console.log(grandChild.c); // "a property on grandChild"
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`hasOwn(grandChild, "a")? ${hasOwn(grandChild, "a")}`); // false
console.log(`hasOwn(grandChild, "b")? ${hasOwn(grandChild, "b")}`); // false
console.log(`hasOwn(grandChild, "c")? ${hasOwn(grandChild, "c")}`); // true
这些示例 属性 值是字符串,但这在 属性 值是函数的情况下被广泛使用,提供了一种从父对象继承方法的方法。
虽然属性访问过程是不对称的;它仅适用于 获取 一个 属性 的值。如果你设置一个对象的属性值,它总是将它设置在对象本身,而不是它的原型上:
const parent = {
prop: "parent",
};
const child = Object.create(parent);
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`[Before] child.prop: ${child.prop}`);
// => "[Before] child.prop: parent"
console.log(`[Before] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[Before] hasOwn(child, "prop")? false"
child.prop = "child";
console.log(`[After] child.prop: ${child.prop}`);
// => "child.prop: child"
console.log(`[After] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[After] hasOwn(child, "prop")? true"
(获取和设置 属性 值之间的差异适用于数据属性 [我们主要创建的类型];访问器属性的工作方式不同,因为获取和设置 属性 会导致函数调用,并且访问器的 setter 函数可以做作者想要它做的任何事情。)