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__】時需謹慎。