js復雜創造面向對象方法
學了這麽久還是對面向對象有點模糊,所以今天就再寫一點關於面向對象的
function Box(){}
var box=new Box();
alert(box.prototype) //undifined 使用對象實例無法訪問到prototype
alert(box.__proto__);//object object 使用對象實例訪問prototype的指針
alert(Box.prototype);//object object 使用構造函數名(對象名)訪問prototype
說明對象的實例是無法直接通過prototype屬性訪問該構造方法的原型的,但是對象實例可以通過訪問prototype的指針的方式來訪問,同時構造函數(即對象名) 訪問prototype。
二、為了讓屬性和方法更好的體現封裝效果,並減少不必要的輸入,原型對象也可以用字面量的方式來創建,代碼如下
function Box(){}
Box.prototype={ ①
name:‘Lee‘,
age:‘27‘,
run:function(){
return this.name+this.age;
}
}
註意:①處的{}其實就是Object,new Object就相當於{}
使用構造函數創造源性對象與字面量創造原型對象在使用上基本相同,但是有一些區別,那就是 用字面量的方式 使用constructor屬性不會指向實例(Box),而是會指向Object,構造函數的方式則不會出現這個問題,會指向(Box),具體看下面代碼
var box=new Box();
alert(box.constructor)//function Object{}
ps:字面量的方式為什麽會是constructor屬性指向Object呢?那是因為,Box.prototype={}這種方式其實是創建了一個新對象,而每創建一個對象,就會同時創建他的prototype對象,這個對象也會自動獲取constructor屬性,所以新對象的constructor重寫了Box原來的constructor,所以會指向新對象,那個新對象沒有指定構造函數,所以就默認為Object了
當然也有辦法讓他的constructor重新指向Box,看如下代碼
function Box(){}
Box.prototype={
constructor:Box, //強制指向Box
name:‘Lee‘,
age:‘27‘,
run:function(){
return this.name+this.age;
}
}
var box=new Box();
alert(box.constructor)//function Box{}
用constructor:Box,就會讓他重新 指向 Box實例
三、原型的聲明是有先後的,所以重寫的原型,會切斷之前的原型
function Box(){}
Box.prototype={
constructor:Box, //強制指向 Box
name:‘Lee‘,
age:‘27‘,
run:function(){
return this.name+this.age;
}
}
Box.prototype={ //重寫了原型對象,這裏不會保留之前的原型的任何信息
age:‘100‘
}
var box=new Box();
alert(box.name);//undifined
這個很好理解,第二次Box.prototype把第一次的給重寫了,表面上是改了 age的值,其實是 把整個原型都重寫了,所以上面的原型徹底與構造函數實例切斷,box.name在第二次根本不存在,因此,undifined也是自然而然的事情了
// //查詢sort是否是Array源性對象裏的方法
// alert(Array.prototype.sort);
// alert(String.prototype.substring);
// 給String添加方法
String.prototype.addString = function() { //盡管給原生的內置引用類型添加方法特別方便,但我們不推薦這種方法
//因為他可能導致命名沖突,不利於代碼維護
return this + "被添加了、、、";
}
alert("xixida".addString()); //xixida被添加了、、、
原型+構造方法
一、我們都知道原型模式構建對象的最大優點就是共享,但是你知道嗎,原型模式最大的缺點就是它最大的優點,如果共享的是方法的話使我們期望的一般也不會有什麽影響,但是如果牽扯到其他的引用類型的話就會出現麻煩,看如下;
//原型的缺點 function Box() {} Box.prototype={ constructor:Box, name:‘Lee‘, age: 20, family:[‘哥哥‘,‘姐姐‘,‘妹妹‘] } var box1 = new Box(); var box2 = new Box(); alert(box1.family);//哥哥、姐姐、妹妹 box1.family.push(‘弟弟‘); 在第一個實例修改後的引用類型,保持了共享 alert(box1.family);//哥哥、姐姐、妹妹、弟弟 alert(box2.family)//哥哥、姐姐、妹妹、弟弟 //共享了box1添加後的引用類型
二、為解決這個問題,我們可以使用組合原型模式+構造函數
function Box(name, age) { //不共享的使用構造函數 this.name = name; this.age = age; this.family = [‘父親‘,‘母親‘,‘妹妹‘] } Box.prototype={ //共享的使用原型模式 constructor:Box, run:function(){ return this.name+this.age; } } var box1 = new Box(‘xixi‘,10); var box2 = new Box(‘Jack‘,20); alert(box1.family); //父親、母親、妹妹 box1.family.push(‘didi‘); alert(box1.family);//父親、母親、妹妹、didi alert(box2.family);//父親、母親、妹妹 引用類型沒有使用原型,所以沒有共享
ps:這種混合方式很好的解決了傳參和引用共享的兩大難題,是創建對象比較好的方法
三、上面的方式雖然一斤很完美了,但是 構造函數和原型模式 兩塊代碼來 創造對象有點怪異,為了更好的體現封裝性我們把它寫在一起
//動態原型模式
//可以將原型封裝到構造函數裏 function Box(name, age) { //不共享的使用構造函數 this.name = name; this.age = age; this.family = [‘父親‘,‘母親‘,‘妹妹‘]; if(typeof this.run != "function"){ //原型的初始化,只要第一次初始化了就行了,沒必要每次構造函數實例化的時候都初始化 Box.prototype.run=function(){ return this.age+this.name; } } }
如果以上的各種創建方式都不能滿足你的需求,那就使可以嘗試一下兩種方式
①寄生構造函數=工廠模式+構造函數寄生.(構造函數模式和工廠模式沒有本質區別,通過new 操作符的就叫寄生構造函數模式,直接調用的就叫工廠模式)
1 function Box(name,age){ 2 var obj = new Object(); 3 obj.name = name; 4 obj.age = age; 5 obj.run=function(){ 6 return this.name+this.age; 7 } 8 return obj; 9 }
寄生構造函數模式與工廠模式的區別是使用了new操作符把createPerson函數作為構造函數,其實是跟工廠模式一模一樣的
②穩妥構造函數
1 // 在一些安全環境中,比附禁止使用this 和 new,(這裏的this指的是不能在構造函數裏面使用), 2 // new指的是不能在外部實例化構造函數的時候使用,這種方式叫做穩妥構造函數 3 function Person(name, age, job) { 4 var o = new Object(); 5 o.name = name; 6 o.age = age; 7 o.job = job; 8 o.sayName = function() { 9 alert(name); //註意這裏沒有了“this”; 10 }; 11 return o; 12 } 13 14 var person = Person("Nicholas", 29, "software Engineer"); 15 Person.sayName(); //"Nicholas"
穩妥構造函數采用的是與寄生構造函數模式相似的模式,除了下列兩點不同:
1.創建對象的實例方法不引用this;
2.不使用new調用構造函數;
所以構造函數適合在禁用this和new的環境中使用(或者說設計的出發點)。
js復雜創造面向對象方法