js深入之原型和原型链
原型和原型链是我一直觉得自己理解但是并不是很深入的一个知识点,因为实际项目中用到的不多,所以一直没有机会深刻理解原型和原型链,在空闲时间学习并记录。
prototype
原型的概念是比较抽象的,在我起初的理解看来,它相当于一个最最基础的模型,我们可以根据原型实例化一个对象,也可以在之前的基础上修改原型。举例:
function Person(){
}
// 这里的prototype是函数才有的属性
Person.prototype.name = "小明";
let p1 = new Person();
我们打印一下实例p1
由此可以得出函数的prototype指向了一个对象,这个对象就是调用该构造函数创建的实例对象的原型,也就是p1的原型,此时如果我们再实例化一个对象p2,prototype也是指向p2的原型的,也因此prototype上绑定的属性和方法在实例间可以共享,也就是所说的“继承”属性。
__ proto __
prototype是函数特有的属性,表示构造函数和实例原型的关系,那我们应该如何表示实例和实例原型关联呢? 这个时候就需要使用到__ proto __ 这个属性。
在创建一个JavaScript对象的时候,每个对象都会有一个__ proto __ 的属性,该属性指向该对象的原型,此时注意。null类型没有 __ proto __ 这个属性
举例:当一个属性为null的时候,打印 a.__ proto __ 会报错
既然这个属性是每一个对象都有的,我们不妨打印一下实例的__ proto __ 与 构造函数的prototype,发现两个打印的结果是一致的。 都指向了实例的原型。
构造函数的prototype ——> 指向实例的原型,
实例化的__ proto __ ——> 指向实例的原型,
由此得出 构造函数 和 实例化对象都可以指向原型,那么原型是否有属性可以指向实例化对象或者构造函数呢??
constructor
首先考虑实例化对象的构造函数是什么?
实例化对象是根据构造函数生成的,一个构造函数可以生成多个实例对象,因此原型是没有属性指向实例对象的,但是有一个属性可以指向构造函数,那就是constructor,每一个原型都有一个constructor属性指向该原型所关联的构造函数,开始验证:
function Person(){
}
console.log(Person.prototype.constructor);
// 打印一下结果
f Person(){ }
// Person.prototype 原型
console.log(Person.prototype.constructor === Person); // true
// 同理的 p1.__proto__.constructor === Person 也是true
以上就是 构造函数 、 实例原型 以及 实例 的概念和它们之间的关系。
实例与原型
当想要访问实例的属性时,如果在实例中找不到,就回去实例所关联的原型中的属性去寻找,也就是在prototype上绑定的属性,如果还是找不到,就去找原型所对应的原型,一直找到最顶层为止。
举个栗子:
function Person(){
}
Person.prototype.name = "小明";
let person = new Person();
person.name = "小红";
console.log(person.name); //此时打印的是小红
delete person.name;
console.log(person.name); // 此时打印的就是 "小明"
// 在实例对象中找不到属性,就会去对应的 person.__proto__ 上也就是原型去寻找,也就是去Person.prototype寻找
上述情况在实例所关联的原型上找到了对应的属性,如果没有找到呢?如果原型上存在原型是要继续向上找的,那原型的原型又是什么呢?
原型链
原型链可以用一个关系直观的表示,就是:子类 ——> 父类 ——> 父类 ——> 父类…一直到最顶层。
由上面的打印结果可知,原型的本质其实是一个对象,是通过new Object()去生成的。那Object的原型又是什么呢? 我们可以打印一下:
打印的结果是null,表示Object没有原型,也就是说Object是原型链的最顶端。所以当我们在原型链上查找属性或者方法的时候,找到Object.prototype之后就会停止查找。这个查找的过程就会创建一个原型链。
最后
prototype的使用场景: 我们可以在自定义对象上绑定公用的方法或属性,也可以在自带的对象(如Array)上绑定我们的自定义方法。
__ proto :在构成原型链的过程中,需要使用到 proto __,一般不需要我们手动操作,事实上它不存在于Person.prototype中,而是在Object.prototype上,它相当于是一个getter/setter的过程,它返回了一个 Object.getPrototype(obj)。