深入了解JS原型及原型继承链机制

JavaScript 原型 prototype

前言------了解prototype

本文的上篇 ,看完能对js原型有个初步了解: 万物皆对象?带你梳理JS原型及其查找链机制

关于 prototype,你要知道这些:

  1. 函数天生拥有 prototype
  2. 我们可以将一些属性和方法挂载在原型上,在创建实例之后,实例就可以调用这些属性和方法
  3. 我们可以将一些共用的属性和方法添加在原型上,这样可以减少构造函数在执行时的性能开销
  4. 实例对象无法修改原型上的属性值
javascript 复制代码
Array.prototype.abc = function(){
    console.log('abc')
}
const arr = []    
arr.unshift(1)
arr.abc()  
const num = 123 
// num.unshift(0)

这段代码将会输出

css 复制代码
abc
[ 1 ]

在上面的例子中,我们给 Array.prototype添加了一个 abc方法,所有的数组实例都可以调用它------------共用的属性和方法添加在原型上,可以减少构造函数在执行时的性能开销

注意:

  • 实例对象中显示拥有的属性 来自于 构造函数中定义的属性
  • 实例对象中隐式拥有的属性 来自于 构造函数的 prototype

new 的工作原理

当我们使用 new 关键字调用构造函数时,V8做了四件事:

  1. 创建空对象 :凭空创建一个 this 对象
  2. 执行构造函数:执行函数中的代码
  3. 建立原型链接 :让隐式原型等于显式原型 this.__proto__ = 对象.prototype
  4. 返回对象 :返回这个对象 return this

对象原型

  • __proto__ (隐式原型) :每个实例对象都拥有的属性,指向构造函数的 prototype
  • prototype(显式原型) : 构造函数天生拥有的属性,指向一个原型对象
  1. 每个对象都拥有__proto__属性,该属性也是一个对象
  2. v8 在访问对象中的一个属性时,会先访问对象中显式存在的属性。如果没有,就会去对象的隐式原型上查找
  3. 实例对象的隐式原型 === 构造函数的显示原型

实例对象的显式原型 和 隐式原型实际上是同一个对象,因为 this.__proto__ = 对象.prototype执行的是赋值语句 [[prototype]] === __proto__

  1. constructior 构造器属性,记录该实例对象是由谁创建的
ini 复制代码
Car.prototype.name = 'su7'
Car.prototype.lang = '4800'
Car.prototype.width = '1400'

function Car(color){
    this.color = color
}
Car.name = 'weilai'
const car = new Car('blue')
console.log(car)
console.log(car.__proto__);

输出

css 复制代码
Car { color: 'blue' }
{ name: 'su7', lang: '4800', width: '1400' }

可以看到,我们给 Car.prototyp 添加了几个属性,然后我们输出了car这个对象,属性有颜色。又输出了 car.__proto__,可以看到我们给他的原型添加的属性。

原型链继承

我们来看一段代码:

javascript 复制代码
function Father() {
  this.lastName = '张'
}
function Child() {
  this.age = 18
}
const p = new Child() 
console.log(p.lastName)

我们定义了对象 FatherChild。一般情况下,孩子应当跟着父亲姓,而Child中我们没写 lastName属性,想让孩子继承父亲的姓,该怎么做?

ini 复制代码
Child.prototype = new Father()

Child.prototype指向Father()的显式原型即可。在执行这段代码时,V8遵循原型链查找原则

  • v8查找实例对象的某个属性值的时候查找不到,就会往上找这个实例对象的 实例.__proto__隐式原型,也就是构造函数的prototype

  • 如果还没有这个属性值,就再查找 构造函数.prototype.__proto__原型

  • 直到找到Object.prototype.__proto__,为 'null' ,如果还找不到,返回 'undefined'

如果我又定义一个 Grand,它是Father的父类,他的属性有一个房子和一张卡传承下来,卡中有一个亿:

javascript 复制代码
Grand.prototype.house = function(){
    console.log('big house');
}
function Grand(){
    this.card = 100000000
}

function Father() {
    this.lastName = '张'
}
Father.prototype = new Grand()

Child.prototype = new Father()

function Child() {
    this.age = 18
}

const p = new Child()
console.log(p.lastName)  
p.house()                

输出

复制代码
张
big house

我们用官方一点的语言总结:

  1. v8在访问对象中的一个属性时,会先访问对象中显式存在的属性,如果没有,就会去对象的隐式原型上查找,如果还没有,就顺着隐式原型一直往上找,直到找到 'null' 为止,这个查找关系 叫做 原型链查找。

任何对象最终都继承自 Object.prototype(原型为 null)

一张图理清原型链

Function()原型链

上图中你会发现,有一个奇怪的地方:Fuction 指向了他自己。

'function foo()->Function()<-->Functino()'

'Function()'的显式原型是他自己,也就是'Function.proto = Function.prototype'

当然最终找到 'Function.prototype.proto = Object.prototype'

总结

  1. prototype :构造函数天生拥有,指向原型对象
  2. __proto : 实例对象天生拥有,指向构造函数的 prototype
  3. constructor :原型对象天生拥有,指向构造函数
  4. 原型链 :对象查找属性的链路,从自身到 Object.prototype
  5. new : 创建实例对象时,建立 proto 链接

如果觉得本文对你有帮助,欢迎点赞收藏评论。如有错误,欢迎指正。🌹🌹

相关推荐
森林的尽头是阳光22 分钟前
tree回显问题
javascript·vue.js·elementui
weixin_BYSJ198743 分钟前
「课设设计」springboot校园超市助购系统26449 (附源码)
java·javascript·spring boot·python·django·flask·php
mfxcyh2 小时前
Vue3+Ant Design Vue 实现手动异步文件上传(含大小校验、弹窗上传)
前端·javascript·vue.js
meilindehuzi_a2 小时前
Vue3进阶基础:指令修饰符、v-model表单绑定、动态样式、计算属性与侦听器实战
前端·javascript·vue.js
csdn2015_2 小时前
vue 前端运行命令
前端·javascript·vue.js
Cobyte2 小时前
根据浏览器解析 HTML 的规范实现 HTML 解析器
前端·javascript·vue.js
默_笙3 小时前
😁 LLM 不知道的事它不会说"不知道",而是会"编"——RAG 就是让它闭嘴先查资料
前端·javascript
Revolution613 小时前
变量提升到底提升了什么:为什么 var 是 undefined,let 却直接报错
前端·javascript
labixiong3 小时前
ES2026 落地了什么?两个真上线的特性 + 两个被毙的,一次讲清楚
javascript·ecmascript 6·ecmascript 8
IT知识分享3 小时前
WebP转JPG开发经验:从格式解码到本地批量转换的实践
javascript·python·图片转换