[JavaScript] 搞懂原型链(上)

    在上一篇文章中,我们弄明白了new操作和Object.create()的原理以及区别,那么不知道你们有没有这个疑问:在Js中,实例对象继承构造函数的构造器属性和原型上的属性的机制是什么呢?这就需要我们来学习**原型链**的知识了

一. 引子

​ 我们先来看一个简单的例子

function Person(){
    
}
let person = new Person();
person.name = 'Zong'
console.log(person.name) //zong
						 //注意,这里的person.name不是从Person.prototype.name继承得来的,而是我们直接赋值给person的name属性		

​ 在这个例子中,我们使用Person这个构造函数,通过new操作创建了person这个实例对象,我们在上一篇文章中讲到,new操作创建了实例对象,实例对象的__proto__属性会指向构造函数的prototype属性,写成代码就是

person.__proto__ === Person.prototype // true

这其中的__proto__和prototype究竟是什么呢?

二. prototype 、 __proto__ 、 constructor

​ 首先我们要知道:

  1. __proto__和constructor属性是对象所独有的,函数实际上也是对象,因此函数也有这两个属性
  2. prototype属性是函数所独有的

​ 举个例子

function Person(){
    
}
Person.prototype.name = "zong";
let person = new Person();
console.log(Person.prototype);
console.log(Person.constructor);
console.log(Person.__proto__);//报错:Invalid or unexpected token

console.log(person.prototype);//undefined
console.log(person.constructor);
console.log(person.__proto__);

​ 先不管打印出来的是什么,我们可以看到只有对象的prototype属性是undefined

​ 我们一直再说原型链,顾名思义,这是一条链子一样的东西,在链子上就拴着对象的constructor,__proto__属性和函数的prototype属性,这些属性相互之间的关系就构成了原型链

三. 原型

​ prototype作为函数独有的属性,它到底指向什么呢?

​ 其实prototype属性指向了一个对象,这个对象正是调用构造函数时创建的实例对象的原型。即:

Person.prototype指向原型

​ 原型其实就是每一个Js对象在创建时会与之关联的另一个对象,在创建这些对象时,会从原型中“继承”属性,是不是很奇妙,和我们之前讲到的new操作联系上了

​ Person.prototype这个对象指向的就是实例对象的原型,那么实例对象怎么访问原型呢?这时候就要通过实例对象的__proto__属性了,即:

person.__proto__ === Person.prototype // true

​ 这里可能会有一些绕,我们通过图来理解

原型链1

四. constructor

​ 既然构造函数和实例对象都可以指向原型,那么原型是否可以指向构造函数或实例对象呢,答案是:原型可以指向构造函数,但不能指向实例对象,因为一个构造函数可以创建多个实例对象。

​ 这时候就可以使用到我们前面提到的constructor属性,还是用上面的例子

Person.prototype.constructor === Person;// true

所有我们的原型链图更新为:

原型链2

五. 原型链

​ 到了这里你可能又会有疑惑了,我们说的是说原型链,但是上面的图明明就是一个环呀,哪里来的链呢?

​ 不要忘记了原型也是一个对象,他也有__proto__属性,原型的__proto__属性指向哪里呢?当然是原型的原型啦。

举个例子:

function Person() {

}
Person.prototype.name = '即来';
Person.prototype.__proto__.name = "则安"
var person = new Person();
person.name = 'zong';
console.log(person.name) // zong
delete person.name;
console.log(person.name) // 即来
delete Person.prototype.name;
console.log(person.name);//则安

​ 在上面的例子中,我们可以看到person.name是person本身的属性时输出zong,当我们删掉person自身的name属性时,person.name输出“即来”,这是person.__proto__的name属性,也就是person原型上的属性

​ 当我们把原型上的name属性也删掉时,输出“则安”,这是person原型的原型上的属性,一条原型链就这么出现了。

​ 由于原型对象是通过Object构造函数生成的,所有原型链的倒数第二站是Object.prototype

​ 那么寻找原型的原型一直往下到最后一站是什么呢?答案时null,即

Object.prototype.__proto__ === null // true

​ 我们来更新一下原型链图

原型链3

六. 小结

​ 由原型间相互关联组成的链状结构就是原型链,这也是JavaScript“继承”的奥秘所在,与其说是继承,不如说是给了一个坐标让实例对象能访问到所需要继承的属性。

​ 因为在我们的认知,继承即复制,但在Js中并不是复制,打个比方,可能这个比方并不是很恰当:就像我们都知道有一句话叫

授人以鱼不如授人以渔

​ Js的原型链就像这样,我并不直接把东西给你,而是告诉你找到东西的方法,让你顺着原型链能找到。

​ 这篇文章就写到这里,可能你还有一些疑问,比如有关Function.prototype.__proto__指向Object.prototype,而Object.__proto__又指向Function.prototype,这里的关系是不是很有些乱呢?让我们留到下次再来探讨。

posted @ 2021-05-14 00:40  是棕啊  阅读(82)  评论(0编辑  收藏  举报