
JavaScript 原型
在本教程中,您将了解 JavaScript 原型及其背后的工作原理
在本教程中,您将了解 JavaScript 原型及其背后的工作原理。
JavaScript 原型简介
在 JavaScript ,对象可以通过原型继承特性。每个对象都有一个自己的原型属性。
因为原型本身也是另一个对象,所以原型有自己的原型。这形成了原型。当原型的值是 null ,原型结束。
假设您有一个对象 person,其属性名为 name:
let person = {'name' : 'John'}在控制台中检查 person 对象时,您会发现 person 对象有一个名为 prototype 的属性 [[Prototype]]:

原型本身是一个具有自己属性的对象:

当您访问对象的属性时,如果对象具有该属性,它将返回属性值。以下示例访问 person 对象的 name 属性:

它按预期返回 name 属性值。
但是,如果您访问对象中不存在的属性,JavaScript 引擎将在对象的原型中进行搜索。
如果 JavaScript 引擎无法在对象的原型中找到该属性,它将在原型的原型中搜索,直到找到该属性或到达原型的末尾。
例如,您调用 person 对象 toString() 的方法:

toString() 方法会返回 person 对象的字符串表示形式 [object Object]。默认情况下,它给出信息是不足够的 。
请注意,当函数是对象属性的值时,它被称为方法。因此,方法是函数作为作为的属性。
在这个例子中,当我们调用 person 对象 toString() 方法时,JavaScript 引擎会在person 对象中找到它。
因为这个 person 对象没有这个 toString() 方法,它会在 person 对象的原型对象中寻找 toString() 这个方法。
由于 person 对象原型有 toString() 方法,因此 JavaScript 引擎会调用 person 对象原型的 toString() 。

