• 理解JS原型链


    前言

    • 感谢大佬 一张图搞定JS原型&原型链 的文章,本文基于大佬的文章而作。作为前置知识,你应该阅读它。
    • 作为一名入门前端,肯定要学会 JavaScript ,你可能是被 JS 原型、原型链、构造函数这些概念困惑。那么希望本文内容可以帮忙到你。

    一张图搞定

    原型链图

    很简单,只要你把这张图看懂,我想你对原型就有了大致的理解了。

    不过,你首先应知道一些东西:

    • 函数 Function 就是 对象 Object 的一种
    • 任何函数都可以作为构造函数,不过只要通过 new 关键字调用的才是构造函数
    • constructor 是构造器,prototype 是显式原型,__ proto __ 是隐式原型

    通过这张图的关系,只要箭头可以指向的目标,你都可以通过 constructor、__ proto __、prototype 去索引。

    比如,你想用 p1 对象去表示 Function.prototype ,从图中,你就有若干种方式表达:
    1)p1.constructor.__ proto __
    2)p1.__ proto __ .constructor. __ proto __
    3)p1.constructor.constructor.__ proto __
    ……

    甚至你可以不惜耐心地绕一个、二个……N个循环来达到目的,本质上是一种等价替换。

    实例

    下面以一个实例来直观理解,首先在 console 控制台中,定义一个 Parent 函数,以 Parent 作为构造函数 new 一个 p1 对象并添加成员变量 name、age。

    prototype

    prototype 属性可以看成是一块特殊的共享存储空间,存储可供子孙对象们使用的方法和属性。JavaScript 不像其它基于 Class 语言,每次新建一个实例对象就复制一份成员和方法,相反 JavaScript 使用的是对象默认引用原型对象(prototype)的成员和方法。

    那你可能要问了,那每个实例对象的成员和方法不都一样了吗?

    是的,在你没有对实例对象进行单独的成员和方法修改操作时是这样的。

    • 但如果你在某个实例对象有新增原型对象没有的方法及成员时,那么这些更改也只会针对当前实例对象而言
    • 如果原型对象中存在的方法及成员,那么实例成员和方法将“覆盖”原型对象中的方法和成员(严格来说不是覆盖,按照下文 __ proto __ 章节中谈到的实例成员和方法调用优先级来查找,即 实例对象->原型函数->Object原型函数->null

    来一组案例吧
    p1 对象事先添加了 name、age 成员

    添加 Parent.prototype.name='demo',会怎样呢

    可以看到,程序朝着我们预期的结果执行

    __ proto __

    你可以使用大佬文章中生动的例子去理解 __ proto __ , 它就是指向原型对象 prototype 的指针,意在指出一个对象的原型对象是谁。

    打印 p1, 可以看到只有一个成员 [[Prototype]]: Object,那它是什么呢,再打印 p1.__ proto __ ,对比发现两者是同一个对象,也就是 [[Prototype]] 表示的就是原型对象。因此,在 p1 打印输出中 [[Prototype]] 就是 p1 的原型对象 Parent.prototype。

    需要清楚:

    • __ proto __ 是指向该对象的原型对象,每个对象都有 __ proto __ 属性,通过访问 __ proto __ 就可以访问到该对象的原型对象,即在 console 控制台中打印的 [[Prototype]]:Object 成员
    • 每个实例对象(p1)都有一个私有属性(即隐式原型 __ proto __ )指向了该对象的构造函数( constructor )的显式原型( prototype ),需要注意的是这里的 一个对象 是指非原型对象,也就是非 *.prototype 对象,在此案例意指 p1、Parent()、Object()、Function()
    • 而非 Object.prototype 的原型对象的原型对象 ( __ proto __ ) 则是统一指向 Object.prototype 根原型对象
    • __ proto __ 最终会沿着原型链指向 Object.prototype,然后由 Object.prototype 指向 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

    那么, __ proto __ 在原型链充当什么的作用呢。你有想过我们定义了 p1 对象,没有定义 toString()方法,那怎么能调用 p1.toString() 呢。

    这便是通过 __ proto __ 来一层一层指向对象的原型对象,调用 p1.toString() 的时候,先在 p1 对象本身寻找,没有找到则通过 p1. __ proto __ 找到了原型对象 Parent.prototype,也没有找到,又通过Parent.prototype. __ proto __ 找到了上一层原型对象Object.prototype,在这一层找到了 toString 方法,返回该方法供 p1 对象调用。

    最终指向的是,Object.prototype 原型对象上的 toString() 方法,倘若层层寻找到 Object.prototype 原型对象,而 Object.prototype 原型对象上也没有预期的成员或方法,则返回 undefined。

    constructor

    constructor 表明是谁创造了自己,自己是由何种函数构造而成。constructor 是对象才有的属性,这是毋庸置疑的。
    p1 对象的构造函数是 Parent(),那么 Parent() 的构造函数又是谁。

    可以看出,Parent() 的构造函数是 Function(),而 Function() 的构造函数就是自身,Function() 是所有函数的根构造函数。

    总结

    总结下来就是那张原型链图,只要配合文字把图片框架多理解几遍就清楚了
    一次看完不懂,就多看几遍来理解
    还注意的是,在这个知识框架中,没有任何一个知识点是孤立的,一定结合理解

    最后的最后,本文为个人理解,可能有所出入,如有什么不当之处,望大佬们不吝赐教!

    参阅

  • 相关阅读:
    LR问题集锦(二)
    报错“you do not have a license for this Vuser type”
    LoadRunner:Error 10344
    loadrunner 录制脚本后登陆用户名是乱码
    经典语句
    jprofiler
    数据类测试的一些方法
    中文页面显示和英文页面显示,不一样的语言显示,会对性能有影响哇
    LoadRunner问题集锦
    用loadrunner做压力测试,怎样把应用服务器压死
  • 原文地址:https://www.cnblogs.com/notfound/p/15143011.html
Copyright © 2020-2023  润新知