深入了解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 链接

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

相关推荐
天平7 小时前
油猴脚本创建webworker踩坑记录
前端·javascript·typescript
山河木马14 小时前
渲染管线-计算得到gl_Position(顶点着色器)之后续GPU流程
javascript·webgl·图形学
竹林81814 小时前
用 The Graph 查询链上数据实战:从手搓 RPC 到 Subgraph,我的 NFT 项目数据加载快了 10 倍
前端·javascript
kyriewen17 小时前
别再每次都 Google 了:我整理了前端日常最常踩的 10 个 Git 坑,附速查表
前端·javascript·git
SmartBoyW18 小时前
深入ECMAScript规范:彻底搞懂JS隐式类型转换与底层ToPrimitive机制
前端·javascript
用户8524950718418 小时前
解密 JavaScript 中的 this:谁才是真正的调用者?
javascript·面试
Heo18 小时前
Vite进阶用法详解
前端·javascript·面试
铁皮饭盒20 小时前
Next.js 风格路由内置?Bun FileSystemRouter 凭啥这么香
javascript
小林ixn21 小时前
别再背八股了!从 5 个真实场景彻底搞懂 JavaScript 的 this
javascript
东风破_21 小时前
JavaScript 面试常考的字符串算法:从反转字符串到回文判断
前端·javascript