prototype和__proto__
以前看的时候就对这个prototype和__proto__感到很奇怪。然后今天看了篇文章,又见到了谈论到这个话题的。
然后搜索了一下,发现这些人讲的都好复杂。。。然后就把自己的想法记录一下吧。
原型链是一个环
先说结论:__proto__
维系着原型链,但是prototype
才是真正的原型。__proto__
指向其构造函数的prototype
。
结论看起来好像有点奇怪,但是其实很容易明白。
首先第一个事实,只有函数才拥有prototype
属性,也可以说只有函数才具有真正的原型。这一点很容易验证Ctrl+Shift+I
,打开控制台就可以查看。
第二个事实,除了undefined
null
以及其他字面量以外,js中所有的值实际上都是一个对象,这一点实际上应该也很容易能够明白。
这两个事实就造成了一个可以看见的现象/事实,函数既有__proto__
又有prototype
,而对象只有__proto__
(这是很重要的一点)。
然后我们就可以很容易描述他们的继承关系了。
class Parents {
name = "parent";
}
class Son extends Parents {
name = "son";
}
const son = new Son();
那当我们son instanceof Parents
的时候会发生什么呢?
son
-> Son.prototype(son.__proto__)
-> Parents.prototype (son.__proto__.__proto__) ✅找到了
你可以验证一下son.__proto__.__proto__ === Parents.prototype
是否为true。
实际上,__proto__
应该是内部实现,是不应该暴露出来的,但是有些浏览器默认将它暴露出来,于是就将错就错了。
但是还是存在一个问题,就是如果对象的__proto__
是其构造函数的prototype
,在JS中实际上函数也是一个对象,__proto__
以及prototype
也是一个对象,Object.prototype
和Function.prototype
将原型链关闭了。那么原型链实际上就不是一个链了,而是一个闭合的环,这当然是绝对不行的。
原型链不是一个环
实际上我们如果查看一下Object.prototype.__proto__
的话,他的值应该是null。这一点一样可以在浏览器控制台真实。所以这个表现告诉我们的(能够符合这个事实表现的推论是)Object.prototype
对象是一个特殊的__proto__
被设置称为null的对象。
Object.prototype
就是原型链的终点。如果安装了firefox的话你甚至如此看到:
当然,默认__proto__
那里是一个小箭头,你需要点击一下才会把null
这个值展示出来,同时你也应该注意到了getter和setter,就是getter将这个__proto__
的值返回的,在chrome上你应该看不到Object.prototype
的__proto__
属性,但是在Object.prototype
上他们同样具有__proto__
的getter和setter。