简简单单看懂原型链

引言

从创建一个函数开始,了解函数与函数的prototype,以及对象实例三者之间形成的原型链关系。

一、创建函数

function CreateCar(){}
console.log(CreateCar);
//function CreateCar(){}
复制代码

上面我们创建了一个函数,直接打印函数名,得到的结果是函数本身,好像并没有其他的属性。但实际上,只要函数被创建,函数就会默认的添加一个隐形的prototype属性。

function CreateCar(){}
console.log(CreateCar.prototype);
//{constructor: function CreateCar(){}}
复制代码

打印CreateCar.prototype,得到的结果是一个对象,这个对象就是函数的原型对象。

二、原型对象

原型对象初始只有一个constructor属性,这个属性的值指向的是函数本身。

function CreateCar(){};
console.log(CreateCar.prototype.constructor === CreateCar);
//true
复制代码

原型对象有什么用呢,现在好像看不出来啥,我们把函数当构造函数用试试:

function CreateCar(){};
let car = new CreateCar();
console.log(car);
//{}
console.log(car.varieties)
//undefined
复制代码

可以看到,创建出来的是个空对象,并且调用对象没有的属性时返回的是undefined,接着对函数的原型对象prototype一顿操作:

function CreateCar(){};
CreateCar.prototype.varieties = '小车';

let car1 = new CreateCar();
console.log(car1);
//{}
console.log(car1.varieties);
//小车

let car2 = new CreateCar();
console.log(car2);
//{}
console.log(car2.varieties);
//小车
复制代码

创建的对象car1和car2都是空对象,但是打印它们的varieties都有值且都为’小车’,可以明显发现,函数原型对象prototype上的属性,是函数作为构造函数时创建出的对象所共有的。但为什么car1和car2都是空对象,但是却都可以打印出varieties的值呢?这就和原型链扯上关系了。

三、原型链

首先,我们知道一个构造函数创建出来的对象,对象上的属性是由函数体内的语句定义的。这些属性我们可以直接看见并打印,请看如下示例。

function CreateCar(color){
  this.color = color;
};

let car1 = new CreateCar('白色');
console.log(car1);
//{color: "白色"}

let car2 = new CreateCar('黑色');
console.log(car2);
//{color: "黑色"}
复制代码

然后我们已经知道,函数的原型对象上的属性,虽然在对象上看不到,但是会被所有该函数创建出来的对象公用,看如下示例。

function CreateCar(color){
  this.color = color;
};
CreateCar.prototype.varieties = '小车';
console.log(CreateCar.prototype);
//{varieties: "小车", constructor: function CreateCar(color){this.color = color;}}

let car1 = new CreateCar('白色');
console.log(car1);
//{color: "白色"}
console.log(car1.varieties);
//小车

let car2 = new CreateCar('黑色');
console.log(car2);
//{color: "黑色"}
console.log(car2.varieties);
//小车
console.log(car2.numberOfwheels);
//undefined
复制代码

varieties属性虽然不在对象内展示,但是car1和car2都可以打印出来,这是因为打印varieties的时候,car1和car2都会先在自己身上找varieties,找不到就会去自己构造函数CreateCar的prototype上找(通过__proto__建立关系),找到了就打印出来。而打印car2.numberOfwheels时候,同样car2首先会在自身找,找不到就去构造函数CreateCar的prototype上找,这时候找不到还会继续向上去原生构造函数Object的prototype上找,此时还找不到,就返回undefined了,下面图示这一系列关系。

1.png

那如果对象自身属性和原型对象中属性重名会怎样

function CreateCar(){
  this.varieties = '大车'
};
CreateCar.prototype.varieties = '小车';
let car = new CreateCar();
console.log(car);
//{varieties: "大车"}
复制代码

可以看到,会优先打印就近找到的属性值。

总结

  1. 每个函数都有一个原型对象prototype。
  2. 函数原型对象prototype上的属性会被所有该函数创建出来的对象公用。
  3. 调用对象某个值,寻找顺序为:对象自身 → 对象构造函数的prototype → 原生构造函数的prototype → null,这条链即为原型链。
  4. 当对象自身属性和构造函数原型对象的属性重名时,会优先打印就近找到的属性值。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享