__proto__和prototype

提到javascript,不得不说到原型链:当在自身找不到某个属性时,则会到上一层原型查找,通过此机制可以实现继承。

 

1,表面的prototype,隐藏的__proto__

先看简单的原型链:

 

这里的a自身没有定义toString方法,但是通过到Object.prototype(原型链的顶层)上找到toString方法,从而正确的输出了。

那a是通过哪个属性追查到Object.prototype的呢?

再看一个经典的原型继承:

 

看到这,貌似就是通过指定prototype属性往上追溯的。别急,看回第一个例子,

 

这个时候,A.prototype已经被置为null,但是toString方法还是能正常调用,说明prototype只是表面上的纽带。输出a看看:

 

__proto__开始浮出水面。

 

2,对象通过__proto__确定上层原型

关于__proto__的定义,具体看这里。概括一下就是:每个对象通过__proto__属性指向上层原型(要么是对象要么是null),这个__proto__是一个访问器属性。

2.1,初始化:除了Object.prototype的__proto__为null,其他对象的__proto__一定为一个对象。

默认情况下,如果一个对象存在构造函数且其构造函数的prototype是一个对象,则该对象的__proto__指向其构造函数的prototype,否则指向博爱的Object.prototype。

存在构造函数的对象(包括内置类型和自定义类型),__proto__指向其构造函数的prototype

 

当构造函数的prototype不是对象,则指向Object.prototype

 

部分无法构造的对象,也直接指向Object.prototype

 

在这里面,Function.prototype是一个特殊的存在,稍后讲。

2.2,setter:除了Object.prototype的__proto__为null,其他对象的__proto__ = 给定的值指向的对象 ||   原始值

 

注意到原文说到,直接给__proto__赋值是不建议的,建议通过Object.create()实现。

 

3,函数的prototype用来存储公用属性

3.1,函数fun如果可以用来做constructor,则一定有prototype,用来为实例化的对象提供公用的属性,且默认带有属性{ constructor: fun, __proto__: Object.protoype }

其中,__proto__的作用已经知道了,constructor指向函数自身,具体是做什么用的?

 

发现这个属性可以被任意修改甚至删除,但是对对象的原型链却不会产生任何影响。最好还是不依赖这个属性。

prototype不能被删除,但是可以被任意重置,并只对之后实例化的对象产生影响。

 

而不能用作constructor的函数,则没有prototype属性

 

3.2,Function和Function.prorotype

首先,除了Function.prototype,Function是所有函数的构造函数

 

这里知道,Function.protoype是Function的原型。

再看Function.prototype,

 

可知,Function.prototype可以被执行,确实是一个函数。就是说:

所有function 的构造函数都是Function,Function的prototype指向Function.prototype,而这个Function.prototype又是一个function?

再进一步查看,发现这些内置类型都存在这个鸡与蛋的怪圈

 

3.3,先有prototype再有对应的类型

以Function为例,看回之前的__proto__的初始化,

 

可知,Function.prototype虽然类型是function,但是没有构造函数,所以其__proto__只能指向Object.prototype。那么就清楚了:

1)原型链顶端是Object.prototype,其__proto__指向null;

2)定义Function.prototype、Array.prototype等原型,由于没有构造函数,所以这些对象的__proto__指向Object.prototype;

3)定义Function,其prototype指向Function.prototype,Function.prototype的constrcutor指向Function;

4)由Function构造各个类型的构造函数:Object、Array等,将这些函数的prototype指向对应的原型,__proto__指向Function.prototype,同时将该原型的constructor属性指向该函数,设置该原型的类型。同时这些构造函数的__proto__指向Function.prototype。

 

4,新的__proto__

使用Object.create,非Object.prototype的对象的__proto__可以被设为null,且一旦被设为null,只能通过Object.setPrototypeOf重置。

在2.2知道,使用Object.create比直接修改__proto__更加高效,这里查看具体api。

当传入一个对象,相当于

 

当传入的是null,相当于

 

之后设置__proto__,建议通过Object.setPrototypeOf来改变,这里查看具体api

 

5,补充

5.1,原始值由于装箱操作,也可以‘访问’到__proto__属性

 

同理,由于拆箱操作,试图修改是无效的

 

5.2,类型确定

5.2.1,typeof val

这里,只能返回’boolean’、’number’等几种类型,对object类型的无法具体确定;

5.2.2,obj instanceOf constructor

具体定义看这里,注意到是在obj的整个原型链上进行对constructor的prototype进行匹配的;

5.2.3,obj.constructor

在3.1知道了,这个属性是不可靠的;

5.2.4,Object.prototype.toString.call( val )

具体定义看这里,能获得较准确的结果,同时可以结合Symbol.toStringTag实现自定义类型

 

5.3,写属性

以上是讲的如何对象确定原型链,以此实现属性的获取,

对于属性的写入,则分以下两种情况:

5.3.1,属性存在对象上:

此时只要属性是可写的或者是一个setter即可

5.3.2,属性在原型链某层:

需要该对象是isExtensible的,该原型属性是可写的

如果是setter,则会直接调用setter而不在底层发生shadow

 

关于具体的限制级别,可以看这里

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值