JS完美收官——原型和原型链
原型
和原型链
这部分知识会影响到写面试题,或者做一些公共的组件和插件,总之是通用型的一些东西
🍀 原型 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
,称之为函数原型
「灵魂拷问:」
test是个函数,请问test是否有 __proto__
属性?
所有函数都是对象,所以test必须有__proto__
属性
let obj = {}
,请问obj
是否有prototype
属性?
obj
不是函数,只有函数才有prototype
,普通对象是没有prototype
的,但它肯定有__proto__
属性
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
它们是共用一块内存空间。这也是为什么我们把同一个函数写在原型上的原因,因为写在原型上,每个对象都可以使用。看看数组原型上有哪些方法:
这就是在提示我们,将来写构造函数的时候,应该把所有共享的东西写在原型里面。当访问一个对象的成员时,首先会看看对象自身是否拥有该成员,如果有直接使用;如果没有,会在原型链中依次查找是否拥有该成员,如果有直接使用
如果有一天发现函数原型上的方法不够用,我们可以自己写函数添加到原型上,以增强对象的功能,这种方式也叫猴子补丁,猴子补丁会导致原型污染,使用需谨慎。
🌴 原型链
我们先来看一张图,理解了图,原型链也就理解了
我们先看白色的箭头,白色表示函数的原型,每一个函数都有原型,Object函数的原型是Object对象【①】,那么自定义函数也是有原型的,自定义函数的原型也是一个对象【③】。这里有个特殊点:
Function
是直接被添加到内存里面的,Function
指向Function的原型
【②】然后看绿色线条,绿色线条表示
new
,所有的函数都是通过new Function
产生的,Object函数和自定义函数都是如此【④⑤】,自定义函数还会生成自定义对象【⑥】,举个例子🌰://自定义函数Test,通过new产生一个自定义对象obj
function Test(){};
let obj = new Test();再看蓝色的线,表示隐式原型,前面也说了,隐式原型指向创建对象的函数原型,谁创建的它,它就指向谁,自定义函数是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方法。
😊 好了, 以上就是我的分享,小伙伴们点个赞再走吧 👍 支持一下哦~ 😘,我会更有动力的 🤞