当前位置: 首页 > news >正文

原型链的理解(全面)

创建一个实例

使用构造函数创建一个实例

function Star(age,name) {
    this.age = age
    this.name = name
    this.sing = function(){
        console.log("我可以唱歌")
    }
}
let ldh = new Star(35,"刘德华")
console.log(ldh)//35,刘德华

理解构造函数里面的内容,构造函数里面是有静态成员和实例成员的

  1. 实例成员就是构造函数内部通过this添加的成员,在这里的sing、name和age就是实例成员,这个只能通过实例对象来访问,不能通过构造函数访问
    console.log(Star.sing)//undefind

  2. 静态成员,就是直接在函数本身上面添加的成员,只能通过构造函数访问
    Star.sex = “男”//这个就是静态成员
    console.log(Star.sex)//男
    console.log(ldh.sex)//undefined

虽然构造函数挺不错的,但是存在一个严重的内存浪费的问题,当我们使用构造函数创建多个实例的时候,如果这个函数里面有多个静态成员的函数,那么我们每一次创建实例对象,构造函数下的方法都会重新开辟一个内存空间去存放这些方法。

但是明明这些方法都是一个构造函数里面的,干嘛要这么麻烦,还要占用内存,所以出现了原型

构造函数里的东西

prototype

构造函数通过原型分配的函数是所有对象所共享的,js规定,每一个构造函数都有一个prototype属性,指向另外一个对象。注意这个prototype既是一个对象,这个对象的所有属性和方法,都是被构造函数所拥有

我们可以把那些不变的方法,直接定义在prototype对象上这样所有的实例都可以共享这个方法

所以上面的方法的改进应该是这样

function Star(age,name) {
    this.age = age
    this.name = name
}
Star.prototype.sing = function(){
    console.log("我会唱歌")
}
let ldh = new Star(35,"刘德华")
ldh.sing//我会唱歌

因为prototype是一个对象,所以我们称构造函数的prototype为原型对象

__proto__

对象都会有一个属性__proto__,指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为有__proto__原型的存在

我们打印这两个看看里面有什么

console.log(ldh)
console.dir(Star)

结果如下
img

我们可以看到ldh这个实例之中是有__proto__的,而构造函数也是有prototype的,那既然说__proto__指向构造函数的prototype对象,那么我们来看看是不是真的

console.log(ldh.__proto === Star.prototype)//true

结果自然是正确的

那既然实例的对象原型(ldh.proto)和构造函数的原型对象(Star.prototype)是一样的,那么我们就可以解释的通为什么实例可以直接使用构造函数上面的方法了

constructor构造函数

我们先打印一点东西看看

console.log(ldh.__proto__)
console.dir(Star.prototype)

结果如下

img
我们可以看到两个里面都有一个constructor对象,那么这个是什么玩意呢

其实这个我们还是称之为构造函数,因为它指向构造函数的本身,

我们可以试试看是不是这样

console.log(ldh.__proto__.constructor)

结果是和预期的一样的

img

所以constructor主要用于记录该对象引用于哪个构造函数,他可以让原型对象指向原来的构造函数

怎么用呢,可以看看这种情况

现在我们的构造函数有很多的方法,但是我们一直用.方法的形式好像有点麻烦,干脆就直接使用对象存储吧

function Star(age, name) {
    this.age = age
    this.name = name
}
Star.prototype = {
    sing:function(){
        console.log("我会唱歌")
    },
    movie:function(){
        console.log("我会演电影")
    }
}
let ldh = new Star(35, "刘德华")
console.log(ldh.__proto__.constructor)

结果如下

img

好像指向的就不是原来Star这个构造函数了,这是怎么回事

原来,Star.prototype本身就是一个对象,你给Star.prototype赋值一个对象,那就会把原来prototype上面的对象给覆盖了,所以这个时候就可以使用constructor了

function Star(age, name) {
    this.age = age
    this.name = name
}
Star.prototype = {
    constructon:Star
    sing:function(){
        console.log("我会唱歌")
    },
    movie:function(){
        console.log("我会演电影")
    }
}
let ldh = new Star(35, "刘德华")
console.log(ldh.__proto__.constructor)

