一.JS中原型的定义:
原型是js中函数对象的一个特殊的内置属性,原型中定义了构造函数创造出来的实例对象的公共祖先,所以说,通过构造函数产生的对象,可以继承到该原型的属性和方法。原型也称作原型对象。
二.什么是构造函数:
构造函数在本质上与普通函数没有什么区别,只不过他是使用了new关键字创建对象的函数,所以被称作构造函数
function Person(name,age){
this.name=name;
this.age=age;
}
const per1=new Person('张峻豪',17);
三.原型对象:
js语言并没有提供传统的面向对象语言中的类式继承,也就是说,类不是必须的,对象未必要从类中创建而来,而是通过一种原型委托的方式来实现对象与对象之间的继承,也就是一个对象是通过克隆另外一个对象所得到的,在js中,函数也是一种对象。所以在js中,每一个函数类型的数据都有一个叫做prototype的属性,这个属性指向的是一个对象,也就是我们所说的原型对象。原型对象最主要的作用就是用来存放实例对象的公有属性和公用方法。对于原型对象来说,他有个constructor属性,指向他的构造函数。
下边来看这样一段代码:
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.type='human';
Person.prototype.speak=function(){
console.log("你好啊");
}
const per1=new Person('张峻豪',17);
console.log(per1.type);//human
per1.speak();//你好啊
看到这里,我们就会想到,type和speak明明不是实例per1中的属性,为什么可以直接通过实例访问到呢,这是因为如果在自身找不到属性或者方法,就会查看构造函数的原型对象,如果其中有这个属性或者方法,就会返回这个属性值或者是调用方法。如果在原型对象上也没有找到需要的原型对象或者方法呢?那么这个时候就需要引出原型链了。
四.原型链:
说到原型链,我们需要了解两个概念,显式原型和隐式原型:
4.1显式原型:
显式原型就是利用prototype属性查找原型,也就是函数的原型;
4.2隐式原型:
隐式原型是利用_proto_属性查找原型,也就是对象的原型。
通过如下代码我们可以更直观了解显式原型和隐式原型:
Person.prototype.name="张峻豪";
function Person(){}
const person=new Person()
console.log(person.__proto__);
我们根据代码的结果和给出的定义可以这样理解显示原型和隐式原型:就js得真正实现来说,我们并不能直接说对象有原型,而只能说对象的构造器有原型,构造器一般是一个函数。所以,js给对象提供了一个名为__proto__(双下划线)的隐藏属性,某个对象的__proto__属性会默认指向它的构造器的原型对象,也就是{Constructor}.prototype。如果要验证我们的猜测就可以根据如下代码来验证:
function Person(){}
const person=new Person()
console.log(person.__proto__===Person.prototype);
所以根据我们的猜测和验证就可以得出结论: 实例对象的隐式原型===构造函数的显式原型,本质区别就是这两个属性之间的性质不一样,前者是每个实例对象上都具有的属性,而后者是构造函数的属性。
4.3原型链:
既然__proto__是对象身上的属性,那么原型对象也是对象,原型对象的__proto__指向谁呢?
我们可以这样思考,既然我们要找一个对象身上的__proto__属性,那么我们找到对象的构造函数就可以知道了。而在js中,对象的构造函数就是Object(),所以对象的原型对象就是Object.prototype。只不过原型对象比较特殊,它没有上一层的原型对象,所以,原型对象的__proto__指向的是null。关系图就可以这样来表示:
可以看出,整个过程都是在顺着__proto__属性一步一步向上查找的,形成了像链条的结构,所以说原型链也叫作隐式原型链。也正是因为这个原因,我们在创建对象,数组,函数等数据的时候,都会自带一些属性和方法,这些属性和方法都是在他们的原型上面保存着,所以它们创建起来就可以直接使用那些属性和方法。