• JavaScript原型&原型链到底是什么


    先来张图

    如果这张图能看懂的话,就不需要浪费时间往下看了,直接毕业。


    有点蒙的话没关系,把下面内容搞懂之后,再回头看看。

    先了解两个概念

    什么是构造函数?

            答:如果一个函数通过new 关键字调用的话,这个函数就是构造函数。并且我们规定首字母得大写(当然首字母不大写也没问题但不规范)。通过new关键字调用函数会返回一个对象。记住这一点,很重要。

    什么是对象?

            在我刚学js的时候,我常听到的一句话就是在js中万物皆对象,后来看到一本书《你不知道的js》,才明白这种说法其实是不对的。

            对象是JavaScript的核心概念之一,是一种最常用的数据类型。数据类型可以分为两大类:简单基本类型和复杂基本类型

            简单基本类型(string、number、boolean、null、undefined)本身并不是对象。null有时会被当成一种对象类型,但是这其实是语言本身的一个bug,即对null执行typeof null时会返回字符串”object“。

            原理是这样的,不同的对象在底层都表示为二进制,在JavaScript中二进制前三位都为0的话会被判断为object类型,null的二进制表示是全0,自然前三位也是0,所以执行typeof时会返回“object”。

            js中有许多特殊的对象子类型,我们称之为复杂基本类型。比如函数就是对象的一个子类型(从技术角度来说就是”可调用对象“),包括数组也是对象的一种类型。


    关于原型中的几个关键属性,如下:

    一、prototype

            js中所有 函数 都有一个属性 prototype,指向一个对象,这个对象就是该函数的原型对象

            比如我写了一个函数:function test(){}

            想查看或者操作这个函数的原型,则通过test.prototype得到该函数的原型

            当然,js内置的函数对象的原型也同理,例如,Object.prototype,Function.prototype,Array.prototype,Number.prototype等等。。分别是这些函数的原型对象        

     二、__proto__

            js中所有 对象 (除了null)(包括原型对象,也就是某函数的prototype) 都具有一个__proto__属性,该属性指向了该对象构造函数的原型。

            注意!不是该对象的原型,普通对象自身没有原型,是构造函数的原型,哪个函数构造的该对象,则指向哪个函数的原型

            比如我定义了一个对象:let obj = {} ,或者更直观定义 let obj = new Object();

            想查看或者操作这个对象的原型,则可以通过obj.__proto__得到该对象构造函数的原型。

            因此,obj.__proto__ 就是 Object.prototype。他们指向的是同一个原型对象

            同理,随便定义一个数组对象,let arr = [], arr.__proto__ 就是Array.prototype

            再同理,随便定义一个自定义函数对象,function test(){}, test.__proto__ 就是Function.prototype(这里应该能够发现,函数对象自己也有原型,这是与其他对象不同的地方,别忘了,函数是可以作为构造函数,构建对象的哟)

            知道了这个有什么用呢,可以看下面这波操作

            假如我们在Object.prototype上定义了一个属性,Object.prototype.makabaka = "玛卡巴卡"

            则我们定义的所有对象,let obj1 = {} ; let obj2 = {} ,都可以通过调用__proto__.makabaka,拿到”玛卡巴卡“

            例如:

                    obj1.__proto__.makabaka === "玛卡巴卡"

                    obj2.__proto__.makabaka === "玛卡巴卡"

            你注意啦!这里有个神奇操作,__proto__是可以不写的。

                    obj1.makabaka === "玛卡巴卡"

                    obj2.makabaka === "玛卡巴卡"

            如果不写__proto__的话js会先在obj1自身上找一下有没有makabaka属性,如果js发现obj1上没有makabaka属性,就会通过obj1.__proto__.makabaka,去obj1的原型上找这个属性,这就是传说中的原型链。

    三、constructor

            js中所有 原型对象 (prototype)都有一个constructor属性,该属性指向了原型对象的构造函数。

            既然可以通过函数得到原型,那么也可以通过原型得到构造函数

            还是写一个函数:function test(){}

            他的原型对象为 test.prototype,所以test.prototype.constructor 的结果为 test(){}

            我们使用更多的可能是一个普通对象调用constructor,来通过这个属性判断,是否是某个函数构建的。

            例如:let a = []; a.constucotr 的结果为Array()

            注意了,数组对象a本身是没有constucotr属性的,这个属性是在原型上的,上面讲过了,如果对象本身没有该属性,会获取a.__proto__.constucotr,得到该原型对象的constucotr属性,进一步得到构造函数。

            不过由于prototype是可以随时修改的,不太安全,也不建议用这个来判断,建议使用instanceof来判断某个对象跟构造函数的关系。这里只需要了解他的作用就好。


    我们再来归类总结一下,通过几个实例再来理解一下。

    一、所有函数(包括Function),他们的__proto__属性指向的是Function.prototype(Function函数的原型)。(函数也是对象,因此所有函数都有__proto__属性)

    1. // 下面判断结果都为true,(Object、Number、String等内置函数,都是构造函数,构造函数也是函数)
    2. Object.__proto__ === Function.prototype
    3. Number.__proto__ === Function.prototype
    4. String.__proto__ === Function.prototype
    5. Array.__proto__ === Function.prototype
    6. Function.__proto__ === Function.prototype
    7. function test() {}
    8. test.__proto__ === Function.prototype
    9. ....

    二、所有函数原型(prototype)的__proto__都是指向Object.prototype的

    1. // 下面判断结果都为true
    2. Function.prototype.__proto__ === Object.prototype
    3. Number.prototype.__proto__ === Object.prototype
    4. String.prototype.__proto__ === Object.prototype
    5. Array.prototype.__proto__ === Object.prototype
    6. function test() {}
    7. test.prototype.__proto__ === Object.prototype
    8. ...

    三、自定义对象实例的__proto__指向构造函数的原型(谁创造的我,我就指向谁的原型)

    1. // 下面判断结果都为true
    2. let obj = {}; // let obj = new Object()
    3. obj.__proto__ === Object.prototype
    4. function Test(){}
    5. let obj2 = new Test()
    6. obj2.__proto__ === Test.prototype
    7. let arr = []; //let arr = new Array()
    8. arr.__proto__ === Array.prototype
    9. ...

    四、Object.prototype.__proto__ 指向null

    Object.prototype.__proto__ === null

    将原型与原型链方面的知识凝结成一张图:

    最后,在上面所有例子的基础上,最后再来看一下完整原型链查找过程

    1. Object.prototype.makabaka = "玛卡巴卡"
    2. function Test(){}
    3. let obj = new Test()
    4. obj.makabaka === "玛卡巴卡"
    5. // 查找路径如下
    6. obj.makabaka // 在obj上没找到玛卡巴卡
    7. // obj.__proto__ === Test.prototype
    8. obj.__proto__.makabaka // 在Test.prototype 上没找到玛卡巴卡
    9. // obj.__proto__.__proto__ === Test.prototype.__proto__ === Object.prototype
    10. obj.__proto__.__proto__.makabaka // 终于在Object.prototype上找到了玛卡巴卡

     相信看到这里再看文章开头的图片,就会一目了然了

    当然,关于原型链这部分知识,远不止本文所讲这些,像js中类的概念就是在原型的基础上建立的。

    参考文档:JavaScript之彻底理解原型与原型链 - 掘金

  • 相关阅读:
    线程的基本操作(三)
    【SpringBoot】静态资源导入探究
    微信小程序提示确认框 wx.showModal
    LIN - 基础
    Mybatis-Plus(插件篇 ==> 分页插件
    java_日期时间API
    ml小笔记-不平衡问题
    基于element-plus button 源码分析造轮子
    java计算机毕业设计小说阅读网站源码+系统+数据库+lw文档+mybatis+运行部署
    iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑)
  • 原文地址:https://blog.csdn.net/qq_36647492/article/details/125917497