06 原型链图解

1 回顾原型的特点

  • 每个函数都有一个prototype属性, 它默认指向一个Object空对象(即称为: 原型对象)
  • 原型对象中有一个属性constructor, 它指向函数对象
  • 每个函数function都有一个prototype,即显式原型
  • 每个实例对象都有一个__proto__,可称为隐式原型
  • 对象的隐式原型的值为其对应构造函数的显式原型的值
  • 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
  • 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值

2 图解原型链

function Fn() {
    this.method1 = function () {
        console.log("method1")
    }
}
Fn.prototype.method2 = function () {
    console.log("method2")
}
var fn = new Fn()
fn.method1();
fn.method2();
console.log(fn.toString())
fn.method3();

下面就一点一点的分析:

function Fn() {
    this.method1 = function () {
        console.log("method1")
    }
}

上面这部分代码的内存分析:

  • 堆空间会开辟一个空间,保存的就是Fn函数对象,假设内存地址为0x123
  • 由于每个函数都有一个prototype属性, 它默认指向一个Object函数的一个实例对象(保存了Object函数的一个实例对象的地址),假设这个地址为0x234
  • 栈空间中会声明一个变量指向0x123这块内存空间
Jietu20210316-210812.jpg

在这就产生一个问题,Fn函数对象的prototype属性指向一个Object函数的实例对象,而
每个实例对象都有一个proto(隐式原型),并且这个对象的隐式原型的值为其对应函数对象的显式原型的值

也就说当前的这个Object函数的实例对象有一个__proto__属性,指向了Object函数的显示原型对象(保存了Object函数的显式原型对象的地址)

那这个Object函数对象是什么时候加载的?js引擎在一开始的时候就加载了这个函数对象:

Jietu20210316-214124.jpg

接下来这部分代码

Fn.prototype.method2 = function () {
    console.log("method2")
}

这个就比较简单了,就是在Fn的原型对象中添加了一个属性method2

Jietu20210316-215152.jpg

简单说明:这里对method2做了简化,他俩也是函数对象

var fn = new Fn()

也比较简单,就创建了一个Fn的实例对象。需要注意两点:

  • 在调用构造函数的时候,会给这个实例对象添加一个method1的属性
  • Fn的实例对象的proto属性指向其构造函数的原型对象
Jietu20210316-215606.jpg
fn.method1();
fn.method2();
console.log(fn.toString())
fn.method3();
  • 当调用method1的时候,发现在自己的实例对象中有该方法,那么就会调用实例对象中的method1
  • 当调用method2的时候,发现在自己的实例对象中没有该方法,那么就会去原型中寻找,发现存在method2,就会调用原型对象中的method2
  • 当调用toString()的时候,发现在自己的实例对象中没有该方法,那么就会去原型中寻找,发现自己的原型对象中也不存在,就会去原型对象的原型对象(Object的原型对象),发现Object的原型对象存在toString()方法
    就会去执行这个toString()方法
  • 当调用method3()的时候,发现在自己的实例对象中没有该方法,那么就会去原型中寻找,发现自己的原型对象中也不存在,就会去原型对象的原型对象(Object的原型对象),发现Object的原型对象也不存在method3方法(undefined),在调用的时候就会报错( fn.method3 is not a function)

总结:
1. 函数对象的原型对象就是Object的一个实例对象,所以函数对象的prototype的proto属性就是Object的原
型对象

2. 函数对象的实例的proto属性是当前函数对象的原型对象
3. Object的原型对象的proto属性是null,不存在Object的原型对象的原型对象

3 Object的原型对象有哪些方法呢?

function Fn() {
    this.method1 = function () {
        console.log("method1")
    }
}
// 如何找Object的原型对象呢: Fn.prototype就是一个Object的实例对象,所以Fn.prototype的__proto__属性就是Object的原型对象
console.log(Fn.prototype.__proto__)
Jietu20210316-220742.jpg

4 验证Object的原型对象的原型是null

function Fn() {
    this.method1 = function () {
        console.log("method1")
    }
}
// 如何找Object的原型对象呢: Fn.prototype就是一个Object的实例对象,所以Fn.prototype的__proto__属性就是Object的原型对象
console.log(Fn.prototype.__proto__)

console.log(Fn.prototype.__proto__.__proto__) // 输出是null

5 原型对象中的constructor属性

刚刚上面打印Object的原型对象的constructor属性就是Object对象的构造函数:


function Fn() {
  
}
// 如何找Object的原型对象呢: Fn.prototype就是一个Object的实例对象,所以Fn.prototype的__proto__属性就是Object的原型对象
console.log(Fn.prototype.__proto__)

console.log(Fn.prototype.__proto__.constructor === Object) // true

函数的原型的constructor又会指向其函数对象

 // 定义一个Fn的函数对象   
function Fn() {
    
}
// 函数的原型的constructor又会指向其函数对象
console.log(Fn.prototype.constructor === Fn) // true

var fn = new Fn();
// 函数的原型的constructor又会指向其函数对象
console.log(fn.__proto__.constructor === Fn) // true
未命名文件(53).png

6 Function函数对象的原型

function Fn() {
  
}

// 上面的也可以写为这样,
var Fn = new Function();

这里就出现了,函数对象都是Function函数的实例了,而所有的实例对象的有隐式原型(__proto__)保存的就是其函数对象的显式原型,也就是说Fn函数对象的__proto__保存的就是Function的原型对象

同样Object的构造函数也是Function函数的实例了,所以Object函数对象的__proto__保存的就是Function的原型对象

上面的图又可以进化了

未命名文件(54).png

总结:
所有函数对象的__proto__都是Function显式原型
Function是其自身的实例,上图可以看出Function.prototype = Function.__proto__

function Fn1() {
this.method1 = function () {
    console.log("method1")
}
}

var Fn2 = Function();
// 所有函数对象的`__proto__`都是Function显式原型
console.log(Fn2.__proto__ === Fn1.__proto__) // true
console.log(Fn1.__proto__ === Function.prototype) // true

7 总结

  1. 函数的显式原型指向的对象默认是Object的实例对象,但是Object除外
function Fn() { }
console.log(Fn.prototype instanceof Object)// true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object)// true

需要需要纠正上面图的一个错误


未命名文件(54).png
  1. Function是他自身的实例:Function = new Function();
  2. 所有函数都是Function的实例,所以:
// 每个实例的__proto__都指向其构造函数的的prototype,而Function就是自己的实例
console.log(Function.prototype === Function.__proto__) // true
  1. Object的原型对象是原型链的尽头

 console.log(Object.prototype.__proto__) // null
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容

  • JS中原型链,说简单也简单。 首先明确: 函数(Function)才有prototype属性,对象(除Object...
    前小白阅读 3,931评论 0 9
  • JS中原型链,说简单也简单。 首先明确: 函数(Function)才有prototype属性,对象(除Object...
    亚讯阅读 4,762评论 1 8
  • 根据自己手绘的简图更进一步理解JavaScript的原型链机制 原型与隐式原型 概念 原型:所有的 函数 都有一个...
    MonkeyDwwl阅读 587评论 0 2
  • 注解 红虚线即原型链,各类实例都是通过原型链继承到各数据类型本身的方法和属性。比如Object原型对象包含着对象的...
    漓漾li阅读 1,388评论 0 2
  • 使用颜色、形状可视化Javascript中抽象的原型链概念: 橙黄色实心代表函数,函数是可执行的对象; 橘红色圈圈...
    帅华君阅读 306评论 0 3