对于原型与原型链的理解,网上一搜一大堆,这里仅对我自己的理解做个笔记。
这里面会涉及到几个名词:构造函数、原型对象、prototype、constructor、实例对象
1.声明一个函数(即构造函数)后,浏览器会在内存中创建一个原型对象。
2.构造函数会有一个prototype属性,指向【1】中说的原型对象。
3.原型对象中有个constructor属性,指向构造函数。
关系图如下
通过构造函数创建实例对象后,如:
function Person(){
// 我就是构造函数
}
const p1 = new Person() // 实例对象
console.log(Person.prototype); // {constructor: ƒ}
console.log(p1.prototype); // undefined
console.log(p1.__proto__ === Person.prototype); // true
可以看出,实例对象是没有prototype属性的,只有构造函数有prototype属性。实例对象的__proto__属性指向构造函数的prototype
实例对象、原型对象、构造函数三者之间的关系如下图
我们再看下面的代码
function Person(){
// 我就是构造函数
}
const p1 = new Person() // 实例对象
p1.age = 14
Person.prototype.name = '张三'
console.log(p1.age); // 14
console.log(p1.name); // 张三
console.log(p1.toString); // toString() { [native code] }
可以看出:p1实例上只有age属性,但为什么能取到name和toString属性的值呢,这就是因为原型链。我们console.log(p1)在控制台输出如下图
说明:我们通过__proto__取到的属性即为上图中对象的[[Prototype]]属性。
【1】.当取 p1.age 时,能直接从p1上找到age属性,直接输出;
【2】.当取 p1.name 时,先查找p1自身的属性,没找到name,则继续从 p1 的__proto__ (即上图中[[Prototype]],也就是p1的构造函数的prototype) 属性中查找,在p1的构造函数的prototype中找到name属性了,停止查找,输出结果;
【3】.当取 p1.toString时,同上先查找p1自身是否有该属性,没有,则从p1的构造函数的prototype属性上查找,仍没找到,则继续向 【p1的构造函数的构造函数】的prototype上查找,找到了,则输出,若仍找到,继续向上层构造函数的prototype上查找。
这样一直向上查找原型(prototype),就形成了一个链,即称为原型链。