JS完美收官——原型和原型链

共 3491字,需浏览 7分钟

 ·

2021-08-22 23:40


人生不止有技术
 链接每一位开发者,让编程更有趣儿!
关注


原型原型链这部分知识会影响到写面试题,或者做一些公共的组件和插件,总之是通用型的一些东西

🍀 原型 prototype

「所有的函数都有一个属性prototype,称之为函数原型」。那是不是真的每个函数都有呢?

举个栗子🌰:

当我们创建一个函数的时候会自动加上prototype属性,那么函数又是怎么创建的呢?函数是通过new Function创建的,所以可以认为是在new Function函数对象中自动加了一个属性prototype

举个栗子🌰:

//创建一个函数
function test(){};
//相当于
let test = new Function();//自动加了一个属性prototype原型

原型prototype到底是个什么东西呢?「默认情况下,prototype是一个普通的Object对象」,如图:

那既然prototype是对象的话,那肯定是有属性的,「默认情况下,prototype有一个属性constructor,它也是一个对象,对象存的是一个引用地址,它指向构造函数本身」

瞅张简图方便大家理解:

说明:add是个函数,函数中有个属性prototype,prototype是个对象,可以称为add的原型,add原型中又有一个属性constructor,它指向构造函数add本身。证明下,如图:

那么原型有什么用呢?原型本身没啥用,但是配合隐式原型却大有作为

🍁 隐式原型 __proto__

所有的对象都有一个属性:__proto__,称之为隐式原型。 刚刚在原型中也说了一句话:所有的函数都有一个属性prototype,称之为函数原型

「灵魂拷问:」

  1. test是个函数,请问test是否有__proto__属性?

所有函数都是对象,所以test必须有__proto__属性

  1. let obj = {},请问obj是否有prototype属性?

obj不是函数,只有函数才有prototype,普通对象是没有prototype的,但它肯定有__proto__属性

  1. test是个函数,它肯定有prototype属性,那么请问prototype里面是否有__proto__属性?

默认情况下,prototype是一个普通的Object对象,既然是对象,那必须有__proto__属性

说了这么多,那隐式原型是个啥东西啊?默认情况下,隐式原型指向创建对象的函数原型,然而创建对象的方式一定是通过new出来的,这种形式let obj = {}其实是语法糖,真正是通过let obj = new Object()创建对象的,包括创建函数,也是同理let test = new Function()

举个栗子🌰:

function test({
            
}
let obj = new test();
//obj.__proto__ === test.prototype; 返回true

如果说test函数中返回了一个对象,那么此时情况又不一样了

function test({
     return {};//相当于return new Object()
}
let obj = new test();
//obj.__proto__ === Object.prototype; 返回true

瞅个简图:

一个函数可以产生多个对象,每new一次产生一个对象,图中add函数通过new产生了对象1和对象2,而所有对象中都有一个属性__proto__隐式原型,而隐式原型又指向函数add的原型,从图中我们可以发现一个现象:对象1的__proto__、对象2的__proto__以及函数add的prototype它们是共用一块内存空间。这也是为什么我们把同一个函数写在原型上的原因,因为写在原型上,每个对象都可以使用。看看数组原型上有哪些方法:

这就是在提示我们,将来写构造函数的时候,应该把所有共享的东西写在原型里面。当访问一个对象的成员时,首先会看看对象自身是否拥有该成员,如果有直接使用;如果没有,会在原型链中依次查找是否拥有该成员,如果有直接使用

如果有一天发现函数原型上的方法不够用,我们可以自己写函数添加到原型上,以增强对象的功能,这种方式也叫猴子补丁,猴子补丁会导致原型污染,使用需谨慎。

🌴 原型链

我们先来看一张图,理解了图,原型链也就理解了

  1. 我们先看白色的箭头,白色表示函数的原型,每一个函数都有原型,Object函数的原型是Object对象【①】,那么自定义函数也是有原型的,自定义函数的原型也是一个对象【③】。这里有个特殊点:Function是直接被添加到内存里面的,Function指向Function的原型【②】

  2. 然后看绿色线条,绿色线条表示new,所有的函数都是通过new Function产生的,Object函数和自定义函数都是如此【④⑤】,自定义函数还会生成自定义对象【⑥】,举个例子🌰:

    //自定义函数Test,通过new产生一个自定义对象obj
    function Test(){};

    let obj = new Test();
  3. 再看蓝色的线,表示隐式原型,前面也说了,隐式原型指向创建对象的函数原型,谁创建的它,它就指向谁,自定义函数是Function创建的,那么它就指向Function的原型【⑦】,Obejct也是通过let Object = new Function()创建的,所以Object隐式原型也会指向Function的原型【⑧】,举个例子🌰:

    function Test(){};

    //Test函数其实是通过下面方式创建的

    let Test = new Function();

    //由于所有的对象都是通过函数产生的,那么Test身上一定有__proto__
    Test.__proto__ === Function.prototype;//true

    📢 特殊点:由于Function是直接添加到内存的,没有谁创建它,所以Function__proto__指向自身的prototype【⑨】

    接着再看蓝色箭头【⑩】,自定义对象的隐式原型指向函数的原型,举个例子🌰:

    function Test(){};

    let obj = new Test();

    obj.__proto__ === Test.prototype;//true,自定义对象的隐式原型指向函数的原型

    文章开头有提到:默认情况下,prototype是一个普通的Object对象,所以可以说原型prototype是通过new Object()创建的,于是原型对象的隐式原型都指向Object的原型【⑪⑫】

    📢 特殊点:Object的原型的隐式原型指向null【⑬】,Object.prototype.__proto__ === null

    看段代码:

    function Test(){};
      
    let obj = new Test();

    console.log(obj.toString());

    输出:

    自定一个函数Test,obj本身并没有toString方法,那为什么可以调用toString?这是因为自身没有,会顺着隐式原型到自定义函数原型上查找,若还未找到,继续顺着自定义函数原型的隐式原型向上查找,最终在在Object原型上找到toString方法。

😊 好了, 以上就是我的分享,小伙伴们点个赞再走吧 👍 支持一下哦~ 😘,我会更有动力的 🤞


浏览 10
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报