淺析JavaScript的原型(prototype)(一)

淺析JavaScript的原型(prototype)(一)。

一. 得到javaScript對象的四種方式的比較

第一種方式:通過new Object得到:沒有類的約束,無法實現對象的重復使用,過於繁瑣

var person=new Object();
person.name="花花";
person.age=17;
person.say=function(){ 
alert("姓名:"+this.name+",年齡:"+this.age);  //必須加this,指向person對象所定義的屬性
};
person.say();
alert(typeof person); 

第二種方式:使用json得到:xml和json的數據可以輔助我們在網絡中傳輸對象數據,但依舊無法實現對對象的重用

var person={
name:"花花",
age:16,
say:function(){
alert(this.name+this.age)
}
};
person.say();

第三種方式:使用工廠模式得到:解決瞭對象無法重用的問題,但是無法判斷的到的對象的類型

function creatPerson(name,age){ //在方法中定義一個對象,將傳遞進來的屬性賦給這個對象
var p=new Object();
p.name=name;
p.age=age;
p.say=function(){
alert(this.name+this.age);
} 
return p;
}
//使用工廠模式的定義方法,有效的解決瞭對象無法重用的問題
var p1=creatPerson("花花",16);
p1.say();
var p2=creatPerson("小草",16);
p2.say();

第四種方式:使用構造函數來創建一個對象:這種方式是JavaScript模擬其他面向對象語言的方式來實現對象的創建 對象重復使用,可判斷類型

//構造構造函數的創建對象的方式和基於工廠的方式類似
//最大區別:函數的名稱就是類的名稱,按面向對象潛規則,首字母大寫,表示這是一個構造函數

function Person(name,age){
this.name=name;  //this.name(變量的屬性)=name(傳遞的形參)
this.age=age;
this.say=function(){
alert("我的名字:"+this.name+",我的年齡"+this.age);
}
}
var p1=new Person("花花",18);
p1.say();
alert(p1 instanceof Person); //true 使用構造函數的好處就是可以使用instanceof來判斷這個對象的類型 

基於構造函數的定義對象的方式存在問題,通過分析:say方法在每個對象創建後都存在一個方法拷貝,(但代碼在隻有調用時,say方法才會在堆中創建),增加內存損耗

解決這個問題的方法:將方法放到全局函數,這樣所有的對象都指向瞭一個方法

function Person(name,age){
this.name=name;  //this.name(變量的屬性)=name(傳遞的形參)
this.age=age;
this.say=say; //讓方法成為全局函數
} 
function say(){
alert("我的名字:"+this.name+",我的年齡"+this.age);
}
var p1=new Person("花花",18);
p1.say();

問題:定義為全局函數,window對象就可調用,破壞瞭對象的封裝性,導致全局變量污染,JavaScript提供瞭一種解決方案,就是基於原型的對象創建方案

二.javaScript的原型(prototype)

1.淺析原型

原型是js中非常特殊一個對象,當一個函數創建之後,會隨之就產生一個原型對象,當通過這個函數的構造函數創建瞭一個具體的對象之後,在這個具體的對象中就會有一個屬性指向原型。

2.用原型創建對象

//定義一個對象
function Person(){};
//使用原型來給對象賦值,將一個對象的屬性和方法放在該對象的原型中,外界無法訪問
Person.prototype.name="xiaoming";
Person.prototype.age=20;
Person.prototype.say=function(){
alert(this.name+this.age);
}
var p1=new Person();
alert(p1.constructor==Person);//true
p1.say();//正常訪問
say();//報錯  window無法訪問,say方法隻屬於Person對象獨有的方法,很好的解決封裝破壞的情況

原型的內存模型圖如下:

3.常見的原型檢測方式:

