发布网友 发布时间:2024-09-30 11:06
共1个回答
热心网友 时间:2024-09-30 11:49
1前言写了几篇vue的源码注释(并不算解析...),感觉到了对原型的理解是不够的,在js中,原型是非常重要的,只要你想在js这座山上往上爬,它就会嘲笑你,你把我搞会了么?如果没有,它就给你加个十倍重力.如果搞懂了,那肯定是能月薪过万,赢取白富美,走向人生巅峰的啦~~~
这篇文章讲的都是我自己的理解,应该是原创的(我有99%把握,除非是我之前看过文章记到脑子里了,没法给到引用了,联系我可以加上),但是如果有人借鉴我的这篇文章,希望给到一个这篇文章的链接.其实我只是想我的文章能有更多的阅读量,我想月薪过万,赢取白富美,走向人生巅峰~~~
2前置知识点2.1数据类型js共有7种数据类型
从可不可以读取属性,可以分为两类
可以读取属性:
自身可以有属性:object
自身不可以有属性:string,number,boolean,symbol
不可以读取属性:null,undefined
null,undefined类型,读取和设置属性都是非法的,直接报错.
只有object能有自有属性,可以读取属性和设置属性
string,number,boolean,symbol类型可以读取属性,其实是先构造成包装对象,再读取属性,设置属性也是一样,可以理解设置到了会立即销毁的包装对象上,就是可以设置,但是没有任何实质效果.
2.2判断是否是自身属性(hasOwnProperty)hasOwnProperty方法是继承来的,用来判断该对象自身上是否有这个属性,有就行,不管是什么值
constobj={a:1}consto=Object.create(obj)o.b=1o.c=void0console.log('a',o.a,o.hasOwnProperty('a'))//可以读取到值,继承而来,但不是自身属性console.log('b',o.b,o.hasOwnProperty('b'))//可以读取到值,自身属性console.log('c',o.c,o.hasOwnProperty('c'))//读取到undefined,自身属性console.log('d',o.d,o.hasOwnProperty('d'))//读取到undefined,不是自身属性,也没有继承到这个属性3一点小思考程序就是数据结构与算法,好的程序最好是使用最小的内存存储数据,使用最快的时间完成运行得到结果.
复用数据可以达到减少内存使用的目的,例如a和b需要完成一样的功能,就可以复用同一个方法(属性).
那么就需要解决一个问题,这个复用的方法存在哪里,a和b怎样能找到它.
在js中的解决方案是,a和b都由函数(这里先叫Function吧)构造而来,复用的方法存放在函数身上(prototype属性里).
(因为只有构造函数身上需要存放复用的方法,所以prototype只有可构造的函数上才有,箭头函数不是用来构造的,它就没有,其它对象,如果连函数都不是,就更不会有这个属性了)
那么需要给a,b和Function建立起联系,因为a,b需要到Function身上找它们可以用的复用方法
在js中的实现是通过constructor属性,即a.constructor,b.constructor可以找到Function
所以通过a.constructor.prototype可以找到它可以复用的方法的存放地址,为了快速找到js提供了一种快捷方法a.__proto__一步到位找到,即a.constructor.prototype和a.__proto__找到的是同一个对象,当然它俩是全等的啦.
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false(所以,如果手动修改了constructor,prototype,__proto__的指向,那么你得清楚你在干什么)
(我不知道js的设计者是不是这样想的,哈哈,我就这样认为,这样好理解多了)
(这个过程称之为继承,而且是一个链式过程,即可以a.constructor.prototype.constructor.prototype.constructor.prototype这样查找,直到找到最顶端,这个过程可以由a.__proto__.__proto__.__proto__加速,这个就叫做原型链,js的继承只有这一种实现方式.)
(上面只是引导思考过程,其实查找原型对象并不会通过a.constructor.prototype去找,而是直接通过__proto__查找)
3.1修改constructorconstDog=function(){}constdog=newDog()dog.constructor=0console.log(dog.hasOwnProperty('constructor'))//trueconsole.log(dog.constructor)//0console.log(dog.__proto__.constructor)//[Function:Dog]总结,修改了这个属性,增加了找到构造它的构造函数的难度,不能直接获取了,需要到原型对象上去读取.
如果它自身的这个属性和原型上的这个属性都被修改了,那么也只是找不到它的构造函数了而已,不会有别的影响.
3.1.1instanceofinstanceof关心的是原型链,跟constructor没有关系
印证上面的点,修改constructor属性,除了让实例找不到构造它的构造函数,没有别的影响了.如果需要通过实例找到它的构造函数,就需要维护好它俩的关系.
//语法是//ainstanceofb//这个操作符是判断a的原型链上是否有b.prototype,因为要判断b.prototype所以b必需是一个可构造的函数,否则会报错constfn=function(){}consto=Object.create(fn.prototype)//此时o的原型链上有fn.prototype,因为o.__proto__===fn.prototypeconsole.log(oinstanceoffn)//trueconstemptyObj={}fn.prototype=emptyObj//此时o的原型链上已经没有fn.prototype了,因为此时o.__proto__已经不再和fn.prototype相等了console.log(oinstanceoffn)//falseo.__proto__=emptyObj//修正了o.__proto__就好了console.log(oinstanceoffn)//true3.1.2isPrototypeOf现在有个新的api,实现的功能和instanceof一致,但是更加语义化一些,直接判断对象是否在另一个对象的原型链上
constfn=function(){}consto=Object.create(fn.prototype)console.log(fn.prototype.isPrototypeOf(o))//true3.2修改__proto__|prototype先说一个总结,在构造实例的时候,会将这个实例的__proto__指向此时的构造函数的prototype,然后实例实际是继承的是__proto__.(为什么强调此时,因为构造函数的prototype可能会被修改指向,修改之后只会影响修改之后构造的实例,修改之前构造的实例还会使用修改之前的prototype)
所以,就可以理解到修改__proto__和prototype会有哪些影响了
修改__proto__的指向
只会影响它自己的继承
constDog=function(){}constdog=newDog()constd=newDog()Dog.prototype.name='Dog'dog.__proto__={name:'__proto__',}console.log(d.name)//Dogconsole.log(dog.name)//__proto__修改__proto__的属性
会影响这一波段构造的实例
constDog=function(){}constdog=newDog()constd=newDog()Dog.prototype.name='Dog'console.log(d.name)//Dogconsole.log(dog.name)//DogDog.prototype={name:'after',}constdog1=newDog()constd1=newDog()console.log(d1.name)//afterconsole.log(dog1.name)//afterdog1.__proto__.name='__proto__'//可以看到只影响了当前这一段构造的实例,之前和之后的都不会被影响到,因为这一段内的是同一个Dog.prototype,它们的__proto__都是指向它的console.log(d1.name)//__proto__console.log(dog1.name)//__proto__Dog.prototype={name:'new',}constdog2=newDog()constd2=newDog()console.log(d2.name)//newconsole.log(dog2.name)//new修改prototype的指向
会影响这一波段构造的实例
修改prototype的属性
会影响这一波段构造的实例,同修改__proto__的属性
4修改和获取原型对象的方式4.1修改上面已经讲了修改prototype和__proto__
4.1.1Object.createconstobj={name:'objName',}consto=Object.create(obj)//它相当于o.__proto__=obj,但是推荐使用`Object.create`console.log(o.name)//objNameconsole.log(o.__proto__===obj)//true4.1.2Object.setPrototypeOfconstobj={name:'objName',}consto={}Object.setPrototypeOf(o,obj)//它相当于o.__proto__=obj,但是推荐使用`Object.setPrototypeOf`constproto=Object.getPrototypeOf(o)console.log(proto===obj&&proto===o.__proto__)//trueconstobj1={}o.__proto__=obj1constproto1=Object.getPrototypeOf(o)console.log(proto1===obj1&&proto1===o.__proto__)//true总结,在什么时候使用Object.create,在什么时候使用Object.setPrototypeOf呢,首先它俩都是标准api,都是建议使用的,在创建对象的时候就要指定原型时使用Object.create,需要动态修改原型对象时,使用Object.setPrototypeOf
4.2获取之前已经讲了,通过constructor.prototype和__proto__获取了
4.2.1Object.getPrototypeOfconstobj={name:'objName',}consto={}Object.setPrototypeOf(o,obj)constproto=Object.getPrototypeOf(o)console.log(proto===obj&&proto===o.__proto__)//true5js内置原生构造函数这些原生的构造函数的prototype属性是不可写,不可枚举,不可配置的
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false05.1js继承的最顶端是什么null,必须是这家伙,不然只能无限套娃了
然后其它所有对象都是从Object构造而来,所以所有的对象都可以继承到Object.prototype.
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false15.2js继承的二等公民(Function)在上面的小思考中,说到,js对象都是函数构造而来,所以包括Object也是由Function构造来的,甚至它自己都是由自己构造而来
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false2我再来一点小理解,可能是在js内部做了小处理,第一个Function是凭空变出来的....然后这个Function构造出了Object,然后这个Object构造出了第一个原型对象Object.prototype,然后再去修改一些引用关系.
其实最复杂的是Object和Function的关系
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false35.3js继承的三等公民(内置的其他构造函数)//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false46用户定义的特定公民构造函数这个才是重点,根据上面的理解,我会再开一篇文章写一下我理解的js的继承,这里就先留个坑
7.总结这篇文章跟网上大多讲constructor,prototype,__proto__的文章都有所不同,我的立足点是从给定的一个可以读取属性的值开始,在js中,除了null和undefined,其它所有的值都可以成为立足点.从这个立足点开始,它的__proto__属性记录了它的原型对象,这个原型对象是构造它时,它的构造函数的prototype属性的值。
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false5读取一个值的属性的值时,如果它自身有这个属性,那么直接返回这个属性的值,否则就会到它的__proto__对象上去找,一直递归下去,直到找到顶部null,找到就返回它的值,没找到就返回undefined
这篇文章有三个理解点,让我茅塞顿开,都是在我试验了好久突然得到的结论:
以一个值为立足点开始分析
在构造实例的时候,会将这个实例__proto__指向此时的构造函数的prototype
查找原型对象时,以__proto__为准
热心网友 时间:2024-09-30 11:47
1前言写了几篇vue的源码注释(并不算解析...),感觉到了对原型的理解是不够的,在js中,原型是非常重要的,只要你想在js这座山上往上爬,它就会嘲笑你,你把我搞会了么?如果没有,它就给你加个十倍重力.如果搞懂了,那肯定是能月薪过万,赢取白富美,走向人生巅峰的啦~~~
这篇文章讲的都是我自己的理解,应该是原创的(我有99%把握,除非是我之前看过文章记到脑子里了,没法给到引用了,联系我可以加上),但是如果有人借鉴我的这篇文章,希望给到一个这篇文章的链接.其实我只是想我的文章能有更多的阅读量,我想月薪过万,赢取白富美,走向人生巅峰~~~
2前置知识点2.1数据类型js共有7种数据类型
从可不可以读取属性,可以分为两类
可以读取属性:
自身可以有属性:object
自身不可以有属性:string,number,boolean,symbol
不可以读取属性:null,undefined
null,undefined类型,读取和设置属性都是非法的,直接报错.
只有object能有自有属性,可以读取属性和设置属性
string,number,boolean,symbol类型可以读取属性,其实是先构造成包装对象,再读取属性,设置属性也是一样,可以理解设置到了会立即销毁的包装对象上,就是可以设置,但是没有任何实质效果.
2.2判断是否是自身属性(hasOwnProperty)hasOwnProperty方法是继承来的,用来判断该对象自身上是否有这个属性,有就行,不管是什么值
constobj={a:1}consto=Object.create(obj)o.b=1o.c=void0console.log('a',o.a,o.hasOwnProperty('a'))//可以读取到值,继承而来,但不是自身属性console.log('b',o.b,o.hasOwnProperty('b'))//可以读取到值,自身属性console.log('c',o.c,o.hasOwnProperty('c'))//读取到undefined,自身属性console.log('d',o.d,o.hasOwnProperty('d'))//读取到undefined,不是自身属性,也没有继承到这个属性3一点小思考程序就是数据结构与算法,好的程序最好是使用最小的内存存储数据,使用最快的时间完成运行得到结果.
复用数据可以达到减少内存使用的目的,例如a和b需要完成一样的功能,就可以复用同一个方法(属性).
那么就需要解决一个问题,这个复用的方法存在哪里,a和b怎样能找到它.
在js中的解决方案是,a和b都由函数(这里先叫Function吧)构造而来,复用的方法存放在函数身上(prototype属性里).
(因为只有构造函数身上需要存放复用的方法,所以prototype只有可构造的函数上才有,箭头函数不是用来构造的,它就没有,其它对象,如果连函数都不是,就更不会有这个属性了)
那么需要给a,b和Function建立起联系,因为a,b需要到Function身上找它们可以用的复用方法
在js中的实现是通过constructor属性,即a.constructor,b.constructor可以找到Function
所以通过a.constructor.prototype可以找到它可以复用的方法的存放地址,为了快速找到js提供了一种快捷方法a.__proto__一步到位找到,即a.constructor.prototype和a.__proto__找到的是同一个对象,当然它俩是全等的啦.
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false(所以,如果手动修改了constructor,prototype,__proto__的指向,那么你得清楚你在干什么)
(我不知道js的设计者是不是这样想的,哈哈,我就这样认为,这样好理解多了)
(这个过程称之为继承,而且是一个链式过程,即可以a.constructor.prototype.constructor.prototype.constructor.prototype这样查找,直到找到最顶端,这个过程可以由a.__proto__.__proto__.__proto__加速,这个就叫做原型链,js的继承只有这一种实现方式.)
(上面只是引导思考过程,其实查找原型对象并不会通过a.constructor.prototype去找,而是直接通过__proto__查找)
3.1修改constructorconstDog=function(){}constdog=newDog()dog.constructor=0console.log(dog.hasOwnProperty('constructor'))//trueconsole.log(dog.constructor)//0console.log(dog.__proto__.constructor)//[Function:Dog]总结,修改了这个属性,增加了找到构造它的构造函数的难度,不能直接获取了,需要到原型对象上去读取.
如果它自身的这个属性和原型上的这个属性都被修改了,那么也只是找不到它的构造函数了而已,不会有别的影响.
3.1.1instanceofinstanceof关心的是原型链,跟constructor没有关系
印证上面的点,修改constructor属性,除了让实例找不到构造它的构造函数,没有别的影响了.如果需要通过实例找到它的构造函数,就需要维护好它俩的关系.
//语法是//ainstanceofb//这个操作符是判断a的原型链上是否有b.prototype,因为要判断b.prototype所以b必需是一个可构造的函数,否则会报错constfn=function(){}consto=Object.create(fn.prototype)//此时o的原型链上有fn.prototype,因为o.__proto__===fn.prototypeconsole.log(oinstanceoffn)//trueconstemptyObj={}fn.prototype=emptyObj//此时o的原型链上已经没有fn.prototype了,因为此时o.__proto__已经不再和fn.prototype相等了console.log(oinstanceoffn)//falseo.__proto__=emptyObj//修正了o.__proto__就好了console.log(oinstanceoffn)//true3.1.2isPrototypeOf现在有个新的api,实现的功能和instanceof一致,但是更加语义化一些,直接判断对象是否在另一个对象的原型链上
constfn=function(){}consto=Object.create(fn.prototype)console.log(fn.prototype.isPrototypeOf(o))//true3.2修改__proto__|prototype先说一个总结,在构造实例的时候,会将这个实例的__proto__指向此时的构造函数的prototype,然后实例实际是继承的是__proto__.(为什么强调此时,因为构造函数的prototype可能会被修改指向,修改之后只会影响修改之后构造的实例,修改之前构造的实例还会使用修改之前的prototype)
所以,就可以理解到修改__proto__和prototype会有哪些影响了
修改__proto__的指向
只会影响它自己的继承
constDog=function(){}constdog=newDog()constd=newDog()Dog.prototype.name='Dog'dog.__proto__={name:'__proto__',}console.log(d.name)//Dogconsole.log(dog.name)//__proto__修改__proto__的属性
会影响这一波段构造的实例
constDog=function(){}constdog=newDog()constd=newDog()Dog.prototype.name='Dog'console.log(d.name)//Dogconsole.log(dog.name)//DogDog.prototype={name:'after',}constdog1=newDog()constd1=newDog()console.log(d1.name)//afterconsole.log(dog1.name)//afterdog1.__proto__.name='__proto__'//可以看到只影响了当前这一段构造的实例,之前和之后的都不会被影响到,因为这一段内的是同一个Dog.prototype,它们的__proto__都是指向它的console.log(d1.name)//__proto__console.log(dog1.name)//__proto__Dog.prototype={name:'new',}constdog2=newDog()constd2=newDog()console.log(d2.name)//newconsole.log(dog2.name)//new修改prototype的指向
会影响这一波段构造的实例
修改prototype的属性
会影响这一波段构造的实例,同修改__proto__的属性
4修改和获取原型对象的方式4.1修改上面已经讲了修改prototype和__proto__
4.1.1Object.createconstobj={name:'objName',}consto=Object.create(obj)//它相当于o.__proto__=obj,但是推荐使用`Object.create`console.log(o.name)//objNameconsole.log(o.__proto__===obj)//true4.1.2Object.setPrototypeOfconstobj={name:'objName',}consto={}Object.setPrototypeOf(o,obj)//它相当于o.__proto__=obj,但是推荐使用`Object.setPrototypeOf`constproto=Object.getPrototypeOf(o)console.log(proto===obj&&proto===o.__proto__)//trueconstobj1={}o.__proto__=obj1constproto1=Object.getPrototypeOf(o)console.log(proto1===obj1&&proto1===o.__proto__)//true总结,在什么时候使用Object.create,在什么时候使用Object.setPrototypeOf呢,首先它俩都是标准api,都是建议使用的,在创建对象的时候就要指定原型时使用Object.create,需要动态修改原型对象时,使用Object.setPrototypeOf
4.2获取之前已经讲了,通过constructor.prototype和__proto__获取了
4.2.1Object.getPrototypeOfconstobj={name:'objName',}consto={}Object.setPrototypeOf(o,obj)constproto=Object.getPrototypeOf(o)console.log(proto===obj&&proto===o.__proto__)//true5js内置原生构造函数这些原生的构造函数的prototype属性是不可写,不可枚举,不可配置的
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false05.1js继承的最顶端是什么null,必须是这家伙,不然只能无限套娃了
然后其它所有对象都是从Object构造而来,所以所有的对象都可以继承到Object.prototype.
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false15.2js继承的二等公民(Function)在上面的小思考中,说到,js对象都是函数构造而来,所以包括Object也是由Function构造来的,甚至它自己都是由自己构造而来
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false2我再来一点小理解,可能是在js内部做了小处理,第一个Function是凭空变出来的....然后这个Function构造出了Object,然后这个Object构造出了第一个原型对象Object.prototype,然后再去修改一些引用关系.
其实最复杂的是Object和Function的关系
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false35.3js继承的三等公民(内置的其他构造函数)//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false46用户定义的特定公民构造函数这个才是重点,根据上面的理解,我会再开一篇文章写一下我理解的js的继承,这里就先留个坑
7.总结这篇文章跟网上大多讲constructor,prototype,__proto__的文章都有所不同,我的立足点是从给定的一个可以读取属性的值开始,在js中,除了null和undefined,其它所有的值都可以成为立足点.从这个立足点开始,它的__proto__属性记录了它的原型对象,这个原型对象是构造它时,它的构造函数的prototype属性的值。
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={}console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false5读取一个值的属性的值时,如果它自身有这个属性,那么直接返回这个属性的值,否则就会到它的__proto__对象上去找,一直递归下去,直到找到顶部null,找到就返回它的值,没找到就返回undefined
这篇文章有三个理解点,让我茅塞顿开,都是在我试验了好久突然得到的结论:
以一个值为立足点开始分析
在构造实例的时候,会将这个实例__proto__指向此时的构造函数的prototype
查找原型对象时,以__proto__为准