建议大家看的时候手动画图!!!这点很重要!!!
原型链在结构上很像链表,每个对象中都保存着一个地址,指向当前对象的原型,可以层层向上查找,起到继承的效果。
原型链:由对象的__proto__属性串联起来直到Object.prototype.__proto__为null的链,就叫原型链。
原型
1、prototype:显式原型
每个函数都有一个prototype(显式原型)属性,都默认指向一个Object对象【全名:Object构造函数的实例对象。这个对象内部有两个默认携带的属性(方法):__proto__、constructor。在被创建时就携带着这两个属性。】
但是有一个对象除外:Object构造函数(又叫Object函数对象)的prototype属性指向 → Object原型对象,又叫Object.prototype
几乎所有的对象都可以看做Object的实例对象,但这个被指向的对象Object原型对象却不能被叫做Object构造函数的实例化对象,原因是:
所有的Object实例对象必须满足一个要求,其内部的__proto__属性要有值,但是Object原型对象是整个原型链的终点,它的__proto__属性为null。
验证这个特殊的对象:
console.log(Object.prototype.__proto__); // null
(1)查看对象的显式原型
Function原型
function Fun() {};
console.log( Fun.prototype );
这里的__proto__
实际上是对象的[[prototype]]
属性的非标准表示。
从上述控制台输出可以看到,里面除了constructor、__proto__之外,没有其他任何属性。
Date原型
Date函数的显式原型:console.log(Date.prototype);
可以看到,Date函数的原型,是一个Object实例对象。但是这个对象身上有很多的属性(方法),这是因为,在Date构造函数中,通过Date.prototype.xxx向它的原型中添加了很多属性。
(2)向构造函数的原型添加属性
例如:向构造函数Fun的原型中添加一个test属性(方法):
let Fun = new Function()
Fun.prototype.test = function(){
console.log("test");
}
console.log( Fun.prototype ); // 检验一下是否添加成功
发现构造函数 Fun 的原型中已经多了一个 test 方法。
图2-1
从上图可以看到,红色箭头就是引用,声明函数和变量会在栈中存放引用地址,实际内容存放在堆中。蓝色箭头表明fun的__proto__指向的是Fun的原型对象,紫色箭头表示Fun的原型对象中constructor指向了Fun的构造函数,而绿色箭头表明Fun构造函数的prototype指针指向的就是Fun的原型对象。
再结合代码,Fun被声明创建了红色箭头的引用。fun是Fun的实例对象,也创建了红色箭头的引用,同时new关键字实例化对象会把fun与Fun的原型进行关联,形成了蓝色箭头。然后在Fun.prototype上面新增了test方法。因此fun可以通过__proto__属性找到构造函数中的test方法,从而进行调用。
Fun函数是Object构造函数的实例化对象,引用类型都会继承Object对象的原型(也就是空Object对象)。结合原型链知识,原型对象中会存在一个指针constructor指向对象的