写这篇文章的时候,我还没有开始系统学习JavaScript的OOP(面向对象编程),甚至有关函数这个一等公民都还没写一篇像样的博客。这里就浅谈一下对__proto__
和prototype
以及constructor
之间那些爱恨情仇的意识流理解。
先看一段代码:
function Foo(){}
var f1 = new Foo()
var o1 = new Object()
这段代码运行的时候,js的世界里发生了什么?
首先,如上图所示的左边,声明了Foo类型和Object类型的两个对象f1和o1。他们也会有同样是Foo类型和Object类型的其他兄弟姐妹,比如f2,f3……fn,o2,o3……on,他们各自有各自的方法属性,都旗帜鲜明,但如果有一些共同的特点,那么是不是每new一个兄弟出来,这些共同点又要写一遍?
不可能,程序员都很懒,抢票都要写脚本自动去刷。
这些公共的属性方法,显然应该放到一个地方集中定义,这就是prototype
的主要作用:定义共享的属性和方法。存放这些公共属性方法的对象就被称为原型对象。如图就是函数Foo()
的原型对象就是Foo.prototype
。
而因为原型对象的属性和方法,并不属于实例对象本身,而是无私地施舍给这些家伙用的,因此,只要修改原型对象,这些变动会立刻体现在所有实例对象上。所谓“牵一发而动全身”正视如此。
那么这些实例对象又如何访问到原型对象里的属性和方法呢?答案是每一个对象在诞生之初就有的__proto__
属性。通过__proto__
属性,每个儿子都有得以窥探他们爸爸那些小九九的钥匙。
这时候,Foo.prototype
这个原型对象也想找爸爸了,于是它也掏出自己的钥匙__proto__
去打开了它爹的门,见到了亲爹Object.prototype
;Object.prototype
也不甘示弱,想掏出了钥匙__proto__
,然而,已经没有门给它开了,钥匙上分明写着“你是他们的祖宗,你找**呢”,Object.prototype.__proto__ === null
。
上面这个“找爸爸”的过程实际上就是js在原型链上给这些子子孙孙们找原型对象的过程。这些对象之间通过__proto__
一层层联系起来,形成了像链条一样的东西,就是原型链。
如果实例对象没有某个属性或方法,js会顺着原型链一直往上找,直到祖宗Object.prototype
,如果还没有,就返回undefined
。就像你问爸爸,为什么我没钱?你爸爸问爷爷……一直问到十八辈祖宗,他托梦说,吾辈向来家徒四壁,基因上你就别指望有这个属性了,只能通过自己的努力去添加挣钱的方法。
另外,如果你和你爸爸有一个相同的方法,比如“出门方式”,你爸开大奔,你骑摩拜,这就是对象对其原型的同名属性或者方法的覆盖(overriding)。
那么constructor
又是什么呢?
翻译一下,构造器,而所有构造器都放在函数一列,故为构造函数。constructor
属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。
在上图中,Foo.prototype
中的constructor
属性指向了函数Foo
,而Foo.prototype
中的所有属性方法是要被函数Foo
new出来的实例对象继承的,因此每个实例对象都有了constructor
属性,指向了函数Foo
,以此表明,这些实例是由函数Foo
构造出来的,即f1.constructor === Foo
。
另一方面,有了constructor属性,就可以从一个实例对象新建另一个实例。比如var f2 = new f1.constructor()
。
constructor
属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor
属性,防止引用的时候出错。
上图转换为代码,可以得到如下的等式:
f1.__proto__ === Foo.prototype
Foo.prototype.constructor === Foo
Foo.__proto__ === Function.prototype // Foo是Function构造出来的实例
f1.__proto__.__proto__ === Object.prototype
Function.__proto__ === Function.prototype // 一切函数都是由函数Function构造的,包括他本身
Function.prototype.constructor === Function
Function.constructor === Function
Object.__proto__ === Function.prototype // 函数Object也是由Function构造的
Function.prototype.__proto__ === Object.prototype
记住一个永恒的公式:对象.__proto__
=== 对象的构造函数.prototype
而所有函数都是由 Function 构造出来的,所以
Number.__proto__ = Function.prototype // 因为 Number 是函数,是 Function 的实例
Object.__proto__ = Function.prototype // 因为 Object 是函数,是 Function 的实例
Function.__proto__ == Function.prototye // 因为 Function 是函数,是 Function 的实例!
总结:
以上是我对原型和原型的浅薄理解,等我深入学习了JavaScript的面向对象编程,梳理顺了this的相关知识,相信会对原型和原型链有更加深刻的认识。
网友评论