只需要在Star.prototype里面重新指向这个构造函数就解决问题了

三者的关系

看了这么多,我们应该可以理解构造函数、实例、原型对象之间的关系了吧,只有理解了这些,才能更快的理解原型链的内容

我们可以只用一张图表示他们的关系

img

下面开始原型链的理解

原型链

直接看图先:

img

讲解一下图里面的内容

  1. 首先说好了的每一个实例都有__proto__,而这个就指向的是构造函数的对象原型,所以ldh.__proto__和Star.prototype是等价的,即ldh.proto`能够只会Star这个构造函数呢,本质上就是通过Star.prototype指回去的。
  2. 那么我们就会想到Star.prototype的__proto__指向的会是谁呢,一打印就能看的出指向的是Object.prototype,所以不用想也知道他的构造函数肯定是Object了
  3. 那Object.prototype还有没有__proto__,打印一看就知道是为null,所以我们知道原型的终点就是null

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念

了解了原型链,我们再来了解最后一个概念

js的成员查找机制

  1. 当访问一个对象的属性或者方法的时候,首先查找这个对象自身有没有这个属性
  2. 如果没有就去找他的原型,也就是__proto__指向的prototype原型对象
  3. 如果还没有就去查找原型对象的原型,即Object的原型对象
  4. 以此类推一直找到null为止

所以我们经常可以使用一些对象上面的方法就是因为如此

function Star(age, name) {
    this.age = age
    this.name = name
}
Star.prototype.sing =function(){
    console.log("我会唱歌")
}
let ldh = new Star(35, "刘德华")
Object.prototype.sex="男"
console.log(ldh.sex)//男

这个是可以输出Object.prototype上面的sex属性的

我们还知道Object.prototype上面有一个toString方法,但是我们自己写的Star构造函数和ldh对象里面是都没有这个方法的,那么我们还可以使用这个方法吗

测试一下不就知道了

function Star(age, name) {
    this.age = age
    this.name = name
}
Star.prototype.sing =function(){
    console.log("我会唱歌")
}
let ldh = new Star(35, "刘德华")
console.log(ldh.toString())//[object Object]

结果依然是正确的,所以这也验证了我们上面的规则

相关文章:

  • oracle check if the display variable is set
  • git项目创建过程
  • initramfs.img,ramdisk 文件解压与压缩
  • win7 Ubuntu双系统重装win7后Ubuntu引导消失
  • java Service warpper安装说明
  • 奇偶剪枝-优化(ZOJ 2110 , HDU 1010)
  • 全引用与部分引用
  • CodeForces 606C
  • 初探MongoDB:暴力美学
  • POJ 1006(中国剩余定理)
  • c++ algorithm中常用的几个内置函数
  • circularprogressbar/smoothprogressbar开源视图使用学习
  • C/C++和JAVA 实现大数相加
  • 苹果推出开源医学研究框架ResearchKit
  • 康拓展开及其逆运算和全排列函数
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • gops —— Go 程序诊断分析工具
  • GraphQL学习过程应该是这样的
  • httpie使用详解
  • js
  • MQ框架的比较
  • React+TypeScript入门
  • React的组件模式
  • React-生命周期杂记
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • Vue.js-Day01
  • 搭建gitbook 和 访问权限认证
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 关于Flux,Vuex,Redux的思考
  • 讲清楚之javascript作用域
  • 京东美团研发面经
  • 微服务框架lagom
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​2021半年盘点,不想你错过的重磅新书
  • ​MySQL主从复制一致性检测
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .Net7 环境安装配置
  • .Net多线程总结
  • .NET命令行(CLI)常用命令
  • .NET委托:一个关于C#的睡前故事
  • /etc/shadow字段详解
  • [1159]adb判断手机屏幕状态并点亮屏幕
  • [16/N]论得趣
  • [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法
  • [delphi]保证程序只运行一个实例
  • [EULAR文摘] 利用蛋白组学技术开发一项蛋白评分用于预测TNFi疗效
  • [GDOUCTF 2023]<ez_ze> SSTI 过滤数字 大括号{等
  • [hive小技巧]同一份数据多种处理