js面向對象、創建對象的工廠模式、構造函數模式、原型鏈模式
JS面向對象編程(轉載)
什麽是面向對象編程(OOP)?用對象的思想去寫代碼,就是面向對象編程。
面向對象編程的特點
- 抽象:抓住核心問題
- 封裝:只能通過對象來訪問方法
- 繼承:從已有對象上繼承出新的對象
- 多態:多對象的不同形態
對象的組成
- 屬性:對象下面的變量叫做對象的屬性
- 方法:對象下面的函數叫做對象的方法
var arr = []; arr.number = 10; //對象下面的變量:叫做對象的屬性 //alert( arr.number ); //alert( arr.length ); arr.test = function(){ //對象下面的函數 : 叫做對象的方法 alert(123); }; arr.test();//方法 arr.push();//方法 arr.sort();
創建一個對象
var obj=new Object();//創建一個空的對象 obj.name=‘小明‘; //屬性 obj.showName=function(){ //方法 alert(this.name);//this指向obj } obj.showName();//小明
如果需要創建兩個或多個對象
var obj1=new Object();//創建一個空的對象 obj1.name=‘小明‘; //屬性 obj1.showName=function(){ //方法 alert(this.name);//this指向obj } obj1.showName();//小明 var obj2=new Object();//創建一個空的對象 obj2.name=‘小灰‘; //屬性 obj2.showName=function(){ //方法 alert(this.name);//this指向obj } obj2.showName();//小灰
使用Object函數或對象字面量都可以創建面向對象,但需要創建多個對象時,會產生大量的重復代碼,可通過工廠方式來解決這個問題
工廠方式 -------------------- 面向對象中的封裝函數
//工廠方式 : 封裝函數 function createPerson(name){ var obj = new Object(); obj.name = name; obj.showName = function(){ alert( this.name ); }; return obj; } var p1 = createPerson(‘小明‘); p1.showName(); var p2 = createPerson(‘小強‘); p2.showName();
創建對象用工廠方式來實現,可以傳遞參數,由於創建對象都是使用Object的原生構造函數來實現的,因此無法識別對象類型
構造函數模式 -------------------- 給一個對象添加方法
//new 後面調用的函數叫構造函數 function CreatePerson(name){ this.name=name; this.showName=function(){ alert(this.name); } } var p1=new CreatePerson(‘小明‘);//當new去調用一個函數時,函數中的this就是創建出來的對象而函數中的返回值就是this p1.showName(); var p2=new CreatePerson(‘小強‘); p2.showName();
使用自定義的構造函數,定義對象類型的屬性和方法,與工廠方式的區別:
- 沒有顯式的創建對象
- 直接將屬性和方法賦給this對象
- 沒有return語句
上面例子中:CreatePerson構造函數生成的兩個對象p1與p2都是CreatePerson的實例
雖然構造函數解決了上面工廠方式的問題,但是它一樣存在缺點,就是在創建對象時,每個對象都有一套自己的方法,每定義一個函數都實例化了一個對象
例如:
function CreatePerson(name){ this.name = name; this.showName = function(){ alert( this.name ); }; } var p1 = new CreatePerson(‘小明‘); //p1.showName(); var p2 = new CreatePerson(‘小強‘); //p2.showName(); alert( p1.showName == p2.showName ); //false 它們的值相同,地址不同
測試例子中的p1.showName與p2.showName是否會相等,彈出的結果是false,說明p1和p2實例都包含一個不同的showName實例
再來舉幾個例子:
var a = [1,2,3]; var b = [1,2,3]; alert( a == b ); //false 值相同,地址不同 var a = 5; var b = a; b += 3 alert(b); //8 alert(a); //5 基本類型 : 賦值的時候只是值的復制
var a = [1,2,3]; var b = a; b.push(4); alert(b); //[1,2,3,4] alert(a); //[1,2,3,4] 對象類型 : 賦值不僅是值的復制,而且也是引用的傳遞
var a = [1,2,3]; var b = a; b = [1,2,3,4]; alert(b); //[1,2,3,4] alert(a); //[1,2,3]
對比上面的幾個例子,不難看出基本類型和對象類型的區別了,對象類型的賦值不僅是值的復制,也是引用的傳遞;提到了對象的引用應該很清楚上述p1.showName==p2.showName為何會返回結果是false
原型模式(prototype) -------------------- 給一類對象添加方法
原型(prototype):重寫對象下面公用的屬性或方法,讓公用的屬性或方法在內存中只存在一份(提高性能),也就是說所有在原型對象中創建的屬性或方法都直接被所有對象實例共享。
- 原型:類比css中的class
- 普通方法:類比css中的style
var arr = [1,2,3,4,5]; var arr2 = [2,2,2,2,2]; Array.prototype.sum = function(){//原型prototype : 要寫在構造函數的下面 var result = 0; for(var i=0;i<this.length;i++){ result += this[i]; } return result; }; alert( arr.sum() ); //15 alert( arr2.sum() ); //10
原型優先級:如果在實例中添加了一個屬性,而該屬性與實例原型中的一個屬性同名,該屬性將會屏蔽原型中的那個屬性
例子1:
var arr = []; arr.number = 10; Array.prototype.number = 20; alert(arr.number);//10
例子2:
Array.prototype.a=12;//原型屬性 var arr=[1,2,3]; alert(arr.a);//12 arr.a=5;//實例屬性 alert(arr.a);//5
工廠方式之原型
function CreatePerson(name){//普通方法 this.name=name; } CreatePerson.prototype.showName=function(){//原型 alert(this.name); } var p1=new CreatePerson(‘小明‘); p1.showName(); var p2=new CreatePerson(‘小強‘); p2.showName(); alert( p1.showName== p2.showName);//true
由上述例子中:p1.showName== p2.showName彈出的結果是true,可見原型解決了構造函數中“每定義一個函數都實例化了一個對象”的問題
原型的運用
選項卡實例:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>選項卡</title> <style> #div1 div{ width:400px; height:300px; border:1px solid #ccc; overflow: hidden; display: none; margin: 15px 0; } #div1 input{ color: #fff; width:100px; height:40px; background: darkseagreen; border:none; font-size: 14px; letter-spacing: 5px; } #div1 p{ font-size: 20px; line-height: 24px; text-align: center; color:darkgreen; } #div1 .title{ padding: 0; font-weight: bold; } #div1 .active{ background:sandybrown; color:#fff; } </style> <script> window.onload=function(){ var oDiv=document.getElementById(‘div1‘); var aInput=oDiv.getElementsByTagName(‘input‘); var aDiv=oDiv.getElementsByTagName(‘div‘); var i=0; for(i=0;i<aInput.length;i++){ aInput[i].index=i; aInput[i].onmousemove=function(){ for(var i=0;i<aInput.length;i++){ aInput[i].className=‘‘; aDiv[i].style.display=‘none‘; } aInput[this.index].className=‘active‘; aDiv[this.index].style.display=‘block‘; } } } </script> </head> <body> <div id="div1"> <input class="active" type="button" value="五言律詩"> <input type="button" value="七言律詩"> <input type="button" value="五言絕句"> <input type="button" value="七言絕句"> <div style="display: block;"> <p class="title">落 花</p> <p class="author">李商隱</p> <p>高閣客竟去,小園花亂飛。</p> <p>參差連曲陌,迢遞送斜暉。</p> <p>腸斷未忍掃,眼穿仍欲歸。</p> <p>芳心向春盡,所得是沾衣。</p> </div> <div> <p class="title">蜀 相</p> <p class="author">杜甫</p> <p>丞相祠堂何處尋,錦官城外柏森森。</p> <p>映階碧草自春色,隔葉黃鸝空好音。</p> <p>三顧頻煩天下計,兩朝開濟老臣心。</p> <p>出師未捷身先死,長使英雄淚滿襟。</p> </div> <div> <p class="title">八陣圖</p> <p class="author">杜甫</p> <p>功蓋三分國,名成八陣圖。</p> <p>江流石不轉,遺恨失吞吳。</p> </div> <div> <p class="title">泊秦淮</p> <p class="author">杜牧</p> <p>煙籠寒水月籠沙,夜泊秦淮近酒家。</p> <p>商女不知亡國恨,隔江猶唱後庭花。</p> </div> </div> </body> </html>
效果(鼠標經過按鈕時選項卡切換):效果圖弄不上(技術欠缺。。。)
面向對象選項卡:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>選項卡</title> <style> #div1 div,#div2 div{ width:400px; height:300px; border:1px solid #ccc; overflow: hidden; display: none; margin: 15px 0; } #div1 input,#div2 input{ color: #fff; width:100px; height:40px; background: darkseagreen; border:none; font-size: 14px; letter-spacing: 5px; } #div1 p,#div2 p{ font-size: 20px; line-height: 24px; text-align: center; color:darkgreen; } #div1 .title,#div2 .title{ padding: 0; font-weight: bold; } #div1 .active,#div2 .active{ background:sandybrown; color:#fff; } </style> <script> window.onload=function(){ var t1=new TabSwitch(‘div1‘); t1.switch(); var t2=new TabSwitch(‘div2‘);//面向對象的復用性 t2.switch(); t2.autoPlay(); /*alert(t2.switch==t1.switch);//ture*/ } function TabSwitch(id){ this.oDiv=document.getElementById(id); this.aInput=this.oDiv.getElementsByTagName(‘input‘); this.aDiv=this.oDiv.getElementsByTagName(‘div‘); this.iNow=0;//自定義屬性 } TabSwitch.prototype.switch=function(){//原型 for(var i=0;i<this.aInput.length;i++){ var This=this;//將指向面向對象的this保存下來 this.aInput[i].index=i; this.aInput[i].onmousemove=function(){ This.tab(this);//This指向面向對象 this指向this.aInput[i] } } } TabSwitch.prototype.tab=function(obj){//原型 for(var i=0;i<this.aInput.length;i++){ this.aInput[i].className=‘‘; this.aDiv[i].style.display=‘none‘; } this.aInput[obj.index].className=‘active‘; this.aDiv[obj.index].style.display=‘block‘; } //自動播放 TabSwitch.prototype.autoPlay=function(){ var This=this; setInterval(function(){ if(This.iNow==This.aInput.length-1){ This.iNow=0; } else{ This.iNow++; } for(var i=0;i<This.aInput.length;i++){ This.aInput[i].className=‘‘; This.aDiv[i].style.display=‘none‘; } This.aInput[This.iNow].className=‘active‘; This.aDiv[This.iNow].style.display=‘block‘; },1000); } </script> </head> <body> <div id="div1"> <input class="active" type="button" value="五言律詩"> <input type="button" value="七言律詩"> <input type="button" value="五言絕句"> <input type="button" value="七言絕句"> <div style="display: block;"> <p class="title">落 花</p> <p class="author">李商隱</p> <p>高閣客竟去,小園花亂飛。</p> <p>參差連曲陌,迢遞送斜暉。</p> <p>腸斷未忍掃,眼穿仍欲歸。</p> <p>芳心向春盡,所得是沾衣。</p> </div> <div> <p class="title">蜀 相</p> <p class="author">杜甫</p> <p>丞相祠堂何處尋,錦官城外柏森森。</p> <p>映階碧草自春色,隔葉黃鸝空好音。</p> <p>三顧頻煩天下計,兩朝開濟老臣心。</p> <p>出師未捷身先死,長使英雄淚滿襟。</p> </div> <div> <p class="title">八陣圖</p> <p class="author">杜甫</p> <p>功蓋三分國,名成八陣圖。</p> <p>江流石不轉,遺恨失吞吳。</p> </div> <div> <p class="title">泊秦淮</p> <p class="author">杜牧</p> <p>煙籠寒水月籠沙,夜泊秦淮近酒家。</p> <p>商女不知亡國恨,隔江猶唱後庭花。</p> </div> </div> <div id="div2"> <input class="active" type="button" value="五言律詩"> <input type="button" value="七言律詩"> <input type="button" value="五言絕句"> <input type="button" value="七言絕句"> <div style="display: block;"> <p class="title">落 花</p> <p class="author">李商隱</p> <p>高閣客竟去,小園花亂飛。</p> <p>參差連曲陌,迢遞送斜暉。</p> <p>腸斷未忍掃,眼穿仍欲歸。</p> <p>芳心向春盡,所得是沾衣。</p> </div> <div> <p class="title">蜀 相</p> <p class="author">杜甫</p> <p>丞相祠堂何處尋,錦官城外柏森森。</p> <p>映階碧草自春色,隔葉黃鸝空好音。</p> <p>三顧頻煩天下計,兩朝開濟老臣心。</p> <p>出師未捷身先死,長使英雄淚滿襟。</p> </div> <div> <p class="title">八陣圖</p> <p class="author">杜甫</p> <p>功蓋三分國,名成八陣圖。</p> <p>江流石不轉,遺恨失吞吳。</p> </div> <div> <p class="title">泊秦淮</p> <p class="author">杜牧</p> <p>煙籠寒水月籠沙,夜泊秦淮近酒家。</p> <p>商女不知亡國恨,隔江猶唱後庭花。</p> </div> </div> </body> </html>
效果(第二個選項卡加了一個自動切換功能):效果圖弄不上(技術欠缺。。。)
面向對象中this的問題
一般會出現問題的情況有兩種:
- 定時器
- 事件
例子1:
//定時器 function Aaa(){ var _this=this;//將當前this值保存 this.a=12; setInterval(function(){//定時器中this指向window _this.show(); },1000); } Aaa.prototype.show=function(){ alert(this.a); } var obj=new Aaa();//12
例子2:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>面向對象中this的問題-----事件</title> <script> function Bbb(){ var _this=this; this.b=5; document.getElementById(‘btn1‘).onclick=function(){//點擊事件 _this.show(); } }
Bbb.prototype.show=function(){ alert(this.b); } window.onload=function(){ var p2=new Bbb(); } </script> </head> <body> <input id="btn1" type="button" value="按鈕"> </body> </html>
上面兩個是分別對定時器和事件中this問題的解決方法,即將指向對象的this保存到了_this中,在嵌套函數中調用對象的方法或屬性時用 _this.屬性 或 _this.方法
再來個實例:
拖拽效果:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>最初寫的拖拽效果</title> <style> #div1{ width:100px; height:100px; background: red; position: absolute; } </style> <script> window.onload=function(){ var oDiv=document.getElementById(‘div1‘); oDiv.onmousedown=function(ev){ var oEvent=ev||event; var disX=0; var disY=0; var disX=oEvent.clientX-oDiv.offsetLeft; var disY=oEvent.clientY-oDiv.offsetTop; document.onmousemove=function(ev){ var oEvent=ev||event; oDiv.style.left=oEvent.clientX-disX+‘px‘; oDiv.style.top=oEvent.clientY-disY+‘px‘; }; document.onmouseup=function(){ document.onmousemove=null; document.onmouseup=null; }; return false; } } </script> </head> <body> <div id="div1"></div> </body> </html>
面向對象的拖拽
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>面向對象寫的拖拽效果</title> <style> #div1{ width:100px; height:100px; background: red; position: absolute; } </style> <script> window.onload=function(){ var p=new Darg(‘div1‘); p.init(); } function Darg(id){ this.oDiv=document.getElementById(id); //屬性 this.disX=0;//屬性 this.disY=0;//屬性 } Darg.prototype.init=function(){//原型 方法 var This=this; this.oDiv.onmousedown=function(ev){ var oEvent=ev||event; This.fnDown(oEvent); return false; } } Darg.prototype.fnDown=function(ev){//原型 方法 var This=this; this.disX=ev.clientX-this.oDiv.offsetLeft; this.disY=ev.clientY-this.oDiv.offsetTop; document.onmousemove=function(ev){ var oEvent=ev||event; This.fnMove(oEvent); }; document.onmouseup=function(){ This.fnUp(); }; } Darg.prototype.fnMove=function(ev){//原型 this.oDiv.style.left=ev.clientX-this.disX+‘px‘; this.oDiv.style.top=ev.clientY-this.disY+‘px‘; } Darg.prototype.fnUp=function(){//原型 document.onmousemove=null; document.onmouseup=null; } </script> </head> <body> <div id="div1"></div> </body> </html>
效果(拖動紅色方塊可以移到任何位置): 效果圖弄不上(技術欠缺。。。)
https://www.cnblogs.com/jnslove/p/7028487.html
上邊是原網址
下面說說,目前對面向對象的理解:
自己有點感覺,js面向對象有點像 模塊化思想,將一個通用方法封裝到對象的通用方法內,誰想用就能用。如果做同類型的事,只是極個別屬性不同,而且需要做大量這樣同類型的事,那麽用面向對象的方式寫代碼會比較省代碼,因為不需要每一個細節代碼都要重寫一遍,只需要用對象裏封裝好的方法即可。占時是這樣理解的。
下面說說,工廠模式、構造函數模式、原型鏈模式的異同,以及為什麽誕生
我們創建對象時,裏面的屬性名相同,就是值有點差異,如果一個一個創建,顯得有點耗時又費力,於是,我們把創建同類對象的方法封裝起來,不同的值用參數傳給這個封裝方法,這樣創建起來同類對象就非常方便快捷,這樣的方式成為(工廠模式);
很明顯,這樣創建出來的對象,看不出來是屬於什麽類型的,為了解決這個問題,(構造函數模式)誕生了。構造函數的 函數名一眼就可以看出是什麽類型的對象,但是它一樣存在缺點,就是在創建對象時,每個對象都有一套自己的方法,每定義一個函數都實例化了一個對象,導致共用的方法和屬性,在內存中存在很多份,比較占用內存,性能也可能隨之降低。那麽為了解決這個缺點,在構造函數的基礎上,就誕生了(原型模式),我們將共用的方法和屬性,在prototype原型上添加,讓公用的屬性或方法在內存中只存在一份(提高性能)。註意(如果在實例中添加了一個屬性,而該屬性與實例原型中的一個屬性同名,該屬性將會屏蔽原型中的那個屬性)。
總之,按現在的理解,工廠模式、構造函數模式、原型鏈模式都是為了創建同類型對象而生。後面關於對象的繼承以及多肽後面再介紹。
js面向對象、創建對象的工廠模式、構造函數模式、原型鏈模式