標籤:bsp job highlight turn 為什麼 執行個體化 javascrip create www
學了這麼久還是對物件導向有點模糊,所以今天就再寫一點關於物件導向的
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);//父親、母親、妹妹、didialert(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複雜創造物件導向方法