用對象的思想去寫代碼,就是面向對象編程css
過程式寫法html
面向對象寫法node
咱們一直都在使用對象jquery
數組Array 時間Date面試
抽象:抓住核心問題編程
封裝:只能經過對象來訪問方法json
繼承:從已有對象上繼承出新的對象數組
多態:多對象的不一樣形態app
方法(行爲、操做) —— 對象下面的函數:過程、動態的ide
屬性 —— 對象下面的變量:狀態、靜態的
//var obj = {}; var obj = new Object(); //建立了一個空的對象 obj.name = '小明'; //屬性 obj.showName = function(){ //方法 alert(this.name); } obj.showName();
爲對象添加屬性和方法
Object對象
this指向
建立兩個對象:重複代碼過多
//var obj = {}; var obj = new Object(); //建立了一個空的對象 obj.name = '小明'; //屬性 obj.showName = function(){ //方法 alert(this.name); } obj.showName(); var obj2 = new Object(); obj2.name = '小強'; obj.showName = function(){ alert(this.name); } obj2.showName();
面向對象中的封裝函數
//工廠方式:封裝函數 function createPerson(name){ //1. 原料 var obj = new Object(); //2. 加工 obj.name = name; obj.showName = function(){ alert(this.name); }; //3. 出廠 return obj; } var p1 = createPerson('小明'); p1.showName(); var p2 = createPerson('小強'); p2.showName();
改爲與系統對象相似的寫法
一、首字母大寫
二、New關鍵字提取
三、this指向爲新建立的對象
/* 當new去調用一個函數:這個時候函數中的this就是建立出來的對象,並且函數的返回值直接就是this啦。(這叫作隱式返回) */ // new後面調用的函數:構造函數 function CreatePerson(name){ this.name = name; this.showName = function(){ alert(this.name); }; // return obj; 隱式返回,因此這一行不用寫了 } var p1 = new CreatePerson('小明'); p1.showName(); var p2 = new CreatePerson('小強'); p2.showName();
構造函數
用來建立對象的函數,叫作構造函數
存在的問題
一、對象的引用
2.浪費內存
/* 當new去調用一個函數:這個時候函數中的this就是建立出來的對象,並且函數的返回值直接就是this啦。(這叫作隱式返回) */ // new後面調用的函數:構造函數 function CreatePerson(name){ this.name = name; this.showName = function(){ alert(this.name); }; // return obj; 隱式返回,因此這一行不用寫了 } var p1 = new CreatePerson('小明'); p1.showName(); var p2 = new CreatePerson('小強'); p2.showName(); //alert(p1.showName == p2.showName); //false
/* var a = [1, 2, 3]; var b = [1, 2, 3]; alert(a == b); //false; */ /* var a = 5; var b = a; b += a; 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] 只要賦值就會在內存中從新生成,因此a,b互補影響 */ /* var a = 5; var b = 5; alert(a == b); //true 基本類型的比較:只要值相同就能夠 */ /* var a = [1, 2, 3]; var b = [1, 2, 3]; alert(a == b); //false 對象類型:值和引用都相同才行 */ /* var a = [1, 2, 3]; var b = a; alert(a == b); //true */
概念
去改寫對象下面公用的方法或者屬性,讓公用的方法或者屬性在內存中僅存在一份(好處:提升性能)
學習原型
類比:原型就是CSS中的class(普通方法就是CSS中的style)
普通方法的優先級比原型要高
原型能夠複用,普通方法不能夠複用
var arr = []; arr.number = 10; Array.prototype.number = 20; alert(arr.number); //10 普通方法的優先級高於原型
//原型:prototype:要寫在構造函數下面 var arr = [1, 2, 3, 4, 5]; var arr2 = [2, 2, 2, 2, 2]; Array.prototype.sum = function(){ var result = 0; for(var i=0; i<this.length; i++){ result += this[i]; } return result; } /* arr.sum = function(){ var result = 0; for(var i=0; i<this.length; i++){ result += this[i]; } return result; } alert(arr.sum()); //15 */ alert(arr.sum()); alert(arr2.sum());
經過原型改寫工廠方式原則:
相同的屬性和方法能夠加在原型上
混合的編程模式
function CreatePerson(name){ this.name = name; //變化的,不能公用的屬性不能寫在原型上 } CreatePerson.prototype.showName = function(){ alert(this.name); } var p1 = new CreatePerson('小明'); var p2 = new CreatePerson('小強'); alert(p1.showName == p2.showName); //true
混合的編程模式
//面向對象的寫法 function 構造函數(){ this.屬性 } 構造函數.原型.方法 = function(){}; //面向對象的使用 var 對象1 = new 構造函數(); 對象1.方法();
總結面向對象寫法:構造函數加屬性,原型加方法
原則
先寫出普通的寫法,而後改爲面向對象寫法
普通方法變型
儘可能不要出現函數嵌套函數
能夠有全局變量
把onload中不是賦值的語句放到單獨的函數中
改爲面向對象
全局變量就是屬性
函數就是方法
onload中建立對象
改this指向問題,要儘可能讓面向對象中的this指向對象
一、先寫出普通方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象的選項卡</title> <style> #div1 div { width: 200px; height: 200px; border: 1px solid #000; display: none; } .active { background: red; } </style> <script> window.onload = function(){ var oParent = document.getElementById('div1'); var aInput = oParent.getElementsByTagName('input'); var aDiv = oParent.getElementsByTagName('div'); for(var i=0; i<aInput.length; i++){ aInput[i].index = i; aInput[i].onclick = function(){ for(var i=0; i<aInput.length; i++){ aInput[i].className = ''; aDiv[i].style.display = 'none'; } this.className = 'active'; aDiv[this.index].style.display = 'block'; } } } </script> </head> <body> <div id="div1"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">11111</div> <div>22222</div> <div>33333</div> </div> </body> </html>
二、普通方法變型
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象的選項卡</title> <style> #div1 div { width: 200px; height: 200px; border: 1px solid #000; display: none; } .active { background: red; } </style> <script> var oParent = null; var aInput = null; var aDiv = null; window.onload = function(){ oParent = document.getElementById('div1'); aInput = oParent.getElementsByTagName('input'); aDiv = oParent.getElementsByTagName('div'); init(); }; function init(){ //初始化的函數方法 for(var i=0; i<aInput.length; i++){ aInput[i].index = i; aInput[i].onclick = change; } } function change(){ for(var i=0; i<aInput.length; i++){ aInput[i].className = ''; aDiv[i].style.display = 'none'; } this.className = 'active'; aDiv[this.index].style.display = 'block'; } /* - 普通方法變型 - 儘可能不要出現函數嵌套函數 - 能夠有全局變量 - 把onload中不是賦值的語句放到單獨的函數中 */ </script> </head> <body> <div id="div1"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">11111</div> <div>22222</div> <div>33333</div> </div> </body> </html>
關於this的指向
oDiv.onclick = function(){ this: oDiv }; --- oDiv.onclick = show; function show(){ this: oDiv } --- oDiv.onclick = function(){ show(); }; function show(){ this: window }
改寫成面向對象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象bianxie</title> <style> #div1 div { width: 200px; height: 200px; border: 1px solid #000; display: none; } .active { background: red; } </style> <script> var oParent = null; var aInput = null; var aDiv = null; window.onload = function(){ var t1 = new Tab(); t1.init(); }; function Tab(){ this.oParent = document.getElementById('div1'); this.aInput = this.oParent.getElementsByTagName('input'); this.aDiv = this.oParent.getElementsByTagName('div'); } Tab.prototype.init = function(){ var This = this; for(var i=0; i<this.aInput.length; i++){ this.aInput[i].index = i; this.aInput[i].onclick = function(){ This.change(this); }; } } Tab.prototype.change = function(obj){ for(var i=0; i<this.aInput.length; i++){ this.aInput[i].className = ''; this.aDiv[i].style.display = 'none'; } obj.className = 'active'; this.aDiv[obj.index].style.display = 'block'; } /* - 改爲面向對象 - 全局變量就是屬性 - 函數就是方法 - onload中建立對象 - 改this指向問題:注意事件或者是定時器裏面的this。要儘可能保持面向對象中的this指向對象 */ </script> </head> <body> <div id="div1"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">11111</div> <div>22222</div> <div>33333</div> </div> </body> </html>
面向對象的複用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象bianxie</title> <style> #div1 div, #div2 div { width: 200px; height: 200px; border: 1px solid #000; display: none; } .active { background: red; } </style> <script> var oParent = null; var aInput = null; var aDiv = null; window.onload = function(){ var t1 = new Tab('div1'); t1.init(); var t2 = new Tab('div2'); t2.init(); t2.autoplay(); }; function Tab(id){ this.oParent = document.getElementById(id); this.aInput = this.oParent.getElementsByTagName('input'); this.aDiv = this.oParent.getElementsByTagName('div'); this.iNow = 0; } Tab.prototype.init = function(){ var This = this; for(var i=0; i<this.aInput.length; i++){ this.aInput[i].index = i; this.aInput[i].onclick = function(){ This.change(this); }; } } Tab.prototype.change = function(obj){ for(var i=0; i<this.aInput.length; i++){ this.aInput[i].className = ''; this.aDiv[i].style.display = 'none'; } obj.className = 'active'; this.aDiv[obj.index].style.display = 'block'; } Tab.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'; }, 2000) } </script> </head> <body> <div id="div1"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">11111</div> <div>22222</div> <div>33333</div> </div> <div id="div2"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">11111</div> <div>22222</div> <div>33333</div> </div> </body> </html>
注意
Event對象(event對象必定要寫到事件函數裏面)
事件函數中用來阻止默認行爲的return false也要寫到事件函數裏面
!DOCTYPE html> <html lang="en"> <head> <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'); var disX = 0; var disY = 0; oDiv.onmousedown = function(ev){ var ev = ev || window.event; disX = ev.clientX - oDiv.offsetLeft; disY = ev.clientY - oDiv.offsetTop; document.onmousemove = function(ev){ var ev = ev || window.event; oDiv.style.left = ev.clientX - disX + 'px'; oDiv.style.top = ev.clientY - disY + 'px'; } document.onmouseup = function(){ document.onmousemove = null; document.onmouseup = null; } return false; } } */ /* 第一步:普通方法變型 //先變型 var oDiv = null; var disX = 0; var disY = 0; window.onload = function(){ oDiv = document.getElementById('div1'); init(); } function init(){ oDiv.onmousedown = fnDown; } function fnDown(ev){ var ev = ev || window.event; disX = ev.clientX - oDiv.offsetLeft; disY = ev.clientY - oDiv.offsetTop; document.onmousemove = fnMove; document.onmouseup = fnUp; return false; } function fnMove(ev){ var ev = ev || window.event; oDiv.style.left = ev.clientX - disX + 'px'; oDiv.style.top = ev.clientY - disY + 'px'; } function fnUp(){ document.onmousemove = null; document.onmouseup = null; } */ //改爲面向對象 window.onload = function(){ var d1 = new Drag('div1'); d1.init(); } function Drag(id){ this.oDiv = document.getElementById(id); this.disX = 0; this.dixY = 0; } Drag.prototype.init = function(){ var This = this; this.oDiv.onmousedown = function(ev){ var ev = ev || window.event; This.fnDown(ev); return false; }; } Drag.prototype.fnDown = function(ev) { var ev = ev || window.event; var This = this; this.disX = ev.clientX - this.oDiv.offsetLeft; this.disY = ev.clientY - this.oDiv.offsetTop; document.onmousemove = function(ev){ var ev = ev || window.event; This.fnMove(ev); }; document.onmouseup = this.fnUp; } Drag.prototype.fnMove = function(ev){ this.oDiv.style.left = ev.clientX - this.disX + 'px'; this.oDiv.style.top = ev.clientY - this.disY + 'px'; } Drag.prototype.fnUp = function(){ document.onmousemove = null; document.onmouseup = null; } </script> </head> <body> <div id="div1"></div> </body> </html>
爲數組對象添加求和,最大值
爲字符串對象添加判斷是否是最後一個字母
面向對象的選項卡
給選項卡添加一個自動播放的方法
任意學過的效果改寫成面向對象
面向對象的面試題
JS基於原型的程序
String Number Boolean
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> </style> <script> /* function Aaa(){ this.name='小明'; } Aaa.prototype.showName=function(){ alert(this.name); } var a1 = new Aaa(); a1.showName(); var arr = new Array(); arr.push(); arr.sort(); // 在js源碼中:系統對象也是基於原型的程序 function Array(){ this.length = 0; } Array.prototype.push = function(){}; Array.prototype.sort = function(){}; */ //儘可能不要去修改或添加系統對象下面的方法和屬性 var arr = [1, 2, 3]; //Arr.prototype.push = function(){}; //加上這句話,就修改了源碼中的push方法,那麼後面那一句中的四、五、6就添加不進去了 Array.prototype.push = function(){ //本身寫一個數組的push方法 //this : 1, 2, 3 //arguments: 4, 5, 6 for(var i=0; i<arguments.length; i++){ this[this.length] = arguments[i]; } return this.length; } arr.push(4, 5, 6); alert(arr); </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> </style> <script> /* var str = 'hello'; alert( typeof str); //string str.charAt(0); //字符串爲何下面會有方法呢? str.indexOf('e'); */ //包裝對象:基本類型都有本身對應的包裝對象(String, Number, Boolean)(null和undefined沒有包裝對象) /* var str = new String('hello'); alert(typeof str); //object alert(str.charAt(1)); //彈出e String.prototype.charAt = function(){}; //基本類型的方法都是放在它們的包裝對象上 */ /* var str = 'hello'; //str是字符串 str.charAt(0); //基本類型找到對應的包裝對象類型,而後包裝對象把全部的屬性和方法給了基本類型,而後包裝對象消失 */ //給基本類型添加對象的時候,就是把方法添加到基本類型對應的包裝對象下面 var str = 'hello'; String.prototype.lastValue = function(){ return this.charAt(this.length-1); }; alert(str.lastValue()); //o var str1 = 'hello'; str1.number = 10; //在str1的包裝對象上建立了一個number,而後包裝對象就消失了 alert(str1.number); //undefined 再去調用這句話的時候,此時又從新建立了一個對象,這個對象與剛纔那個對象不是同一個 </script> </head> <body> </body> </html>
實例對象與原型之間的鏈接,叫作原型鏈
proto (隱式鏈接)
Object對象類型是原型鏈的最外層
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象中的原型鏈</title> <style></style> <script> //原型鏈:實例對象與原型之間的鏈接叫做原型鏈 //_proto_ (隱式鏈接) //原型鏈的最外層是Object.prototype function Aaa(){ } Aaa.prototype.num = 10; var a1 = new Aaa(); alert(a1.num); //10 a1如今它本身下面找,找不到這個num,而後又經過原型鏈到了Aaa.prototype,到它下面去找,最終找到了num function Bbb(){ this.num = 20; } Bbb.prototype.num = 10; var b1 = new Bbb(); alert(b1.num); //20 簡單來說,實例上的方法的優先級高於原型上的方法;本質上來說,是從b1下面找num直接就找到了,因而就不須要再進一步經過原型鏈去找到Bbb.prototype上的num了。 function Ccc(){}; var c1 = new Ccc(); Object.prototype.num = 30; //彈出30 c1下找不到num;順着原型鏈找到Ccc也找不到num,繼續順着原型鏈往上找,找到Object.prototype,找到了num alert(c1.num); </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象中的原型鏈</title> <style></style> <script> //hasOwnProperty: 看是否是對象自身下面的屬性 var arr = []; arr.num = 10; Array.prototype.num2 = 20; alert(arr.hasOwnProperty('num')); //true alert(arr.hasOwnProperty('num2')); //false alert(arr.hasOwnProperty == Object.prototype.hasOwnProperty); //true hasOwnProperty實際上是Object.prototype下面的方法 </script> </head> <body> </body> </html>
每一個原型都會自動添加constructor屬性
for in 的時候,有些屬性是找不到的
避免修改constructor屬性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象中的原型鏈</title> <style></style> <script> //constructor: 查看對象的構造函數 function Aaa(){ } var a1 = new Aaa(); alert(a1.constructor); //function Aaa var arr = []; alert(arr.constructor == Array); //true /* 在寫完一個構造函數以後 function Aaa(){} 程序會自動添加一句話: Aaa.prototype.constructor = Aaa; //每個函數都會有的,都是自動生成的 這時候new一個Aaa var a1 = new Aaa(); alert(a1.constructor); //彈出Aaa 若是手動寫一句: Aaa.prototype.constructor = Array; 那麼再去彈a1.constructor的話,彈出的就是Array了。 而hasOwnProperty這個屬性是添加在Object.prototype下面的,因此每一個對象下面去查這個hasOwnProperty也都會有。可是hasOwnProperty這個方法不是在具體對象下面的,而都是沿着原型鏈找到Object.prototype身上找到的。跟constructor是不同的。 */ function Bbb(){ } Bbb.prototype.name = '小明'; Bbb.prototype.age = 20; var b1 = new Bbb(); alert(b1.constructor); //function Bbb() //下面這種寫法就無心識地改變了c1的constructor,由於json直接賦值給了Ccc.prototype,而不是向上面那段代碼是添加操做。那麼本來系統自動生成的那句話Ccc.prototype.constructor = Ccc這句話就被覆蓋掉了。而後經過c1.constructor去找的話,其實找的就是這個json所對應的constructor了。 function Ccc(){ } Ccc.prototype = { name: '小明', age: 20 } var c1 = new Ccc(); alert(c1.constructor); //function Object //爲了不上述問題,應該注意,用json簡寫的時候,要把constructor修正過來,以下: function Ddd(){ } Ddd.prototype = { constructor: Ddd, //經過這一句來修正一下 name: '小明', age: 20 } var d1 = new Ddd(); alert(d1.constructor); //Ddd </script> </head> <body> </body> </html>
for in循環,有些屬性找不到
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>面向對象中的原型鏈</title> <style></style> <script> function Aaa(){ } Aaa.prototype.name = 10; Aaa.prototype.constructor = Aaa; for (var attr in Aaa.prototype){ alert(attr); //只能找到本身添加的,系統自動生成的好比constructor,for in循環是找不到的 } </script> </head> <body> </body> </html>
對象與構造函數在原型鏈上是否有關係
能夠用來做類型判斷
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>instanceof()</title> <style></style> <script> // instanceof: 對象與構造函數在原型鏈上是否有關係 function Aaa(){ } var a1 = new Aaa(); alert(a1 instanceof Aaa); //true 看a1是否與Aaa在同一個原型鏈上 alert(a1 instanceof Array); //false alert(a1 instanceof Object); //true </script> </head> <body> </body> </html>
object上的方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>toString()</title> <style></style> <script> // toString(): // 位置:系統對象下面都是自帶的;本身寫的對象都是經過原型鏈找Object下面的 // 做用:把對象轉成字符串 var arr = []; alert(arr.toString); //找獲得 alert(arr.toString == Object.prototype.toString); //false 系統對象下面的toString不在Object原型上 function Aaa(){ } var a1 = new Aaa(); alert(a1.toString); //找獲得 alert(a1.toString == Object.prototype.toString); //true 構造函數生成的對象的toString方法在Object原型上 var arr = [1, 2, 3]; alert(typeof arr.toString()); //string alert(arr.toString()); //"1, 2, 3" //由於知道toString在哪兒了,因此能夠手動修改toString方法 Array.prototype.toString = function(){ return this.join('+'); } alert(arr.toString()); //"1+2+3" var num = 255; alert(num.toString(16)); //"ff" 將255轉成16進制 //利用toString進行類型判斷(用constructor和instanceof也均可以進行類型判斷)。推薦toString來判斷例如數組的類型 var arr = [] var date = new Date; var json = {} var reg = new RegExp; var n = null; alert(Object.prototype.toString.call(arr)); //[object Array] alert(Object.prototype.toString.call(date)); //[object Date] alert(Object.prototype.toString.call(json)); //[object Object] alert(Object.prototype.toString.call(reg)); //[object RegExp] alert(Object.prototype.toString.call(n)); //[object Null] //判斷類型時直接進行比較就能夠了,例如判斷arr是不是數組: alert(Object.prototype.toString.call(arr) == '[object Array]'); //true //舉例說明用instanceof和constructor來判斷數組失效,可是toString依然有效的例子 window.onload = function(){ var oF = document.createElement('iframe'); document.body.appendChild(oF); var ifArray = window.frames[0].Array; //ifArray就是iframe裏面的數組 var arr = new ifArray(); //ifArray就是iframe裏面的數組 這時候跨頁面了 alert(arr.constructor == Array); //false constructor判斷iframe下面的數組失效 alert(arr instanceof Array); //false 判斷失效 alert(Object.prototype.toString.call(arr) == '[object Array]'); //true 判斷依然有效 } </script> </head> <body> </body> </html>
在原有對象的基礎上,略做修改,獲得一個新的對象
不影響原有對象的功能
屬性:call
方法:for in
例子:繼承的拖拽
!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>繼承</title> <style></style> <script> // 繼承:子類不影響父類;子類能夠繼承父類的一些功能(實現代碼複用) function CreatePerson(name, sex){ //父類 this.name = name; this.sex = sex; } CreatePerson.prototype.showName = function(){ alert(this.name); } var p1 = new CreatePerson('小明', '男'); p1.showName(); //屬性的繼承:調用父類的構造函數 用call改this指向 //方法的繼承:用for in循環 function CreateStar(name, sex, job){ //子類 // this.name = name; // this.sex = sex; CreatePerson.call(this, name, sex); //調用父類構造函數 須要修改指向 this.job = job; } /* CreateStar.prototype = CreatePerson.prototype; //將父類的原型賦給子類,那麼子類就得到了父類下全部的屬性值,實現了方法的繼承 可是這裏有對象的引用問題,形成互相干涉 例如: CreateStar.prototype.showJob = function(){} 上面子類的原型添加的方法,那麼父類CreatePerson.prototype下面也有了showJob的方法 */ //方法的繼承應該用for in循環,將父類的全部屬性拷貝給子類,這叫做「拷貝繼承」。jQuery也是採用拷貝繼承extend function extend(obj1, obj2){ for(var attr in obj2){ obj1[attr] = obj2[attr]; } } extend(CreateStar.prototype, CreatePerson.prototype ) var p2 = new CreateStar('黃曉明', '男', '演員') p2.showName(); </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>繼承</title> <style></style> <script> var a = { name: '小明' }; //var b = a; // var b = {}; // for(var attr in a){ // b[attr] = a[attr]; // } // b.name = "小強"; // alert(a.name); //將對象屬性的拷貝封裝成一個函數 function extend(obj1, obj2){ for(var attr in obj2){ obj1[attr] = obj2[attr]; } } var b = {}; extend(b, a); b.name = '小強'; alert(b.name); alert(a.name); </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>繼承</title> <style></style> <script> var a = [1, 2, 3]; var b = a; // b.push(4); //修改 a、b之間引用鏈條存在 // alert(a); //1, 2, 3, 4 a被b影響了 b = [1, 2, 3, 4]; //從新賦值,內存中這個b又從新生成了。a和b之間這個引用的鏈條已經斷開,a、b沒有關係 alert(a); //1, 2, 3 a未被影響 //在for in循環中,子類原型的方法也直接等於了父類原型的方法,由於方法是函數,也是個對象,爲何這種「對象 = 對象」沒有互相干涉呢?這是由於函數有些特殊,不能被修改,只能被從新賦值 </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> #div1 {width: 100px; height: 100px; background: red; position: absolute;} #div2 {width: 100px; height: 100px; background: yellow; position: absolute; left: 100px;} </style> <script src="jquery-1.11.1.js"></script> <script> window.onload = function(){ var d1 = new Drag('div1'); d1.init(); var d2 = new ChildDrag('div2'); d2.init(); } //父類的面向對象的拖拽開始 function Drag(id){ //父類 this.obj = document.getElementById(id); this.disX = 0; this.disY = 0; } Drag.prototype.init = function(){ var This = this; this.obj.onmousedown = function(ev){ var ev = ev || window.event; This.fnDown(ev); document.onmousemove = function(ev){ var ev = ev || window.event; This.fnMove(ev); } document.onmouseup = function(){ This.fnUp(); } return false; } } Drag.prototype.fnDown = function(ev){ this.disX = ev.clientX - this.obj.offsetLeft; this.disY = ev.clientY - this.obj.offsetTop; } Drag.prototype.fnMove = function(ev){ this.obj.style.left = ev.clientX - this.disX + 'px'; this.obj.style.top = ev.clientY - this.disY + 'px'; } Drag.prototype.fnUp = function(){ document.onmousemove = null; document.onmouseup = null; } //父類的面向對象的拖拽結束 //子類繼承父類開始 function ChildDrag(id){ //子類 Drag.call(this, id); } extend(ChildDrag.prototype, Drag.prototype); //經過改變ChildDrap原型下的fnMove,給子類添加了限制拖拽時,元素超出可視區左右邊界的功能 ChildDrag.prototype.fnMove = function(ev){ var L = ev.clientX - this.disX; var T = ev.clientY - this.disY; if(L < 0){ L = 0; } else if (L > document.documentElement.clientWidth - this.obj.offsetWidth){ L = document.documentElement.clientWidth - this.obj.offsetWidth; } this.obj.style.left = L + 'px'; this.obj.style.top = T + 'px'; } function extend(obj1, obj2){ for(var attr in obj2){ obj1[attr] = obj2[attr]; } } //子類繼承父類結束 </script> </head> <body> <div id="div1"></div> <div id="div2"></div> </body> </html>
一、類式繼承
利用構造函數(類)繼承的方式
二、原型繼承
藉助原型來實現對象繼承對象
類式繼承
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> </style> <script> //類:JS是沒有類的概念的,能夠把JS中的構造函數看作是類 //要作屬性和方法繼承的時候,要分開繼承 function Aaa(){ //父類 this.name = '小明'; this.arr = [1, 2, 3]; } Aaa.prototype.showName = function(){ alert(this.name); } function Bbb(){ //子類 } Bbb.prototype = new Aaa(); //這句話就是類式繼承 //只寫上面一句話是有問題的,修改了constructor指向。 //Bbb.prototype.constructor = Bbb; //修正指向問題 var b1 = new Bbb(); b1.showName(); alert(b1.name); alert(b1.constructor); //彈出的並非Bbb,而是Aaa。只有寫了上面修正指向的那句話,這裏纔會變成Bbb //真正規範的類式繼承要用下面幾句話: // function Bbb(){ //這裏只繼承了屬性 // Aaa.call(this) // } //var F = function(){}; //建立一個空的構造函數 //F.prototype = Aaa.prototype; //Bbb.prototype = new F(); //這裏只繼承了方法 //Bbb.prototype.constructor = Bbb; b1.arr.push(4); var b2 = new Bbb(); alert(b2.arr); //[1, 2, 3, 4] 這裏看到上面的b1.arr.push(4)影響到了這裏 要避免這種問題,應該用上面的類式繼承的規範寫法才行 </script> </head> <body> </body> </html>
原型繼承
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>原型繼承</title> <style> </style> <script> var a = { name: '小明' } var b = cloneObj(a); //讓b繼承a b.name = '小強'; //這一句不會更改a的name,由於這一句在b的下面又新建了一個屬性name,值爲「小強」 alert(b.name); //小強 alert(a.name); //小明 function cloneObj(obj){ var F = function(){}; F.prototype = obj; return new F(); } </script> </head> <body> </body> </html>
拷貝繼承:通用型的 有new或無new的時候均可以
類式繼承:適合經過new構造函數形式
原型繼承:比較適合無new的對象
一、對象的多種表現形式
提升對象的複用性
如何配置參數和默認參數
例子:拖拽
例子:彈窗
二、什麼是組件?
對面向對象的深刻應用(UI組件、功能組件)
將配置參數、方法、事件、三者進行分離
三、建立自定義事件
有利於多人協做開發代碼
如何去掛載自定義事件與事件函數
四、例子:基於JQ的選項卡的組件開發模式
trigger() extend()等方法的使用
五、本課練習
組件開發的練習
http://tangram.baidu.com/magic/
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>組件開發中的參數問題</title> <script> //用json來解決參數數量問題 function show(opt){ } show({ id: 'div1', toDown: function(){}, toUp: function(){} }); //用下面的方法解決參數順序問題 var a = { //配置參數 //name: '小明' name: '小明' } var b = { //默認參數 name: '小強' } extend(b, a); //當有配置的時候,走配置,沒有配置的時候,走默認 alert(b.name); function extend(obj1, obj2){ for(var attr in obj2){ obj1[attr] = obj2[attr]; } } </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> #div1 {width: 100px; height: 100px; background: red; position: absolute;} #div2 {width: 100px; height: 100px; background: yellow; position: absolute; left: 100px;} #div3 {width: 100px; height: 100px; background: blue; position: absolute; left: 200px;} #div4 {width: 100px; height: 100px; background: green; position: absolute; left: 300px;} </style> <script src="jquery-1.11.1.js"></script> <script> /* 組件開發:像兄弟之間的關係(代碼複用的一種形式) */ window.onload = function(){ var d1 = new Drag(); d1.init('div1'); var d2 = new Drag(); d2.init('div2', { //配置參數 toDown: function(){ document.title = 'hello'; } }); var d3 = new Drag(); d3.init('div3', { //配置參數 toDown: function(){ document.title = '妙味'; }, toUp: function(){ document.title = '課堂'; } }); var d4 = new Drag(); d4.init('div4', { //配置參數 toUp: function(){ document.title = 'byebye'; } }); } function Drag(){ this.obj = null; this.disX = 0; this.disY = 0; this.settings = { //默認參數 //id不該該屬於配置參數當中,它屬於必填項 toDown: function(){}, toUp: function(){} } } Drag.prototype.init = function(id, opt){ var This = this; this.obj = document.getElementById(id); extend(this.settings, opt); //用配置覆蓋默認 this.obj.onmousedown = function(ev){ var ev = ev || window.event; This.fnDown(ev); This.settings.toDown(); document.onmousemove = function(ev){ var ev = ev || window.event; This.fnMove(ev); } document.onmouseup = function(){ This.fnUp(); This.settings.toUp(); } return false; } } Drag.prototype.fnDown = function(ev){ this.disX = ev.clientX - this.obj.offsetLeft; this.disY = ev.clientY - this.obj.offsetTop; } Drag.prototype.fnMove = function(ev){ this.obj.style.left = ev.clientX - this.disX + 'px'; this.obj.style.top = ev.clientY - this.disY + 'px'; } Drag.prototype.fnUp = function(){ document.onmousemove = null; document.onmouseup = null; } function extend(obj1, obj2){ for(var attr in obj2){ obj1[attr] = obj2[attr]; } } </script> </head> <body> <div id="div1"></div> <!-- 紅色方塊是老大 --> <div id="div2"></div> <!-- 黃色方塊是老二 黃色的按下以後title有一個變化 --> <div id="div3"></div> <!-- 老三 按下title變化,擡起title變化 --> <div id="div4"></div> <!-- 老四 鼠標擡起時title有變化 --> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>彈窗的組件開發</title> <style> * {margin : 0; padding; 0} .login {background: white; border: 1px #000 solid; position: absolute; left: 0; top: 0; z-index: 2;} .title {height: 30px; background: gray; color: white;} .title .close {float: right;} #mask {width: 500px; height: 500px; background: black; filter: alpha(oapcity=50); opacity: 0.5; position: absolute; left: 0; top: 0; z-index: 1;} </style> <script> window.onload = function(){ var aInput = document.getElementsByTagName('input'); aInput[0].onclick = function(){ var d1 = new Dialog(); d1.init({//配置參數 iNow: 0, title: '登錄' }); } aInput[1].onclick = function(){ var d2 = new Dialog(); d2.init({//配置參數 iNow: 1, w : 100, h : 400, dir: 'right', title: '公告' }); } aInput[2].onclick = function(){ var d3 = new Dialog(); d3.init({//配置參數 iNow: 2, mask: true }); } } function Dialog(){ this.oLogin = null; this.settings = { //默認參數 w: 300, h: 300, dir: 'center', title: '', mask: false } } Dialog.prototype.json = {}; //防止添加多個彈窗 Dialog.prototype.init = function(opt){ extend(this.settings, opt); if(this.json[opt.iNow] == undefined){ this.json[opt.iNow] = true; } if(this.json[opt.iNow]){ //防止添加多個彈窗 this.create(); this.fnClose(); if(this.settings.mask){ this.createMask(); } this.json[opt.iNow] = false; } } Dialog.prototype.create = function(){ this.oLogin = document.createElement('div'); this.oLogin.className = 'login'; this.oLogin.innerHTML = '<div class="title"><span>' + this.settings.title + '</span><span class="close">X</span></div><div class="content"></div>'; document.body.appendChild(this.oLogin); this.setData(); } Dialog.prototype.setData = function(){ this.oLogin.style.width = this.settings.w + 'px'; this.oLogin.style.height = this.settings.h + 'px'; if(this.settings.dir == 'center'){ this.oLogin.style.left = (viewWidth() - this.oLogin.offsetWidth)/2 + 'px'; this.oLogin.style.top = (viewHeight() - this.oLogin.offsetHeight)/2 + 'px'; } else if (this.settings.dir == 'right'){ this.oLogin.style.left = (viewWidth() - this.oLogin.offsetWidth) + 'px'; this.oLogin.style.top = (viewHeight() - this.oLogin.offsetHeight) + 'px'; } } Dialog.prototype.fnClose = function(){ var oClose = this.oLogin.getElementsByTagName('span')[1]; var This = this; oClose.onclick = function(){ document.body.removeChild(This.oLogin); if(This.settings.mask){ document.body.removeChild(This.oMask); } This.json[This.settings.iNow] = true; } } Dialog.prototype.createMask = function(){ var oMask = document.createElement('div'); oMask.id = 'mask'; document.body.appendChild(oMask); this.oMask = oMask; oMask.style.width = viewWidth() + 'px'; oMask.style.height = viewHeight() + 'px'; } function extend(obj1, obj2){ for(var attr in obj2){ obj1[attr] = obj2[attr]; } } function viewWidth(){ return document.documentElement.clientWidth; } function viewHeight(){ return document.documentElement.clientHeight; } </script> </head> <body> <input type="button" value="1"> <input type="button" value="2"> <input type="button" value="3"> <!-- <div class="login"> <div class="title"> <span>標題</span><span class="close">X</span> </div> <div class="content"></div> </div> --> <!-- <div id="mask"></div> --> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>自定義事件</title> <script> // 自定義事件:主要是跟函數有關係,讓函數可以具有事件的某些特性 // 三我的都針對show來添加功能。都添加到一個地方會亂,因而三我的分開來寫 /* function show(){ alert(1); //第一我的寫 } function show(){ alert(2); //第二我的寫 } function show(){ alert(3); //第三我的寫 } show(); //只能執行第三我的寫的,由於函數不能修改只能覆蓋 */ //看看是否能讓函數具有事件的特性,多人寫的能夠綁定上去,就解決了多人協做的問題。原理就如如下情形。固然如下代碼不是添加自定義事件的代碼,只是爲了方便理解: /* window.addEventListener('show', function(){ alert(1); //第一我的寫的 }, false); window.addEventListener('show', function(){ alert(2); //第二我的寫的 }, false); window.addEventListener('show', function(){ alert(3); //第三我的寫的 }, false); show(); //主動觸發自定義事件 */ </script> </head> <body> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>自定義事件</title> <script> //自定義事件重點在於將函數和元素以及事件名稱創建關係,在執行某個函數的時候,主動觸發某個事件名稱下面相關的全部函數 window.onload = function(){ var oDiv = document.getElementById('div1'); var oSpan = document.getElementById('span1'); bindEvent(oDiv, 'click', function(){ alert(1); }) bindEvent(oDiv, 'click', function(){ alert(2); }) bindEvent(oSpan, 'show', function(){ //這裏的show就是個自定義事件 alert(3); }) bindEvent(oSpan, 'show', function(){ //這裏的show就是個自定義事件 alert(4); }) bindEvent(oSpan, 'hide', function(){ //這裏的show就是個自定義事件 alert(5); }) fireEvent(oSpan, 'show'); //主動觸發,彈出3, 4 fireEvent(oDiv, 'click'); //主動觸發,彈出1, 2 } function bindEvent(obj, events, fn){ //fn: 看做一本書 《西遊記》 //obj: 至關於圖書館的樓層 文學樓 //events: 至關於書架 古典文學書架 obj.listeners = obj.listeners || {}; //先找到樓層,沒有樓層就建立樓層 obj.listeners[events] = obj.listeners[events] || []; //再找到書架,沒有書架就建立書架 obj.listeners[events].push(fn); //把fn這本書放到書架上 /* 經過以上的方式,將obj,events和fn創建了關係*/ if(obj.addEventListener){ obj.addEventListener(events, fn, false); } else { obj.attachEvent('on' + events, fn); } } function fireEvent(obj, events){ //主動觸發自定義事件 for(var i=0; i<obj.listeners[events].length; i++){ obj.listeners[events][i](); } } </script> </head> <body> <div id="div1">div</div> <span id="span1">span</span> </body>
——正規組件的寫法——基於拖拽的組件進行的修改
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>自定義事件的拖拽組件</title> <style> #div1 {width: 100px; height: 100px; background: red; position: absolute;} #div2 {width: 100px; height: 100px; background: yellow; position: absolute; left: 100px;} #div3 {width: 100px; height: 100px; background: blue; position: absolute; left: 200px;} #div4 {width: 100px; height: 100px; background: green; position: absolute; left: 300px;} </style> <script src="jquery-1.11.1.js"></script> <script> window.onload = function(){ var d1 = new Drag(); d1.init('div1'); var d2 = new Drag(); d2.init('div2'); bindEvent(d2, 'toDown', function(){ document.title = 'hello'; }); bindEvent(d2, 'toDown', function(){ //新來的一個同事擴展d2的toDown事件的時候,更容易 document.body.style.background = 'black'; }) var d3 = new Drag(); d3.init('div3'); bindEvent(d3, 'toDown', function(){ document.title = '妙味'; }) bindEvent(d3, 'toUp', function(){ document.title = '課堂'; }) var d4 = new Drag(); d4.init('div4'); bindEvent(d4, 'toUp', function(){ document.title = 'byebye'; }) } function Drag(){ this.obj = null; this.disX = 0; this.disY = 0; this.settings = { } } Drag.prototype.init = function(id, opt){ var This = this; this.obj = document.getElementById(id); extend(this.settings, opt); this.obj.onmousedown = function(ev){ var ev = ev || window.event; This.fnDown(ev); fireEvent(This, 'toDown'); document.onmousemove = function(ev){ var ev = ev || window.event; This.fnMove(ev); } document.onmouseup = function(){ This.fnUp(); fireEvent(This, 'toUp'); } return false; } } Drag.prototype.fnDown = function(ev){ this.disX = ev.clientX - this.obj.offsetLeft; this.disY = ev.clientY - this.obj.offsetTop; } Drag.prototype.fnMove = function(ev){ this.obj.style.left = ev.clientX - this.disX + 'px'; this.obj.style.top = ev.clientY - this.disY + 'px'; } Drag.prototype.fnUp = function(){ document.onmousemove = null; document.onmouseup = null; } function extend(obj1, obj2){ for(var attr in obj2){ obj1[attr] = obj2[attr]; } } function bindEvent(obj, events, fn){ obj.listeners = obj.listeners || {}; obj.listeners[events] = obj.listeners[events] || []; obj.listeners[events].push(fn); if(obj.nodeType){ //若是傳進來的是DOM元素的話,走着下面;若是傳進來的不是DOM,是對象的話,就不走下面,只走上面了 if(obj.addEventListener){ obj.addEventListener(events, fn, false); } else { obj.attachEvent('on' + events, fn); } } } function fireEvent(obj, events){ if(obj.listeners && obj.listeners[events]){ for(var i=0; i<obj.listeners[events].length; i++){ obj.listeners[events][i](); } } } </script> </head> <body> <div id="div1"></div> <div id="div2"></div> <div id="div3"></div> <div id="div4"></div> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>基於JQ的選項卡組件開發</title> <style> html, body {margin: 0; padding: 0;} #div1 div, #div2 div, #div3 div, #div4 div {width: 200px; height: 200px; border: 1px solid #000; display: none;} #div1 .active, #div2 .active, #div3 .active, #div4 .active {background: red;} </style> <script src="jquery-1.11.1.js"></script> <script> /* title: 基於JQ的選項卡組件 Options: - event - delay Methods: - nowSel() - 設置開始的tab數 - getContent() - 獲取當前內容 Events: - beforeClick 點擊前觸發 - afterClick 點擊後觸發 */ // JQ中的主動觸發:trigger() $(function(){ var t1 = new Tab(); t1.init('div1', {}); var t2 = new Tab(); t2.init('div2', { event: 'mouseover' }); var t3 = new Tab(); t3.init('div3', { event: 'mouseover', delay: 200 }); var t4 = new Tab(); t4.init('div4', {}); t4.nowSel(2); $('#input1').click(function(){ alert(t4.getContent()); }) $(t4).on('beforeClick', function(){ alert(t4.getContent()); }) $(t4).on('afterClick', function(){ alert(t4.getContent()); }) }) function Tab(){ this.oParent = null; this.aInput = null; this.aDiv = null; this.iNow = 0; this.settings = { //默認參數 event: 'click', delay: 0 } } Tab.prototype.init = function(oParent, opt){ $.extend(this.settings, opt); this.oParent = $('#' + oParent); this.aInput = this.oParent.find('input'); this.aDiv = this.oParent.find('div'); this.change(); } Tab.prototype.change = function(){ var This = this; var timer = null; this.aInput.on(this.settings.event, function(){ if(This.settings.event == 'mouseover' && This.settings.delay){ var _this = this; timer = setTimeout(function(){ show(_this); }, This.settings.delay) } else { show(this); } }).mouseout(function(){ clearTimeout(timer); }); function show(obj){ $(This).trigger('beforeClick'); This.aInput.attr('class', ''); This.aDiv.css('display', 'none'); $(obj).attr('class', 'active'); This.aDiv.eq($(obj).index()).css('display', 'block'); This.iNow = $(obj).index(); $(This).trigger('afterClick'); } Tab.prototype.nowSel = function(index){ this.aInput.attr('class', ''); this.aDiv.css('display', 'none'); this.aInput.eq(index).attr('class', 'active'); this.aDiv.eq(index).css('display', 'block'); this.iNow = index; } Tab.prototype.getContent = function(){ return this.aDiv.eq(this.iNow).html(); } } </script> </head> <body> <div id="div1"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">111111</div> <div>222222</div> <div>333333</div> </div> <div id="div2"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">111111</div> <div>222222</div> <div>333333</div> </div> <div id="div3"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">111111</div> <div>222222</div> <div>333333</div> </div> <div id="div4"> <input type="button" value="1" class="active"> <input type="button" value="2"> <input type="button" value="3"> <div style="display: block">111111</div> <div>222222</div> <div>333333</div> </div> <input type="button" value="點擊" id="input1"> </body> </html>