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

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

相关推荐
一只叁木Meow3 小时前
电商 SKU 选择器:用算法实现优雅的用户交互
前端·javascript·算法
代码煮茶3 小时前
Vue3 Mock 数据实战 | 用 Mockjs + vite-plugin-mock 搭建前端独立开发环境
javascript·vue.js
JieE2123 小时前
反转链表:从双指针到递归,吃透链表反转的核心逻辑
javascript·算法
码银4 小时前
在若依中如何新建一个模块(图文教程)
java·javascript
OrangeForce4 小时前
Monknow 书签导出工具:从本地存储提取数据并转为标准 HTML 书签
javascript·chrome·python·edge·html·firefox
mCell4 小时前
JavaScript:从事件循环到手写 Promise
javascript·面试·浏览器
芯芯点灯5 小时前
gd32f303烧录提示Flash Timeout. Reset the Target and try it again.;
开发语言·前端·javascript
放下华子我只抽RuiKe55 小时前
React 从入门到生产(七):性能优化实战
前端·javascript·人工智能·react.js·性能优化·前端框架·github
范同学~5 小时前
多个表单如何用element ui 校验
javascript·vue.js·ui