JS中的原型链与继承

原型链的类比

JS中原型链,本质上就是对象之间的关系,通过protoype[[Prototype]]属性建立起来的连接。这种链条是动态的,可以随时变更。

这个就跟C/C++中通过指针建立的关系很相似,比如,通过指针建立一个链表,一个个地址就是通过指针串连起来,产生关系。指针指向变化,就是决定了链表的形态。

JS中原型链最大的作用就是模拟继承,达到代码复用的目的。

MDN上的这篇文章对JS中原型链和继承介绍的很详细

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

但是读起来比较枯燥,尝试将总结下,加强理解。

原型链

对像与函数拥有的原型属性不同

尽管JS一切皆对象,但还是可以把对象细分下:

  1. 普通对象,包括自定义对象和内建对象(非new操作符创建的对象)。
  2. new操作符创建的对象。
  3. 函数。

为什么这么分,因为它们所拥有的原型的属性有所不同。

如下代码,定义一个函数Person,用它创建一个对象person

javascript 复制代码
function Person() {}

let person = new Person();

函数Person和对象person它们所包含的原型属性不同。

  • 函数Person

`

函数Person中包含prototype[[Prototype]](__prototype__)属性,普通函数也是如此。

  • 对象person

对象person中只包含[[Prototype]](__prototype__)属性,普通对象也是如此。

每个函数都一个prototype[[Prototype]](__prototype__)属性,对象没有prototype,只有[[Prototype]](__prototype__)属性。

原型链的产生

通过new操作符,使对象和函数间产生了连接。这条链接就是原型链 ,它们通过原型属性 prototype[[Prototype]]的指向产生链接。

上面的代码中,函数Person和对象person通过new操作符产生了链接,如下关系图:

  • person__proto__属性指向了Personprototype属性所指的对象,这个对象就是Person的原型对象。
javascript 复制代码
console.log(person.__proto__ == Person.prototype) //打印true
  • Person的原型对象(属性prototype的指向)是Person prototype,它也只有__proto__属性,它指向了Object的原型对象(属性prototype的指向)Object prototype
javascript 复制代码
console.log(Person.prototype.__proto__ == Object.prototype) //打印true
  • Person属性__proto__指向Function的原型对象(属性prototype的指向)Function prototype
javascript 复制代码
console.log(Person.__proto__ == Function.prototype) //打印true
  • Object对象的原型对象(属性prototype的指向)的__proto__对象指向null

对象的constructor属性

每个对象中都有constructor属性,表示由谁创建。普通对象的constructor属性指向Object

new操作符创建的对象,constructor属性指向new操作符调用的函数,称为对象的构造函数。

可以看看上面的图,画出了constructor属性的指向。

继承

原型链将各个对象链接在一起,但试图访问对象的属性时,不仅在该对象上查找属性,还会在该对象的原型上查找属性,以及原型的原型,依此类推,直到找个一个名字匹配的属性或到达原型链的末尾。这就很像,在链表中查找值,一个一个的节点查找,直到末尾。

如下代码,通过原型建立继承:

javascript 复制代码
// 构造函数
function Box(value) {
  this.value = value;
}

// 使用 Box() 构造函数创建的所有盒子都将具有的属性
Box.prototype.getValue = function () {
  return this.value;
};

const boxes = [new Box(1), new Box(2), new Box(3)];

这种写法也很像为指针赋值,Box.prototype.getVale() = ... ,改变Box原型对象的内容,以达到代码复用的目的。

对我这样c++的程序员来说,这种形式的继承实现,感觉非常奇怪。它也叫继承,但是与java,c++中继承大相径庭,在c++中继承是语法层面,静态特性。

JS这种实现,以我C++的角度看来更像是"链表"模拟继承。它的原型属性的指向可以随时变更,constructor属性也可以随时变更。也就是原来的继承体系,可以任意更改。

这样的方式,只能说是写代码的约定(约定通过原型实现继承),而不像c++中是语法层面的强约束。

在es6中继承的写法得到了改观,虽然只是个语法糖,本质没变,但是在写代码层,让人更容易理解,如下代码也是实现继承:

javascript 复制代码
class Base {}
class Derived extends Base {}

const obj = new Derived();
// obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null

Derived继承Base,写法上不再去显示操作原型属性,这样也增强了约定的约束力。

相关推荐
脉动数据行情14 分钟前
C# 实现德指 DAX 期货 WebSocket 实时行情监听
开发语言·websocket·c#
大圣编蚕30 分钟前
Java ByteArrayInputStream 详解:从入门到实战
java·开发语言·算法
菜鸟~noob23339 分钟前
【电子战】第02篇:能量检测器 vs. 相关检测器【含matlab代码】
开发语言·matlab
mayaairi1 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
OpenAnolis小助手2 小时前
一句话看透 JVM,SysOM 诊断 Skill 新增 Java 应用诊断能力
java·开发语言·jvm·阿里云·操作系统控制台·sysom
小玮看世界2 小时前
[Python]OD算法在OD实际运用转化参考清单
开发语言·python·算法
kyriewen2 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
ForteScarlet2 小时前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
萧瑟余晖2 小时前
Java深入解析篇六十二之安全机制详解
java·开发语言
BillKu3 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss