我真的讲清楚原型和原型链了吗?

854 阅读3分钟

什么是原型

    引用红宝石一书原文:无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象 那么接下来让我们验证一下是否只有函数存在prototype属性

function Person () {};
const str = '';
const num = 1;
const u = undefined;
const n = null;
const o = {};
const b = true;

console.log(Person.prototype) //Person {}
console.log(str.prototype) //undefined
console.log(num.prototype) //undefined
console.log(u.prototype) //Cannot read property 'prototype' of undefined
console.log(n.prototype) // Cannot read property 'prototype' of undefined
console.log(o.prototype) //undefined
console.log(b.prototype) //undefined

由此可见除了构造函数打印出了Person{}, 其他都不存在prototype 那么当我们创建了一个实例以后,该实例又是什么状态呢, 它是否也具备了构造函数的实例以及方法呢

Person.prototype.name = '前端'
let foo = new Person()
console.log(foo.name) // 前端

那么实例的prototype是否等于构造函数的prototype呢,很明显,实例并没有prototype的属性,那么它是怎么查找到name的呢

console.log(foo.prototype) // undefined
console.log(foo.prototype == Person.prototype) // false

__proto__

答案是通过__proto__进行查找

console.log(foo.__proto__) // Person { name: '前端' }
console.log(foo.__proto__.name) // 前端
console.log(foo.__proto__ === Person.prototype) //true

ok,那通过上面的代码我们就有了这样的结论: 每一个构造函数都存在prototype的属性,每一个实例都会有一个__proto__的属性。实例中的__proto__会等于构造函数中的prototype 好的,那我们就可以画一幅关系图出来了

image.png

constructor

那么我们又会有一个疑问,那原型是否又有属性指向构造函数或者实例呢,答案是有,这个属性就是constructor

console.log(Person.prototype.constructor === Person)// true
console.log(Person.prototype.constructor === foo.constructor)// true

这个时候可能会有疑问了,实例中的constructor是从何而来,这个我们后面再说

好了,现在说了构造函数和实例以及原型之间的关系了,那么接下来我们说下实例和原型之间的关系,当你在实例中查找一个属性或者方法时,如果构造函数中未定义,那么实例就会往原型中查找,如果原型中仍未找到,那它就会往原型的原型中进行查找,一直追溯到顶层,如果仍未找到,那么就会返回null,举个例子:

Person.prototype.name = 'person'
let foo = new Person()
foo.name = 'foo'
console.log(foo.name) //foo
delete foo.name
console.log(foo.name) //person

创建实例以后,给实例添加了name,那么第一次查找就会返回foo,当删除这个name以后再进行查找,实例本身已经没有name了,那么它就会往原型上进行查找,返回person,那么如果原型上也没有呢

delete Person.prototype.name
console.log(foo.name) //undefined

刚才所说到如果原型上未找到,就会查找原型的原型,那么原型的原型又是什么东西呢?其实他也是一个对象,也就是Obeject,那么上面的那副关系图又可以修改一下

image.png console.log(Person.prototype.__proto__ === Object.prototype)

如果对顶层是Object还有疑惑,那么在上面删除Person.name,再插入一行Object.prototype.name == 'object' 再执行console.log(foo.name)试试看会发生什么呢

原型链

最后什么是原型链呢,其实上面已经给出了答案,实例从原型中进行查找,没找到就会原型的原型中进行查找,这一系列的操作下来,其实就已经构成了原型链,直到最后Object,那么也就是说原型链的最顶端就是null了,让我们验证一下

console.log(Object.prototype.__proto__ === null) // true

好了,总结到这里就已经结束了,不过上面还有一个问题console.log(Person.prototype.constructor === person.constructor)// true,实例的constructor是哪里来的呢?总结到这里应该很清楚了,其实就是实例中并没有constructor这个属性,它只是从原型中获取到了,那么最后验证一下

console.log(foo.constructor === Person.prototype.constructor) // true
console.log(Person.prototype.constructor === Person) // true
console.log(foo.constructor === Person) // true

到这里基本结束了

image.png