深入了解 JavaScript 中的原型链和关于原型链的继承方式

写在前面

JavaScript 中的原型链属于重难点,也是继承的必要条件,掌握好 JavaScript 中的原型链不管是学习还是工作都会有很大的益处

在这里插入图片描述

原型中的关系

在了解原型链是什么之前我们先了解一下原型中的关系

在 JavaScript 中的每个函数都会有一个 prototype 属性,这个属性又会返回一个原型,原型又有一个 constructor 属性,这个属性指向与之关联的构造函数。通过构造函数实例化的对象会有一个 __proto__ 属性,这个 __proto__ 属性指向构造函数的 prototype

现在来测试 Object 构造函数与原型的关系,示例代码如下所示

// 首先 Object 是一个构造函数,就会有 prototype 属性
var result = Object.prototype
console.log(result); // 得到一个原型对象
/*
 * 原型对象就会有两个属性
 *1. constructor 属性 -> 返回与之关联的构造函数
 *2. __proto__ 属性 -> 返回指向构造函数的 prototype
 */
var result2 = result.constructor
console.log(result2); // [Function: Object]
var result3 = result.__proto__
console.log(result3); // null

上面的代码可以画成如下的图

image-20201027203644978

Object.prototype 对象的原型是 null, 即 Object.prototype 没有原型。意味着查找属性的时候查到 Object.prototype 就可以停止查找了。

那其他构造函数的原型会是什么样子的呢?如下代码测试

// 定义一个构造函数
function A() {}
// 获取 A 的原型
var result4 = A.prototype
console.log(result4); // 返回一个原型对象
// 获取 与 A 的原型关联的构造函数
var result5 = result4.constructor
console.log(result5); // 返回A() 这个构造函数
// 获取 A 原型对象的原型
var result6 = result4.__proto__
console.log(result6); // 返回 object 的 原型对象
console.log(result6 == Object.prototype); // true
// 实例化构造函数 A
var a = new A();
console.log(a.__proto__); // 返回A的原型对象

经过代码验证知道了构造函数 A 的原型对象的原型是 Object 的原型。

图解如下所示:

原型

原型链是什么

如果构造函数或对象 A,A 的原型指向构造函数或对象 B,B 的原型在指向构造函数或对象 C,以此类推,最终的构造函数或对象的原型指向 Object 的原型。简单的说就是利用原型让一个引用类型继承另一个引用类型的属性和方法。然后层层递进,由此形成了一条链状结构,被称之为原型链

按照上述的描述,在 B 中定义的属性或方法,可以直接在 A 中使用并不需要定义。这就是继承,它允许每个对象来访问其原型链上的任何属性或方法。

原型链是 ECMAScript 标准中指定的默认实现继承的方式。

示例代码如下所示:

// 创建 A 构造函数
function A() {
  this.a = 'a'
}
// 通过构造函数创建对象
var a = new A();
// 创建 B 构造函数
function B() {
  this.b = 'b'
}
// 使构造函数 B 的原型指向对象 a
B.prototype = a;
// 通过构造函数创建对象
var b = new B();
console.log(b.a);
console.log(b.b);

这段代码画成原型链如下图所示

简单的原型链

构造函数 B 的实例化 b 对象的原型指向构造函数 A 的实例化 a 对象,a对象的原型指向 Object 的原型,图中为画红线的标注,这就是原型链

通过原型链的方式可以完成继承,但是这种继承仅仅是某些方面的继承,并不是真正的继承。

只继承于原型

出于对效率的考虑,尽可能地将属性和方法添加到原型上。可以采取以下方式:

  • 不要为继承关系单独创建新的对象
  • 尽量减少运行时的方法搜索。

示例代码如下所示:

function A(){};
A.prototype.a = 'a'
function B(){};
// 将B原型指向A原型
B.prototype= A.prototype
// 为B原型新增自己的属性
B.prototype.b = 'b'
function C(){};
// 将C原型指向B原型
C.prototype = B.prototype
// 为C原型新增自己的属性
C.prototype.c = 'c'

const c = new C();
console.log(c.a);
const a = new A();
console.log(a.c);

图解

只继承于原型的原型链

原型链虽然很强大,用它可以实现JavaScript中的继承,但同时也存在着一些问题。

  • 原型链实际上是在多个构造函数或对象之间共享属性和方法
  • 创建子类的对象时,不能向父级的构造函数传递任何参数。

综上所述,在实际开发中很少会单独使用原型链。

写在最后

此篇博客介绍了 JavaScript 中的原型链,以及通过原型链完成继承的两种方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一碗周.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值