JavaScript原型和原型链详解

156 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。

大部分面向对象的编程语言,都是通过“类”(class)来实现对象的继承。但JavaScript严格意义上并不是面向对象的语言, 它的继承是通过“原型对象”(prototype)实现的。

原型(Prototype)

先来放张图:

image.png

上图中涉及到了三个“角色”,构造函数Person,Prototype对象和Person的实例对象person。

 function Person(){
     
 }
 ​
 Person.prototype.say = () => {
     console.log('我可以说话!')
 }
 ​
 Person.prototype.eat = () => {
     console.log('我可以吃饭!')
 }
 ​
 console.log(Person.prototype.constructor)//[Function: Person]
 ​
 let person = new Person();
 ​
 console.log(person.__proto__);//{ say: [Function (anonymous)], eat: [Function (anonymous)] }
 ​
 person.say();//我可以说话!
 person.eat();//我可以吃饭!

看完图和代码,你可以用自己的话来讲述什么是原型了吗?

构造函数(Person)存在一个prototype属性,指向一个对象(就是原型Prototype),这个对象也存在contructor属性,指向构造函数Person,我们可以通过Person.prototype访问并修改原型对象。

当使用new操作符得到一个Person的实例person后,得到的person实例对象存在一个__proto__属性,同样指向构造函数Person指向的原型对象,并且可以直接访问原型对象中的内容,例如say方法和eat方法。

原型链

我们了解了原型的基本概念,原型Prototype本质上也是一个对象,那么对象就会存在__proto__属性,指向自己的原型对象,就和peron一样,也就说原型对象也是另一个构造函数的实例对象,例如Person.prototype其实也是Object构造函数的实例:

 console.log(Person.prototype.__proto__);//[Object: null prototype] {}
 ​
 console.log(person.prototype.__proto__.constructor);//[Function: Object]
 ​
 //等价于
 console.log(person.__proto__.__proto__);//[Object: null prototype] {}
 ​
 console.log(person.__proto__.__proto__.constructor);//[Function: Object]

那么什么时候这条“链子”才到头呢?Object是最顶级的构造函数,那么Object.prototype.__proto__便是尽头了,指向null。

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

image.png

那么现在我们打印peron.toString(),是什么样的流程呢?

 console.log(person.toString())//[object Object]

首先,person现在自己“身上”查找,有没有toString方法,显然没有,那么就顺着原型链,去person.__proto__上找,没有找到,接着去person.__proto__.__proto__上(即Object.prototype)上找,发现存在toString方法,所以person能够调用toString方法。

我们验证一下,在Person.prototype上修改toString()方法:

 Person.prototype.toString = () => {
     return "Person.prototype上的toString!"
 }
 ​
 console.log(person.toString())//Person.prototype上的toString!

同样,我们打印person.sayHello(),这个sayHello方法是我乱编的,所以顺着原型链查找,最总找到Object.__proto__.__proto__上(null),即到了尽头,直接报错。

 console.log(person.sayHello())//TypeError: person.sayHello is not a function