在一般进行对象定义时,我们往往会首先定义一个类作为现实世界的抽象,然后由类来进行对象的实例化,然而在JS(ES5)中是没有class这个类型的,而是由构造函数、原型以及原型链实现了对象的定义以及继承等。
1.构造函数function Fruit(name){
this.name=name
};
var grape=new Fruit("grape")
grape.color="purple";
console.log(grape.name,grape.color);
// grape purple
上面的Fruit函数即为一个构造函数,我们通过一个new+构造函数的形式来建立了一个对象实例grape,并通过构造函数name参数将grape对象名字设置为“grape”,然后又通过obj.property=value的形式将grape实例的color属性设置为“purple”。
2.prototype与protoprototype是函数的一个属性,并且每个函数都有这个属性,当将其作为构造函数进行对象实例创建的时候,便会将该实例的原型(proto)设置为prototype,并且这些创建的实例共享原型对象的属性,从而实现了继承的效果。
function Fruit(name){
this.name=name
};
Fruit.prototype.color="red";
var melon=new Fruit("melon");
var apple=new Fruit("apple");
console.log(melon.color,apple.color);
// red red
我们将Fruit函数的prototype的color属性设置为“red”,然后由Fruit函数创建的实例都继承了这个属性,将其color进行输出,都输出为red。
__proto__则是属于对象的一个属性,除了null对象为,所有的对象都有__proto__这个属性,这个属性指向了对象的原型,也就是说,构造函数的prototype属性与该构造函数创造出的实例的__proto__属性是完全相同的两个属性,我们通过以下代码来验证:
function Fruit(name){
this.name=name
};
var grape=new Fruit("grape")
console.log(Fruit.prototype===grape.__proto__)
//true
另外每一个构造函数的prototype属性代表的原型都有一个constructor属性,该属性指向了此构造函数。通过以下代码来验证:
function Fruit(name){
this.name=name
};
var grape=new Fruit("grape")
console.log(Fruit.prototype.constructor===Fruit)
//true
接下来我们通过一幅图来更清楚地表明构造函数、对象实例、prototype、__proto__之间的关系。
3.原型链首先解释一下什么是原型链, 每个对象都可以有一个原型__proto__,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找… 这个操作被委托在整个原型链上,这个就是我们说的原型链了。
接下来我们通过代码来具体了解一下原型链到底是什么,这次我们换一个中国男老师的例子来感受。
//中国人的类
function Person(){
this.country="China";
}
//创建中国人实例
var Chinese=new Person()
//将其作为原型建立中国男人的实例
var ChineseMen=Object.create(Chinese);
ChineseMen.gender="male";
//将其作为原型建立中国男老师的实例
var ChineseMenTeacher=Object.create(ChineseMen);
ChineseMenTeacher.job="teacher";
console.log(ChineseMenTeacher.country,ChineseMenTeacher.gender,ChineseMenTeacher.job)
// China Male teacher
console.log(ChineseMenTeacher.__proto__)
//Person{gender:"male"}
console.log(ChineseMen.__proto__)
//Person{country:"China"}
console.log(Chinese.__proto__==Person.prototype)
//true
console.log(Chinese.__proto__.__proto__==Object.prototype)
//true
console.log(Chinese.__proto__.__proto__.__proto__)
//null
我们首先建立了一个构造函数,并且将其“country”属性设置为“China”,然后由此构造函数创建了“Chinese”实例,然后以此为原型创建了ChinessMen对象,并将其gender属性设为“male”,然后以此为原型创建了ChinessMenTeacher对象,并将其job属性设为“teacher”,由此对象有原型,对象的原型还有原型,这样就生成了一条“原型链”。
原型链最底层的对象继承了上面所有层对象的属性,因此ChineseMenTeacher对象也继承了相应的gender属性以及country属性。
然后我们对对象的原型进行输出,ChineseMenTeacher对象的原型为Person{gender:“male”},即为ChineseMen,ChineseMen的对象的原型为Person{country:“China”},即为Chinese,Chinese对象的原型即为其对应构造函数的prototype属性,即Person.prototype。其原型具体为:
我们可以看到Chinese对象的原型有一个__proto__属性,说明其还有原型,并且其原型的__proto__属性里有一个constructor属性,为f Object(),所以Chinese.__proto__的原型对象为Object构造函数的prototype属性。具体原型如下图:
到了Object的prototype在不会再有原型了,其原型为null。
我们再次以图的形式来形象的说明原型链之间的关系,下图黄色的线就是原型链。