1、每个函数都有一个【prototype】属性,这个属性其实是个指针,指向的是该函数的原型对象,因此,我们可以通过这个属性设置和访问该函数的原型对象,如:
function F() {
this.name = "Xiao Ming";
}
F.prototype = {
sayName : function() {
return this.name;
}
};
console.log(F.prototype); //{sayName: ƒ}
2、每个对象都有一个【__proto__】属性,而这个属性其实是一个浏览器(Firefox、Safari、Chrome、IE11等浏览器有写入)的内置属性,并非正式的开放API,这个属性也是一个指针(即是对指针【[[Prototype]]】的一种访问实现),指向该对象的原型对象(原型对象的默认属性和方法是从【Object】继承而来的),所以我们也可以通过这个属性来设置和访问该对象的原型对象,如:
var obj = {
name : "Xiao Ming"
};
obj.__proto__ = {
sayName : function() {
return this.name;
}
};
console.log(obj); //{name: "Xiao Ming"}
console.log(obj.sayName()); //Xiao Ming
console.log(obj.__proto__); //{sayName: ƒ}
由于这个属性并非可用的正式API,故,在ES5中新增了【Object.setPrototypeOf()】方法来实现为指定对象设置原型对象,和【Object.getPrototypeOf()】方法来实现获取指定对象的原型对象。在上面代码的基础上测试如下:
Object.setPrototypeOf(obj, {
stop : function() {
return;
}
});
console.log(Object.getPrototypeOf(obj)); //{stop: ƒ}
console.log(obj.__proto__); //{stop: ƒ}
结合1&2补充说明:函数默认没有【__proto__】属性,对象默认没有【prototype】属性。
3、说说构造函数与实例在原型对象上的关系,如下:
function F() {
this.name = "Xiao Ming";
}
F.prototype = {
sayName : function() {
return this.name;
}
};
var f = new F();
现有构造函数【F】和其实例【f】。我们知道,实例的内部存在一个指针,指向所属构造函数的原型对象,因此,构造函数的所有实例便可以共享构造函数原型对象上的属性和方法。而实例又是一个对象:
console.log(typeof f); //object
根据上面 2 中所述,每个对象都有自己的内部指针【__proto__】,指向该对象的原型对象,通过:
console.log(f.__proto__); //{sayName: ƒ}
console.log(Object.getPrototypeOf(f)); //{sayName: ƒ}
我们可以看到【f】的【__proto__】的内容就是【F】的原型对象的内容,即实例【f】这时的原型对象就是【F】的原型对象了(这一句表达的好像有点不准确,大概意思就是这样的)。当然,我们可以通过修改【__proto__】的指向来改变实例的原型对象的内容,如:
f.__proto__ = {
age : 0
};
//或者使用:
//Object.setPrototypeOf(f, {
// age : 0
//});
console.log(Object.getPrototypeOf(f)); //{age: 0}
console.log(F.prototype); //{sayName: ƒ}
并且不会影响到【F】的原型对象,因为这是直接改变了【f】的指针指向。但如果通过【__proto__】来修改原型对象的属性的话,就会影响到【F】的原型对象了,如:
f.__proto__.sayName = "修改后";
console.log(F.prototype); //{sayName: "修改后"}
当然,这是由于原型对象的值是引用类型导致的,所用实例要使用【__proto__】时需谨慎。