原型和原型链
在说明原型和原型链之前先来看看这么一串代码:
Function.prototype.a = 'aaa'
Object.prototype.b = 'bbb'
function Fn() {}
Fn.prototype.c = 'ccc'
var fn = new Fn()
fn.__proto__.d = 'ddd'
fn.e = 'eee'
console.log(fn); // fn {}
console.log(fn.a); // undefined
console.log(fn.b); // bbb
console.log(fn.c); // ccc
console.log(fn.d); // ddd
console.log(fn.e); // eee
打印出来如下
除了fn.e和想的一样,其它的都是云里雾里的。
原型
这里就涉及到原型了,原型分为显示原型 prototype 和隐式原型__proto__。
prototype和__proto__
显示原型prototype是在你定义函数的时候就会帮你自动生成一个prototype,默认为空对象;隐式原型是你在创建一个对象的时候会帮你添加一个__proto__,默认为构造函数的prototype属性;
来看一下下面这段代码吧:
var arr = []
var obj = {}
function Fn() {}
var fn = new Fn()
console.log(arr.__proto__ === Array.prototype); // true
console.log(obj.__proto__ === Object.prototype); // true
console.log(Fn.__proto__ === Function.prototype); // true
console.log(fn.__proto__ === Fn.prototype); // true
大伙们可以自己去试一试
以var fn 为例,JavaScript一切皆对象,所以在fn被创建的时候就会添加一个__proto__,指向构造函数的prototype也就是Fn,而Fn在你定义它的时候就添加了prototype,所以fn.__ proto __ === Fn.prototype其它的也是同理。
要点:实例对象的隐式原型(__ proto __) === 构造函数的显示原型(prototype)
原型链
刚刚上面的例子其实涉及到原型链,我们以最开始这个为例子把
Function.prototype.a = 'aaa'
Object.prototype.b = 'bbb'
function Fn() {}
Fn.prototype.c = 'ccc'
var fn = new Fn()
fn.__proto__.d = 'ddd'
fn.e = 'eee'
console.log(fn); // fn {}
console.log(fn.a); // undefined
console.log(fn.b); // bbb
console.log(fn.c); // ccc
console.log(fn.d); // ddd
console.log(fn.e); // ddd
这里是fn的心路历程:
首先fn会现在自身寻找,看看有没有a、b、c、d,发现自己只有e,然后它会尝试从原型上去寻找,fn的原型只有__proto__,因为他不是构造函数只是个实例对象,所以就会在fn.__proto__里面寻找有没有,根据刚刚说原型的要点 可以找到 d 和 c ,到这里都OK。
这时候重点来了,b要怎么找呢?这时候就要岔开话题了,来看下面这个代码
function Fn() {}
var fn = new Fn()
console.log(Fn.prototype)
// {
// c: "ccc",
// d: "ddd",
// constructor: ƒ Fn(),
// __proto__: Object <---------------这里也有个
//}
console.log(fn.__proto__ === Fn.prototype); // true
console.log(Fn.prototype.__proto__ === Object.prototype); // true
看了一下里面好像有个__proto__,诶?为什么呢?记得JavaScript是面向什么编程的吗?面向对象的!!所以这个prototype也是有隐式原型的,指向的是Object的显示原型。
这里大家可能会有疑惑,所以各位可以去试着写一个这样的代码:
var obj = {
a: {}
}
console.log(obj.a.__proto__ === Object.prototype); // true
把obj.a当做prototype是不是就很好理解了?
所以它是这样找到b的。
那问题又来了。那港道理,凭啥fn.a没有?欺负人呗!
那么根据刚刚一切皆对象的理论,可以得知这个Fn函数也是个对象,那么它应该也是有隐式原型的,那它应该有对应的构造函数,就是Function了,那么理论没错的话应该可以得到Fn.__ proto __ === Function.prototype 为true。那么来试一试把。
Function.prototype.a = 'aaa'
function Fn() {}
console.log(Fn.__proto__ === Function.prototype); // true
console.log(Fn.__proto__.a === Function.prototype.a); // true
和刚刚猜想的结论是一致的!那么为什么fn.a会没有呢?这是因为每条原型链都是一路到底的,不能漂移到其它原型链上。
所以他们两的查询路线分别是这样的一条链;
fn.proto === Fn.prototype——》 Fn.prototype.__ proto __ === Object.prototype——》 Object.prototype.__ proto __
Fn.__ proto __ === Function.prototype—》Function.prototype.__ proto __ === Object.prototype—》 Object.prototype.__ proto __
所以fn.a是找不到的原因就是这样。
xx.prototype.constructor与xx
差点忘了,这一块和构造函数有关系的constructor,
function Fn() {}
var fn = new Fn()
console.log(Function === Function.prototype.constructor); // true
console.log(Object === Object.prototype.constructor); // true
console.log(Fn === Fn.prototype.constructor); // true
简单理解就是,prototype有个属性constructor的值是xx本身。
最后我把自己画的关系图放上来