背景
原型链是多数面试官心中的“宠儿”,同时也是很多小伙伴面试时躲不过的梦魇,每次都被“八股文”和各种盘根错节的关系搞得头大,这里霸哥使用图文结合的方式,带大家探索原型链的“前世今生”
原型链图解
小伙伴们先看一下原型链的“完全体”,接下来我们将由点到面,从Teacher构造函数开始,和大家一起完成原型链的“进化”
声明一个构造函数
声明一个空的构造函数Teacher,并在控制台打印其结构,这里我们重点关注prototype属性
function Teacher() {}
原型对象
Teacher为构造函数,通过prototype属性可以访问Teacher原型,我们通常使用Teacher.prototype来表示
因为原型为对象结构,我们也称其为原型对象
原型的访问方式
对于原型对象,我们有2种访问方式
通过显式原型
(1)如上所示,通过构造函数的prototype属性进行访问(这里的prototype被称为“显示原型”)
Teacher.prototype
通过隐式原型
(2)使用new操作符,创建构造函数的实例,实例会继承该属性,实例中有1个神秘的属性帮我们进行访问,这个属性就是隐式原型,现在我们先来创建一个实例
创建实例
创建teacher实例
const teacher = new Teacher();
隐式原型
在实例中,我们可以通过上文提到的隐式原型直接访问原型对象,隐式原型有2种表示方法:__photo__ 和 [[Prototype]]
注意:
__proto__中的__是由两个_组成,_位于键盘上“0”的右边
不同点
-
[[Prototype]]用于表示一个对象的原型属性,但我们无法直接使用其进行访问
-
__photo__用于访问对象的原型
如下图所示
prototype和__proto__指向同一对象
我们通过构造函数Teacher的prototype在原型上添加sex属性,然后使用实例teacher的__proto__进行访问,
能成功获取sex的属性值'man',则说明prototype和__proto指向的是同一个对象——原型对象
我们再来用'==='加以佐证
constructor属性访问构造函数
在原型中为我们提供了constructor属性访问构造函数
注意:
(1)区分大小写,Constructor为构造函数,constructor为原型中的属性
(2)因为一个构造函数可以生成多个实例,所以原型无法直接访问实例,只能访问构造函数
(3)实例无法直接访问构造函数,但可以通过原型进行访问
原型对象的作用
当我们尝试访问实例中的一个属性时,如果当前实例中没有,则会通过__proto__访问原型对象进行查找,实现了属性的继承
// 声明一个构造函数Student
function Student(sex) {
// 定义sex属性
this.sex = sex;
}
// 创建student实例
const student = new Student();
console.log(student.name); // 结果为undefined
// 通过prototype在原型上声明name属性
Student.prototype.name = 'Peter'
console.log(student.name); // 结果为Peter
我们可以看到sex是student实例自身属性,name是我们通过隐式原型继承自原型对象的属性
形成原型链
Teacher原型也是对象,由Object构造函数通过new操作符创建
我们发现这里的操作和上面new Teacher()有异曲同工之妙,Teacher原型作为Object的实例,它应该也具有__photo__属性去访问Object构造函数的原型(Object.prototype)
当我们在teacher实例中查找属性不能找到时,会通过__proto__访问Teacher原型对象,再找不到继续通过__proto__向上查找
Object.prototype是对象的最终原型,绝大多数对象最终都会继承自Object.prototype,而Object.prototype的原型是null
null被创造出来是为了表示空对象,但在原型链的属性继承中无实际意义,所以将Object.prototype视为最终原型
后语
恭喜各位小伙伴,我们已经成功的把原型链从“幼年期”孵化为“完全体”啦
原型链在我们的工作中也是不可或缺的知识点,希望大家以理解为目的,不要单纯为了面试而学习,面试只是对我们学习成果的一次检验
我是前端霸哥,愿你的代码中没有bug
写的不好的地方,欢迎各位小伙伴批评指正