前言:
原型和原型链作为 JS 的重要知识点,是很多面试经常考查的内容,这篇文章主要针对 javascript中的原型、原型链进行讲解
原型
什么是原型
在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个”[[Prototype]]”内部属性,这个属性所对应的就是该对象的原型。原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有“proto”属性,函数对象有”prototype”属性,原型对象有”constructor”属性。
所有的引用类型(数组,对象,函数)都有一个隐式原型属性(_proto_);
所有的函数,都有一个显示原型属性(prototype);
所有的引用类型,_proto_属性值指向它的构造函数的prototype属性值;
当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去_proto_(即它的构造函数的prototype中)寻找。
在自定义构造函数时,原型对象默认只会获得constructor属性,其他的所有方法都会继承自Object。
原型是Javascript中的继承的基础,JavaScript的继承就是基于原型的继承。
简单代码
class Student{
constructor(name,score){
this.name = name
this.score = score
}
introduce(){
console.log(`我是${this.name},考了${this.score}分。`)
}
}
const student = new Student('张三',99)
console.log('student',student)
student.introduce()
打印结果
在上面的代码中 ,使用 class 定义一个类 Student ,这个时候得到一个 实例对象 student ,可以看到创建出来的 实例对象student 可以访问 类Student 的属性和方法 ,但是在控制台中可以看到, 实例对象只有 name 和 score 属性,它并没有 introduce 方法 (如上图所示)
实例对象student 可以调用 introduce 方法是因为,这个方法在它的 __proto__ (隐式原型) 中
new操作符的过程
- 先在内存中创建一个对象 let student = { }
- this指向这个对象,也就是 this = student
- 将函数的显示原型 prototype 赋值给前面创建出来的对象的隐式原型,这里也就是student.__ proto __ = Student.prototype
- 如果构造函数没有对象,则返回空值。反之返回创建出来的新对象(student)。
验证一下
Object 是所有对象的父类(它是最顶层的原型)
四个规则
1、引用类型,都具有对象特性,即可自由扩展属性。
2、引用类型,都有一个隐式原型 __proto__
属性,属性值是一个普通的对象。
3、引用类型,隐式原型 __proto__
的属性值指向它的构造函数的显式原型 prototype
属性值。
4、当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型 __proto__
(也就是它的构造函数的显式原型 prototype
)中寻找。
原型链
什么是原型链
每个构造函数都有原型对象,每个构造函数实例都包含一个指向原型对象的内部指针(proto),如果我们让第一个构造函数的原型对象等于第二个构造函数的实例,结果第一个构造函数的原型对象将包含一个指向第二个原型对象的指针,再然第三个原型对象等于第一个构造函数的实例,这样第三个原型对象也将包含指向第一个原型对象的指针,以此类推,就够成了实例于原型的链条,这就是原型链的基本概念。
因为每个对象和原型都有原型,对象的原型指向原型对象,而父的原型又指向父的父
这种原型层层连接起来的就构成了一个链条,这个链条我们称之为原型链(prototype chain)。
属性逐层查找
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止,到查找到达原型链的顶部(也就是 Object.prototype),如果仍然没有找到指定的属性,就会返回 undefined。
代码
let student = {
name : '张三',
age:18
}
console.log(student.score); //undefined
因为现在没有定义 score 这个属性,程序打印结果理所当然的是 undefined。
JS引擎先从在 student 对象里寻找,发现没有找到,然后接着去找 student.__ proto __ 里面寻找,发现还是没找到,所以结果为 undefined。我们可以给student.__ proto __赋值(如下)
let student = {
name : '张三',
age:18
}
student.__proto__ = {
score:100
}
console.log(student.score); //100
可以看到 现在程序已经 有打印结果了 ,现在就可以通过 .__ proto __ 进行无限套娃了,我这里只演示一层套娃
let student = {
name : '张三',
age:18
}
student.__proto__ = {
//这里一定要开辟一个空间,不能直接写student.__proto__.__proto__ = { score:100 }
}
student.__proto__.__proto__= {
score:100
}
console.log(student.score); //100
注意
student.__proto__ = {
//这里一定要开辟一个空间,不能直接写student.__proto__.__proto__ = { score:100 }
}
如果没有这个操作的话,程序会报错 (如下)
图片描述原型链
总结
1. 所有的对象都有__proto__属性,该属性对应该对象的原型.
2. 所有的函数对象都有prototype属性,该属性的值会被赋值给该函数创建的对象的_proto_属性.
原型链好比继承+迭代,它可以让一个对象,继承多种多样的属性,方法。
而 __proto__ 和 prototype ,就好比人的DNA。__proto__指向父母,prototype 代表父母自己。两者的区别就在于此。
文章如有错误,恳请大家提出问题,本人不胜感激 。 不懂的地方可以评论,我都会一一回复
文章对大家有帮助的话,希望大家能动手点赞鼓励,大家未来一起努力 长路漫漫,道阻且长