【前端知识整理】原型 原型对象 原型链

原型与原型对象

js继承

class Teacher{
	constructor(name,subject){
        this.name = name
        this.subject = subject
    }
    speak(){
        console.log('我是'+this.name);
    }
    teach(){
    	console.log('我教'+this.subject);
    }
}

class Student{
    constructor(name,score){
        this.name = name
        this.score = score
    }
    speak(){
        console.log('我是'+this.name);
    }
    instroduce(){
        console.log('我考了'+ this.score +'分');
    }
}

以上代码使用class定义了两个类TeacherStudent,他们都有一个共同的属性name以及一个共同的方法speak(),那么我们就可以定义一个父类Person来保存他们共同的属性和方法,于是,我们可以使用extends来让Teacher类和Student类继承Person类,这样,我们就可以将代码改为:

class Person{
	constructor(name){
        this.name = name
    }
    speak(){
        console.log('我是'+this.name);
    }
}

class Teacher extends Person{
	constructor(name,subject){
        super(name)
        this.subject = subject
    }
    teach(){
    	console.log('我教'+this.subject);
    }
}

class Student extends Person{
    constructor(name,score){
        super(name)
        this.score = score
    }
    instroduce(){
        console.log('我考了'+ this.score +'分');
    }
}

原型

如果我们实例化一个Student对象TomTom = new Student('Tom',99),在浏览器的控制台中输入Tom可以看到如下结果:
在这里插入图片描述

我们展开可以看到,这是Tom对象的属性name,和score,以及一个不认识的[[Prototype]],它的值是Student类的父类Person。这个整体就是Tom对象的原型,属性namescore被称为显式原型,而这个[[Prototype]]就被称为隐式原型,在代码和旧版Chrome浏览器中,它应该被写作__proto__,新版的Chrome浏览器改成了现在的样子。
在这里插入图片描述

我们再将这个原型展开之后可以看到,里面是Tom对象所用到的构造函数constructor,以及Student类中包含的方法introduce,以及又一个原型[[Prototype]]。这个原型后面的值为Object,也就是说Person类的原型是Object
在这里插入图片描述
通过hasOwnProperty()方法,我们可以判断对象自身是否拥有某一属性,在下图中可以看到,Tom对象的name属性是自身的,而introduce()方法不是,但是我们可以调用这个方法,因为它在Tom对象的原型上

在这里插入图片描述 在这里插入图片描述

原型对象

如果我们在浏览器中输出Tom.__proto__(注意左右两边都是两个下划线),可以看到如下结果
在这里插入图片描述
和之前看到的[[Prototype]]中的内容一样
我们知道,Tom对象是通过Student类创建的,而Student类有一个属性叫做protoType,我们在浏览器中输出可以看到
在这里插入图片描述
这两个输出的内容一模一样,那么他们是相等的吗?可以看到,确实是相等的,他们指向的是同一个东西,这个东西被称作原型对象
在这里插入图片描述

原型链

之前展开Tom对象的原型后,我们在里面还发现了一个新的原型,再将它展开,可以看到,里面是Student类的父类Person的构造函数constructor以及方法speak(),这个原型里面还有一个原型,因为Person类没有继承别的类,所以它的原型将会是Object,也就是所有原型的终点。
在这里插入图片描述
再继续展开,可以看到很多东西,但是这里面不再有[[Prototype]]
在这里插入图片描述
以上从Tom对象,到Student类,再到Person类,再到Object的所有原型串起来,就形成了原型链。而javascript的继承就是通过访问原型链上的属性和方法来实现的。
当我们要访问一个对象的属性或者方法时,会先在自身上寻找,如果找不到,会去自己的原型上找,还找不到就去原型的原型上去找,以此类推,最终找到这个属性或者方法并进行访问和调用,这个过程就形成了原型链。
例如我们使用Tom.speak()方法时,会现在Tom对象上找这个方法,但是没有找到,于是我们会去Tom对象的原型上,也就是Student类的原型对象上找,但是也没找到,于是我们继续在Student类的原型对象的原型上找,也就是Person类的原型对象,找到了,于是访问调用并输出。
可以通过下图来理解原型链
在这里插入图片描述
图中红色箭头的路径就是原型链,原型链最终会指向一个值为null的原型。

如何利用原型链去判断数据类型

我们一般会使用typeof去判断一个东西的数据类型,但是对于数组和对象,这个方法就不好用了。

let a = []
let b = {}
console.log(typeof a,typeof b);
/*运行结果
object object
*/

可以看到输出的结果都是object,因为数组Array其实也是一个对象,它的原型链上的最后一个类也是Object,那么我们要怎么区别数组和对象呢。
虽然原型链的终点都是Object,但是不同的对象在原型链的中间部分是不一样的,而数组对象Array就有这样一个特别的原型对象Array。于是,我们只要找到了原型对象Array,不就证明这是一个数组吗。
我们可以使用instanceof关键字来判断对象的原型链上是否有这一个原型对象。

let a = []
let b = {}
console.log(a instanceof Array,b instanceof Array);
/*运行结果
true false
*/

可以从输出结果中很清楚的辨别出数组和对象

js中new一个对象的过程

首先我们会想到调用构造函数,但是并不是这么简单,这只是其中一步。
Tom对象为例,第一步是创建一个新对象Tom,然后它会被执行[[prototype]]链接,也就是链接到原型链上,对象被创建时就会带有一个__proto__属性,通过Tom.__proto__ = Student.protoType即可实现链接。然后新对象Tom会和函数调用的this绑定起来,让this关键字指向正确的对象,第四步才是调用构造函数,给对象赋上属性。最后一步,如果构造函数没有返回值,那么将自动返回this
对于最后一步,可以通过以下这个例子理解:

function A(name){this.name = name}
function B(name){this.name = name}
a = new A('a')
b = B('b')

运行结果如下
在这里插入图片描述
可以看到,new关键字会自动给函数返回this,如果没有使用new来创建对象,那么必须要在构造函数中进行return this,才能正确地创建对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值