2022.9.21 js的原型和原型链原理解析

原型

什么是原型?
在js中,每个对象都对应着一个原型对象,对每个构造函数来说,都有一个prototype属性,该属性指向该构造函数的原型对象;对于构造方法的创建的实例对象来说,每个实例对象(子对象)都有一个__proto__属性,它也是指向创建这个实例的对象(构造方法)的原型对象。
比如:Teacher是一个构造方法,它所创建的实例是teacher,那么Teacher.prototype === teacher.proto,有图片很好理解:
在这里插入图片描述

prototype 属性

我们都知道,当一个构造函数调用prototype 属性时,会得到它的原型对象;那如果在原型对象上添加新的属性或方法时,结果会怎样?

function Teacher (age) {
        this.age = age
    }
    
Teacher.prototype.name = "张三"
var teacher = new Teacher(30)
console.log(teacher.name)   // 张三

原型上增加的name属性,teacher实例作为原型的子对象也具备了name属性;这时如果出现第二个实例对象或者更多,它们也都共享这个name属性。
再者,如果实例对象本身就有name属性,那就用不到原型上的了:

function Teacher (age, name) {
        this.age = age
        this.name = name
    }
    
Teacher.prototype.name = "张三"
var teacher = new Teacher(30, "李四")
console.log(teacher.name)   // 李四

从上面的例子可以看出,teacher实例确实就像是Teacher.prototype原型对象衍生出来的子对象。

constructor 属性

原型对象都有一个属性constructor指向它的构造函数
关于constructor 属性有两点要注意:
1、实例对象本身没有constructor 属性,但是它可以知道他的构造函数是谁

teacher.constructor === Teacher.prototype.constructor  //true

通过获取原型上的constructor属性。

2、当我们手动修改原型对象时,一般需要同时修改constructor

Teacher.prototype =  Student.prototype
console.log(Teacher.prototype.constructor) //打印出Student构造函数

此时的constructor属性指向了Student
在这里插入图片描述
在修改原型的同时,修改constructor属性

Teacher.prototype =  Student.prototype
Teacher.prototype.constructor = Teacher
console.log(Teacher.prototype.constructor) //打印出Teacher构造函数

在这里插入图片描述

__proto__属性

对于__proto__属性,它是用来读取或设置当前对象的原型对象,除了提供了__proto__属性,还给出了Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)这两个方法,前者用来设置某个对象的原型,后者用来获取某个对象的原型。可以这么理解,__proto__属性把这两个方法整合在一起,同时拥有读和写的功能。实际上,它本质是一个内部属性,不应该对使用者暴露。因此,应该尽量少用这个属性,而是使用Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)这两个方法进行原型对象的读写操作。

读操作

 function Teacher (age) {
        this.age = age
    }
 var teacher = new Teacher(30)
 // 读
 teacher.__proto__ === Object.getPrototypeOf(teacher) //true

不管是teacher.__proto__或者是Object.getPrototypeOf(teacher),它们其实都指向了Teacher.prototype,所有获取到的值是一样的。

写操作

function Teacher (age) {
        this.age = age
 	}
 	
function Student (age, name) {
        this.age = age
        this.name = name
    } 	
        
var teacher = new Teacher(30)
var student = new Student(19, "张三")
// 用__proto__设置teacher的原型对象
teacher.__proto__ = student
// 用Object.setPrototypeOf()设置teacher原型对象
Object.setPrototypeOf(teacher, student)

console.log(Student.prototype.isPrototypeOf(teacher)) //true

在这里插入图片描述
其实这样写的话,已经出来有链式的感觉,teacher已经有了student的属性。
此时我这样写

console.log(teacher.name)  // 张三

teacher对象中并没有name属性,却打印出了“张三”,而这属性是student实现对象的属性。

原型链

顾名思义,由原型组成;在js中任何一个对象,都可以充当其他对象的原型,那么原型作为一个对象,它也有属于自己的原型,从实例对象到原型,到原型的原型,形成原型链。
首先这条链是有终点的,当找到Object.prototype时,这条原型链就到末尾了,因为Object.prototype的原型是null,由于null是一个“空”的对象,没有任何属性和方法,也就没有prototype属性。
其次当读取对象的某个属性时,js本身会先去找找对象本身有没有该属性,如果没有就去对象的原型上找,如果再找不到就去原型的原型上找,最终都会找到Object.prototype,没有就报undefined。

实际上,在创建了Teacher对象后,就已经形成一条原型链。
在这里插入图片描述
这条链通过__proto__属性很直观就展现出来,当然,__proto__是一个内置属性,不提倡直接使用,使用getPrototypeOf()同样可以达到目的。

console.log(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf(teacher))))  // null

真就是在套娃。

js使用原型链来实现继承

在这里插入图片描述
像上面这样看似有了student的属性,实则如果再创建一个teacher实例的话,就找不到student上的属性了。那怎样才能使每个teacher实例都有Student上的属性和方法呢?构造方法的继承,老师这个职业都是从学生过来的,那么我们可以让Teacher继承Student。

    function Student (age) {
        this.age = age
        this.name = "张三"
    }
    function Teacher (age) {
        Student.call(this);
        this.age = age
    }
    Teacher.prototype = Object.create(Student.prototype);
    Teacher.prototype.constructor = Teacher;

    var teacher = new Teacher(30)
    
    console.log(teacher.name)  // 张三

其实还是操作原型,这里有几点需要注意:
1、Teacher继承Student,Teacher函数里面的this指向是当前函数,我们需要改变this指向,Student.call(this)把this指向父对象Student;这样会让子类实例具有父类实例的属性。
2、改原型,让Teacher的原型指向Student的原型,这里用到Object.create()方法,是为了避免下面代码操作直接修改Student.prototype的指向,该方法是用来创建一个实例对象,接受一个对象作为参数,以它为原型,生成一个实例对象。让Teacher.prototype指向这个实例对象,从而达到指向Student.prototype
3、手动改了原型,要同时修改constructor 属性指向。
在这里插入图片描述
参考文献:
javascript教程:https://wangdoc.com/javascript/oop/prototype.html
es6入门教程:https://es6.ruanyifeng.com

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值