研究javascript的原型概念两天,看到这篇文章后,终于恍然大悟,里面的图展现了一切!大部份内容转载自:https://segmentfault.com/a/1190000005824449javascript
却不知,JavaScript的世界中的对象,追根溯源来自于一个 nulljava
「一切皆为对象」,这句着实是一手好营销,易记,易上口,印象深入。segmentfault
万物初生时,一个null
对象,凭空而生,接着Object
、Function
学着null
的模样塑造了本身,而且它们彼此之间喜结连理,提供了prototype
和constructor
,一个给子孙提供了基因,一个则制造万千子子孙孙。浏览器
在JavaScript中,null
也是做为一个对象存在,基于它继承的子子孙孙,当属对象。乍一看,null
像是上帝,而Object
和Function
犹如JavaScript世界中的亚当与夏娃。函数
__proto__
在JavaScript中,每一个对象都拥有一个原型对象,而指向该原型对象的内部指针则是__proto__
,经过它能够从中继承原型对象的属性,原型是JavaScript中的基因连接,有了这个,才能知道这个对象的祖祖辈辈。从对象中的__proto__
能够访问到他所继承的原型对象。ui
var a = new Array(); a.__proto__ === Array.prototype // true
上面代码中,建立了一个Array的实例a
,该实例的原型指向了Array.prototype
。Array.prototype
自己也是一个对象,也有继承的原型:spa
a.__proto__.__proto__ === Object.prototype // true // 等同于 Array.prototype.__proto__ === Object.prototype
这就说了明了,Array自己也是继承自Object的,那么Object的原型指向的是谁呢?prototype
a.__proto__.__proto__.__proto__ === null // true // 等同于 Object.prototype.__proto__ === null
因此说,JavaScript中的对象,追根溯源都是来自一个null对象。佛曰:万物皆空,善哉善哉。指针
除了使用.__proto__
方式访问对象的原型,还能够经过Object.getPrototypeOf
方法来获取对象的原型,以及经过Object.setPrototypeOf
方法来重写对象的原型。code
值得注意的是,按照语言标准,__proto__
属性只有浏览器才须要部署,其余环境能够没有这个属性,并且先后的两根下划线,表示它本质是一个内部属性,不该该对使用者暴露。所以,应该尽可能少用这个属性,而是用 Object.getPrototypeof
和Object.setPrototypeOf
,进行原型对象的读写操做。这里用__proto__
属性来描述对象中的原型,是由于这样来得更加形象,且容易理解。
prototype
函数做为JavaScript中的一等公民,它既是函数又是对象,函数的原型指向的是Function.prototype
var Foo = function() {} Foo.__proto__ === Function.prototype // true
函数实例除了拥有__proto__
属性以外,还拥有prototype
属性。经过该函数构造的新的实例对象,其原型指针__proto__
会指向该函数的prototype
属性。
var a = new Foo(); a.__proto__ === Foo.prototype; // true
而函数的prototype
属性,自己是一个由Object
构造的实例对象。
Foo.prototype.__proto__ === Object.prototype; // true
prototype
属性很特殊,它还有一个隐式的constructor
,指向了构造函数自己。
Foo.prototype.constructor === Foo; // true a.constructor === Foo; // true a.constructor === Foo.prototype.constructor; // true
概念:
原型链做为实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。
每一个构造函数都有一个原型对象(prototype
),原型对象都包含一个指向构造函数的指针(constructor
),而实例都包含一个指向原型对象的内部指针(__proto__
)。
那么,假如咱们让原型对象等于另外一个类型的实例,此时的原型对象将包含一个指向另外一个原型的指针,相应地,另外一个原型中也包含着一个指向另外一个构造函数的指针。假如另外一个原型又是另外一个类型的实例,那么上述关系依然成立。如此层层递进,就构造了实例与原型的链条,这就是原型链的基本概念。
意义:“原型链”的做用在于,当读取对象的某个属性时,JavaScript引擎先寻找对象自己的属性,若是找不到,就到它的原型去找,若是仍是找不到,就到原型的原型去找。以此类推,若是直到最顶层的Object.prototype仍是找不到,则返回undefine。
在JavaScript中,也存在鉴定亲子之间DNA关系的方法:
instanceof 运算符返回一个布尔值,表示一个对象是否由某个构造函数建立。
Object.isPrototypeOf() 只要某个对象处在原型链上,isProtypeOf都返回true
var Bar = function() {} var b = new Bar(); b instanceof Bar // true Bar.prototype.isPrototypeOf(b) // true Object.prototype.isPrototypeOf(Bar) // true
要注意,实例b
的原型是Bar.prototype
而不是Bar
这是一张描述了Object
、Function
以及一个函数实例Foo
他们之间原型之间联系。若是理解了上面的概念,这张图是不难读懂。
从上图中,能看到一个有趣的地方。
Function.prototype.__proto__
指向了 Object.prototype
,这说明Function.prototype
是一个 Object
实例,那么应当是先有的Object
再有Function
。
可是Object.prototype.constructor.__proto__
又指向了 Function.prototype
。这样看来,没有Function
,Object
也不能建立实例。
这就产生了一种类「先有鸡仍是先有蛋」的经典问题,究竟是先有的Object
仍是先有的Function
呢?
这么哲学向的问题,留给你思考了。
总结一句话:
对象:
自身属性
构造器属性(constructor)
原型(__proto__)
函数(构造方法):
自身属性
构造器属性(constructor)
原型属性(prototype)
原型(__proto__)
对象.原型(__proto__) === 函数(构造方法).原型属性(prototype)对象.构造器属性 === 函数(构造方法)