function Cat(){};
Cat.prototype.name="花貓";
Cat.prototype.say=function(){
console.log(this.name+"愛吃魚");
}
var p1=new Cat();
var p2=new Cat();
p2.name="黑貓";
p2.say();
alert(Cat.prototype.isPrototypeOf(p1));//檢測p1是不是指向Cat的原型對象
alert(p1.constructor==Cat);//檢測p1的構造器是不是指向Cat對象
alert(p2.hasOwnProperty("name"));//檢測p2中有沒有自己的屬性
delete p2.name;
alert("name" in p2);
//雖然刪除瞭自己的name屬性,但是原型中有
//若檢測隻在原型中,不在自己種的屬性
function isPrototypeProperty(property,obj){
if(!obj.hasOwnProperty(property)){
if(property in obj){
return true;
}
}
return false;
}
alert(isPrototypeProperty("name",p1));

三.原型重寫

//定義一個對象
function Person(){};
Person.prototype.name="xiaoming";
Person.prototype.age=20;
Person.prototype.say=function(){
alert(this.name+this.age);
}
var p1=new Person();
alert(p1.constructor==Person);//true
p1.say();//正常訪問
Person.prototype={
name:"huahua",
age:18,
say:function(){
alert(this.name+this.age);
}
}
//此時p1的構造器不再指向p1,而是指向Object,原型重寫導致Person原型被覆蓋
var p1=new Person();
p1.say();//
alert(p1.constructor==Person);//false

//因此我們需要手動指向 在JSON中添加constructor:Person
//原型重寫 JSON
//註意原型重寫時,要緊跟在構造函數之後,因為當有對象指向原先的原型對象時,原先的原型對象不會被釋放

四.封裝–原型創建對象

因為原型存在,我們實現瞭對象的封裝,但是這種封裝也同樣可能存在問題的。
1、 我們無法像使用構造函數的那樣將屬性傳遞用於設置值
2、 當屬性中有引用類型,可能存在變量值的重復

function Person(){};
Person.prototype={
constructor:Person,
name:"huahua",
age:18,
friends:["小寶","明明"],
say:function(){
alert(this.name+this.age+this.friends);
}
}
var p1=new Person();
p1.friends.push("娃娃");
alert(p1.friends);//娃娃
var p2=new Person;
p2.name="xiaocao";
p2.say();
alert(p2.friends);//娃娃  因為p1和p2對象都指向瞭同一個原型鏈,所以當p1屬性值發生變化時,p2也變化

3.終極方案–基於組合的對象定義:需要通過組合的封裝構造函數和原型來實現對象的創建

是目前最常用的一種方式

//基於組合的對象定義
//即屬性在構造方法中定義,方法在原型中定義

function Person(name,age,friends){
this.name=name;
this.age=age;
this.friends=friends;
}
//此時所有的屬性都保存在自己的內存中
Person.prototype={
constructor:Person,
say:function(){
alert(this.name+this.age+this.friends);
}
}
方法定義在原型中
var p1=new Person("花貓",19,["小花","小妹"]);
p1.friends.push("豆豆");
alert(p1.friends);
var p2=new Person("黑貓",19,["小草","小妹"]);
p2.friends.push("南瓜");
alert(p2.friends);

4.終極方案–基於動態原型的對象定義

因為一些面向對象的程序員(如:java、c#)等開發人員他們認為將方法放在外面不像面向對象的寫法,所以提供瞭另一種寫法,在
這裡說說,僅供參考:

//基於動態原型的對象定義
function Person(name,age,friends){
this.name=name;
this.age=age;
this.friends=friends;
//判斷不存在的時候寫
//如果存在就不寫,減少內存消耗
if(!Person.prototype.say){
Person.prototype.say=function(){
alert(this.name+this.age+this.friends);
} 
}
}
var p1=new Person("花貓",19,["小花","小妹"]);
p1.friends.push("豆豆");
alert(p1.friends);
var p2=new Person("黑貓",19,["小草","小妹"]);
p2.friends.push("南瓜");
alert(p2.friends);
//將屬性和方法封裝在所對應的對象中,其他的對象無法訪問
Javascript 面向對象對應一個對象的方式,上述兩種都行,根據個人習慣而定。這也是javascript 中面向對象的封裝。

發佈留言