JavaScript 原型图
JavaScript 具有内置 Object() 函数。如果您将函数传递给运算符 typeof,typeof 运算符将返回 'function' 。例如:
typeof(Object)输出:
'function'请注意,Object() 是一个函数,而不是一个对象。如果这是您第一次了解 JavaScript 原型,您会感到困惑。此外,JavaScript 提供一个匿名对象,可以通过对象的 prototype 的属性来引用 Object() 函数:
console.log(Object.prototype);
Object.prototype 对象具有一些有用的属性和方法,例如 toString() 和 valueOf()。
Object.prototype 还有 一个重要的属性,称为 constructor,constructor 引用着 Object() 函数。
以下语句可以确定 Object.prototype.constructor 属性引用 Object 函数:
console.log(Object.prototype.constructor === Object); // true假设一个圆代表一个函数,一个正方形代表一个对象。下图说明 Object() 函数与 Object.prototype 对象的关系:
首先,定义一个构造函数称为 Person。如下:
function Person(name) {
this.name = name;
}在此示例中,Person()函数接受一个 name 参数并将其分配给 this 对象 name 的属性。
在幕后,JavaScript 创建一个函数 Person() 和一个匿名对象:
与 Object() 函数一样,Person() 函数有一个属性叫 prototype ,prototype 引用着匿名对象。并且匿名对象有一个 constructor 属性,引用着 Person() 函数。
下面展示 Person() 函数与匿名对象通过 Person.prototype 互相引用。
console.log(Person);
console.log(Person.prototype);
此外,JavaScript 通过将 Person.prototype 对象链接到对象,这被称为原型链接。Object.prototype[[Prototype]]
In addition, JavaScript links the Person.prototype object to the Object.prototype object via the [[Prototype]], which is known as a prototype linkage.
除此之外,JavaScript 通过 [[Prototype]] 链接 Person.prototype 与 Object.prototype ,这称为原型链。
原型链接在下图中用 [[Prototype]] 表示:
在 JavaScript 原型对象定义方法
下面定义了一个 greet() 方法在 Person.prototype 对象:
Person.prototype.greet = function() {
return "Hi, I'm " + this.name + "!";
}在这种情况下,JavaScript 引擎将 greet() 方法添加到 Person.prototype 对象:
下面创建一个 Person 实例:
let p1 = new Person('John');在内部,JavaScript 引擎创建一个名为 p1 的新对象 ,并通过原型链接将 p1 对象链接到 Person.prototype 对象:
p1、Person.prototype 和 Object.protoype 之间的链接称为原型链。
下面在 p1 对象调用 greet() 方法:
let greeting = p1.greet();
console.log(greeting);因为 p1 没有 greet() 方法,JavaScript 引擎搜索原型链并在 Person.prototype 对象找到它 greet()。
由于 JavaScript 可以在 Person.prototype 对象上找到 greet() 方法,因此它会执行greet() 方法并返回结果:
下面调用 p1 对象上的 toString() 方法:
let s = p1.toString();
console.log(s);在这种情况下,JavaScript 引擎会在 Person.prototype 原型链查找 toString()。
因为 Person.prototype 没有 toString() 方法,JavaScript 引擎会向上搜索原型链并在 Object.prototype 对象找到 toString() 方法。
由于 JavaScript 可以找到 Object.prototype 的 toString() 方法,因此它会执行 toString() 方法。
如果你调用了一个在 Person.prototype 和 Object.prototype 对象上都不存在的方法,JavaScript 引擎会沿着原型链,如果找不到该方法就会抛出错误。例如:
p1.fly(); 由于原型链中的任何对象都不存在 fly() 方法,因此 JavaScript 引擎会抛出以下错误:
TypeError: p1.fly is not a function以下创建 Person 的另一个实例其名称属性的值是 'Jane':
let p2 = new Person('Jane');对象 p2 具有 p1 对象的属性和方法。总之,当你在 prototype 对象定义一个方法时,这个方法被所有实例共享。
在单个对象中定义方法
下面在 p2 对象定义 draw() 方法。
p2.draw = function () {
return "I can draw.";
};JavaScript 引擎将 draw() 方法添加到 p2 对象,而不是 Person.prototype 对象:
这意味着您可以调用 p2 对象的 draw() 方法:
p2.draw();但是你不能在 p1 对象上调用 draw() 方法:
p1.draw()错误:
TypeError: p1.draw is not a function当您在对象中定义方法时,该方法仅可用于该对象。默认情况下不能与其他对象共享。
获取原型链接
__proto__ 发音为 dunder proto。__proto__ 是 Object.prototype 对象的访问器属性。它公开访问它的对象的内部原型链接。
__proto__ 在 ES6 中标准化,以确保与 Web 浏览器的兼容性。但是,将来可能会弃用__proto__ 并使用 Object.getPrototypeOf() 代替。因此,您不应该在生产代码中使用 __proto__。
p1.__proto__ 公开了引用 Person.prototype 对象的 [[Prototype]] 。同样,p2.__proto__ 也引用相同的对象作为 p1.__proto__:。
console.log(p1.__proto__ === Person.prototype); // true
console.log(p1.__proto__ === p2.__proto__); // true如前所述,您应该使用 Object.getPrototypeOf() 方法而不是 __proto__. Object.getPrototypeOf() 方法返回指定对象的原型。
console.log(p1.__proto__ === Object.getPrototypeOf(p1)); // true获取原型链接的另一种流行方法是当 Object.getPrototypeOf() 方法不可用时通过constructor 属性获取,如下所示:
p1.constructor.prototype因此 .constructor.prototype 返回原型对象, p1.constructor 返回 Person 。
隐藏方法
请参阅以下方法调用:
console.log(p1.greet());
p1 对象没有 greet() 定义方法,因此 JavaScript 会向上到原型链搜索 greet()。在这种情况下,它可以找到 Person.prototype 对象中的方法。
让我们向 p1 对象添加一个新方法,其名称与 Person.prototype 对象中的方法相同:
p1.greet = function() {
console.log('Hello');
}然后调用 greet() 方法:
console.log(p1.greet());因为 p1 对象有 greet() 方法,JavaScript 直接执行它,而不用在原型链中搜索 greet() 方法。
这是阴影的一个例子。p1 对象的 greet() 方法隐藏 prototype 对象的 greet() 方法。
结论
- Object() 函数有一个名为 prototype 的属性,它引用 Object.prototype 对象。
- Object.prototype 对象具有所有对象中可用的所有属性和方法,例如 toString() 和 valueOf()。
- Object.prototype 对象具有引用 Object 函数的构造函数属性。
- 每个函数都有一个 prototype 对象。Object.prototype 此原型对象通过 [[prototype]] 链接或属性引用对象 __proto__。
- 原型链允许一个对象通过 [[prototype]] 链接使用其原型对象的方法和属性。
- Object.getPrototypeOf() 方法返回指定对象的原型对象。 请使用 Object.getPrototypeOf() 方法而不是 __proto__。












