JavaScript 是基于 原型(prototype) 继承的语言,每个对象都可以从它的 原型 继承属性和方法。 原型链(Prototype Chain) 就是多个对象通过
prototype
关联形成的链式结构,用于实现继承。
__proto__
(原型指向):实例对象内部的 __proto__
连接到其构造函数的 prototype
,形成原型链。
实例对象.__proto__ === 构造函数.prototype
按照我自己的理解来说,就是 创建对象的那个东西长什么样子。
举个例子:有一批东西产品,是从一个模具做出来的。那这个产品 的__proto__
(原型)就是这个模具,这个产品有自己的颜色和功能。(颜色可以看作这个产品自身的属性,每个都不同;功能是共享模具的,每个对象都一样)。
下面用js来深刻体会一下原型链:
// 1 定义一个模具(构造函数)
function Product(color) {
this.color = color; // 每个产品(实例)都有不同的颜色
}
// 2 给模具添加通用功能(方法)
Product.prototype.use = function() {
console.log("Using the product!");
};
// 3 生产两个产品(实例)
const productA = new Product("red");
const productB = new Product("blue");
console.log(productA.color); // "red" (每个实例的属性不同)
console.log(productB.color); // "blue"
productA.use(); // "Using the product!" (共享方法)
productB.use(); // "Using the product!"
console.log(productA.__proto__ === Product.prototype); // true
console.log(productB.__proto__ === Product.prototype); // true
另外,通过在控制台中输出一下原型,帮助理解
function Product(color) {
this.color = color; // 每个产品(实例)都有不同的颜色
}
Product.prototype.use = function() {
console.log("Using the product!");
};
const p1 = new Product("red");
// 控制台输出p1原型
console.log(p1.__prodo__)
prototype:只存在于函数对象,用于定义实例共享的方法和属性
__proto__:任何对象都有,指向创建该对象的构造函数的 prototype
简单来说,它们关系如下
// 构造函数
function Person(name) {
this.name = name;
}
// 通过prototype向构造函数添加方法
Person.prototype.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
// 实例对象
const p1 = new Person('小红');
p1.sayHello() // Hello, my name is 小红
//简单来说
实例对象.__proto__ === 构造函数.prototype
结合上面的Person,给出下面的一些关系,供理解
console.log(p1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
一个关系示意图是这样子的:
p1 (实例对象)
|
| __proto__
↓
Person.prototype (原型对象)
|
| __proto__
↓
Object.prototype
|
| __proto__
↓
null (原型链终点)
prototype
经过上面,我就在想方法写在构造函数内部和使用 prototype
添加,有什么区别呢?
第一种写法,把方法写在构造函数内部
function Product(color) {
this.color = color;
// 每次创建一个实例,就会重新创建一个新函数
this.use = function() {
console.log("Using the product!");
};
}
const p1 = new Product("red");
const p2 = new Product("blue");
console.log(p1.use === p2.use); // false (每个实例都有自己的 use 方法,占用更多内存)
第二种写法,使用 prototype
让所有实例共享方法
function Product(color) {
this.color = color;
}
// use 方法只创建一次,所有实例共享
Product.prototype.use = function() {
console.log("Using the product!");
};
const p1 = new Product("red");
const p2 = new Product("blue");
console.log(p1.use === p2.use); // true (所有实例共享同一个 use 方法)
经查,得出结论,写在构造函数内部性能消耗严重
new Product()
,都会创建一个新的 use
方法,导致内存浪费。use
方法的功能完全一样,但 JavaScript 仍然要给每个实例分配不同的函数空间。使用 prototype
优势:
use
方法只创建一次,所有 Product
实例共享这个方法。