這一節是繼上一節高質量的Javascript
7)編程實用技巧
1:彈性
從 一個標簽區和內容區的實例(就是點擊不同的標簽菜單顯示不同的內容塊)來說明不需要每個tabmenu都設置onclick事件,為了讓程序更有彈性,可 以將所有的點擊時間封裝成一個函數,變化的標簽作為參數傳入實現點擊不同的標簽顯示對應的內容塊,這樣標簽的數量可自適應,可增可減,而js代碼可以不用 變動,只需要修改html的標簽就可以。-------------這點我想做過項目的伙伴們都能深深的體會到。
瑣碎知識點:
js 一個非常經典的問題:在遍歷數組時對DOM監聽事件(如上例中的菜單單擊事件tabMenus[i].onclick),索引值始終等於遍歷結束后的值。 解決這一問題可以用閉包,或者給每個DOM節點添加Index屬性用來標示區別。---------我遍歷一般用jquery的each函數,挺好用的, 沒用過Dom,但是還是一個值得注意的地方。
2:可復用性
因為一個html頁面的id只能出現一次,所以,如果你的程序需要被多次復用,就一定不能用id來標識,不能用id作為js獲得DOM節點的掛鈎,更好的應該是使用class. 在做項目過程中,我們可以通過class取得一組Dom標簽,但由於id的唯一性,只適合取某個固定的標簽。
組件需要指定根節點,以保持每個組件之間的獨立性。--------具體的可能要看書上的代碼示例來體會。(就是盡管有相同的class來標識同類結構(一個組件),但是不同的組件與組件之間還需要指定一個父節點來區別對待)。
3:通過參數實現定制
如果每個tab標簽需要不同的樣式,這個時候就只能根據每個tabMenu的唯一標示作為參數傳到處理函數,在函數中根據不同的標識來選擇不同的樣式
如果一個函數內某個因素很不穩定,我們可以將它從函數內部分離出來,以參數的形式傳入,從而將不穩定的因素和函數解耦。---與第一點彈性有相似處
4:this關鍵字的指向
在js全局域中 this指向window.
A:JavaScript偽協議和內聯事件對於this的指向不同:
偽協議:a標簽的href="javascript:alert(this==window)" 結果:彈出true 偽協議中this指向window
內聯事件:onclick="alert(this.tagName)" 結果:彈出a 內聯事件的this指向當前的Dom元素
B:setTimeout和setInterval也會改變this的指向,在這兩個函數中 this指向window
C:DomNode.on***事件也會改變this的指向,this會指向當前調用時間的Dom結點。
使用匿名函數可將B,C中this的指向改變。
例:
1 <script type="text/javascript"> 2 var name = "global-name"; 3 var btn = document.getElementById("btn"); 4 var test = { 5 name: "test-name", 6 say: function () { 7 alert(this.name); 8 } 9 } 10 test.say(); //輸出test-name 11 setTimeout(function () { test.say() }, 1000); //輸出test-name 匿名函數會改變指向,誰調用就指向誰 12 /* 13 setTimeout和setInterval兩個函數的調用方式,第一個參數應該是函數指針,或字符串 14 */ 15 setTimeout('test.say()', 1000); //作為字符串,還是由test調用,輸出test-name 16 setTimeout(test.say, 1000); //作為函數指針,輸出global-name setTimeout的直接調用指向方式,this指向window 17 18 setInterval(function () { test.say() }, 1000); //輸出test-name 19 setInterval('test.say()', 1000); //輸出test-name ,由test主調 20 setInterval(test.say, 1000); //輸出global-name 21 setTimeout(function () { alert(this == window) }, 1000); //輸出true, this為全局window對象 22 23 $(function () { 24 var btn2 = document.getElementById("btn"); 25 btn2.onclick = function () { test.say() }; //輸出test-name 26 btn2.onclick = function () { alert(this == btn2) }; //輸出true, this為DOM元素對象 27 }); 28 </script> 29 <input type="button" name="btn-name" id="btn" value="test-btn" />
總結:1:如果setTimeout和setInterval調用的處理函數,該處理函數是“直接調用的函數”,那么this指向window
2:DomNode.on***關聯的處理函數,該處理函數是“直接調用的函數”,this指向Dom結點
3:如果將上面的處理函數用匿名函數封裝起來,那么,處理函數的調用方式:由直接調用變為間接調用;這樣就不會受到外面調用函數的影響,this該指向誰就指向誰
自己的話:一般來說,this----該動作是誰調用的,那么this就指向調用者,只有碰到setTimeout、setInterval、btn.on***等會改變指向的函數,才不會遵守基本規則,但是也可以通過匿名函數的間接調用來解決,這樣又變回了一般狀態。
另外還可以通過call和apply函數來改變處理函數的this指向,test.say.call(btn); test.say.apply(btn); this 直接指定的。
5:預留回調接口
我所理解的就是,在函數中預留出一個參數handler,該參數的實參是一個函數。 判斷回調函數是否有用,if(handler){ handler(參數);//調用回調函數;}
添加回調的接口可以參加代碼的可擴展性。
6:編程中的DRY規則
DRY----don't repeat yourself,強調在程序中不要將相同的代碼重復編寫多次,更好的做法是只寫一次,然后多次引用。提高重用率,減少代碼量
7:用hash傳參
比較: 普通傳參方式:參數量大,而且參數的順序很重要,
hash傳參方式:hash是一個key-value的集合,可包含任意類型的數據,用hash對象傳參,可以提高函數調用的靈活性,沒有順序的控制
如: test函數有6個參數,調用時有些為空,有些不為空,普通傳參: test(null,null,null,null,null,"hello"); 用hash傳參: test( {str:"hello"} ) 顯然第二種簡單直觀.
8)面向對象編程
1:面向對象
將數據和處理函數定義到了一個對象的內部,作為這個對象的屬性和行為存在,在對象的內部,屬性和行為通過this關鍵字來訪問,在對象的外部,用對象的屬性和對象的行為來調用。 額~,抽象的概念,理解起來就是別扭。
它的思維過程是定義一個對象,對象有自己的屬性和行為。屬性和行為都從屬於對象,於是有對象內,和對象外的概念。
整個程序可以由一堆對象組成,對象和對象之間可能會有通訊,為了能相互訪問,於是就有了私有和公有的概念。
2:js的面向對象
A:類的概念
一般的類定義,都要class關鍵字,js中的類沒有class關鍵字,它是用函數來充當類的。函數在js中既可以用作普通函數,也可以當類來使用,當充當類時,又擔負着構造函數的作用。
fuction test(){//code...}
調用方式: 函數充當普通函數,直接使用()進行調用。 如:test()
函數充當類時,使用new來實例化。如:var c=new test();
B:原型(prototype)
原型在js中是一個很重要的概念,因為JS是基於原型的語言,通過new實例化出來的對象,其屬性和行為來自於兩部分:1:構造函數(既函數本身),2:原型
原型的概念及由來:我們只需要知道“在聲明一個類的時候,同時就生成了一個對應的類的原型。”------------我理解為同樣的生命周期。
通過test.prototype就可以指向這個原型, 而原型也可以通過它的constructor屬性指向test類,具體指的是test類的構造函數。
只有當函數作為類使用,new出來一個對象時,原型才具有它存在的價值(個人觀點)
原型是個hash對象,也可以分開定義
如:test.prototype={ 分開定義:
name:"***", test.prototype.name="***";
type:"***", test.prototype.type="***";
say:function(){.....} test.prototype.say=function(){...};
}
C :優先級
當構造函數和原型中都定義了同名的屬性和行為,則構造函數中的屬性和行為優先級要高,它會覆蓋原型中的屬性和行為。
this關鍵字無論是出現在構造函數還是原型中它指代的都是實例對象。能改變this指向的函數就另當別論。
D:公有和私有
js中沒有public,protect,private等關鍵字,js中的公有還是私有是通過作用域來實現的。
用this.***定義的屬性都是公有的(原因:在構造和原型中的this 都是同一個實例對象,在原型中可以訪問得到),而用var ***定義的屬性都是私有的(在原型中訪問不到)。方法的公有私有也一樣用this區別開來。
習慣: 定義類時,一般我們會把屬性(變量)放在構造函數里------方便構造函數接收參數,而行為(方法)放在原型里。
原因:因為在內存中一個類的原型只有一個,寫在原型中的行為可以被所有實例共享,實例化時不會在實例的內存中復制一份;而寫在類中的行為,會每個實例都會復制一份。--------------為了減少內存消耗。
自己的話:實例化時,原型中的屬性和行為是引用,構造函數中的屬性和行為是復制。
3:繼承
瑣碎知識點:在js中,fuction作為普通函數存在時,直接使用()進行調用,函數內的this是指向window;
function作為類存在時,通過new實例化,類里面的this指向實例對象。
A:構造函數中屬性和行為的繼承
為了實現構造函數中屬性和行為的繼承,可以通過call/apply方法來實現。
如:
當實例化B的對象時就能訪問到構造函數里的屬性和方法。
B:原型中屬性和行為的繼承
瑣碎知識點:js中的傳值和傳址。
在js中,賦值語句會用傳值和傳址兩種不同的方式進行復制,如果是數值型,布爾型,字符型等基本數據類型,將復制一份數據進行賦值。---傳值
如果是"數值,hash對象等復雜類型"(數值,hash對象可包含簡單類型數據),在進行賦值時會直接使用內存地址賦值。-----傳址
原型中的屬性和行為的繼承,我們可以直接將A的原型賦值給B的原型,如B.prototype=A.prototype
這樣B的實例及B.prototype 是可以訪問A類原型中的say方法,但是prototype本質上是一個hash對象,它的賦值是傳址的方式,當我們在B的原型中在添加一個AddFunctionForB()方法時,由於傳址方式,在A的原型中也會添加B的AddFunctionForB()方法.
如:
為了解決這個問題,我們用另一種方法實現prototype的傳值-new somefunction() ----new出基類的對象,然后重新定向B類的構造。
在上面的代碼中我們定義了A類及A類的原型、B類;我們只需要將B類的原型中指向A類的實例對象(這時,B類會繼承A類的構造及原型中的屬性和方法),這樣就可以訪問A類原型中的方法。但由於這種賦值會使得B.prototype.constructor指向了A類的構造,所以我們要將它糾正,重新指向B類。
這樣就解決了上面的問題,既能從原型繼承方法,又不會引起不必要的混亂。
9)prototype和內置類
從第8點知道了prototype和類的關系,現在看看prototype和js自帶的內置類的關系。
Js的內置類包括Array,String,Function等 如Array提供length屬性,push.pop方法,String提供length屬性,replace.split方法 ,Fuction提供Call.apply方法.
一般內置類我們一般不用New實例化,習慣更簡單的方式,如 var a="sssss"; 而不是 var a=new String("sssss");
只要是類就會有原型,So 我們可以對內置類的原型進行修改,以重寫或擴展它的功能。
如:常見的有 Array.prototype.each=function(){....} Array.prototype.map=function(){.....}
里面可能會涉及到this指針的指向,我們只需要記住,在類的構造函數和原型中的this ,都是指向該類實例化的對象。
注意:內置類的方法可以重寫,比如toString()方法,但是 他的屬性不能重寫,如length;
10)標簽的自定義屬性
在html語言中的標簽一般有自己的屬性,如 id,class href等,我們有時候會用到自定義的屬性
為了從兼容性來考慮,用JS來讀取屬性時,筆者建議對於常規屬性,統一使用node.***的方式讀取,對於自定義屬性,統一使用node.getAttribute("***")讀取。
自定義屬性一個非常有用的技巧:----將普通字符串轉換為hash對象或數組
字符串的反序列化-----------通過eval函數來實現。
如在a標簽里自定義一個屬性<a id="a" userinfo="name:'alice' , age:22 , pwd:'123456' " ></a>
取到a的userinfo屬性值 var info = document.getElementbyId("a").getAttribute("userinfo");
alert(typeof info) //string 類型 訪問info.name info.age都訪問不到
info=eval("("+info+")"); 轉化為對象類型; 訪問info.name 輸出alice ; 訪問 info.age 輸出22;
11) 標簽的內聯事件和event對象
在IE下,event是window對象的一個屬性,是全局作用域下的; 在FF中,event對象是作為事件的參數存在。
在標簽的內聯事件中,FF下,使用arguments[0]可以訪問到event對象。不是內聯事件,可以給處理函數傳參來訪問 funtion(e){...}
關於event對象前面的章節中也有講過,這里就不再描述了。
12)一些規則
基本在前面的內容中均已描述過,包括css命名規則、注釋規則、html規范、css規范、js規范。
到這全書就結束了。將學習筆記記錄下來希望對看到該文的朋友有一定的幫助。O(∩_∩)O~
轉載請注明出處
原文地址:http://www.cnblogs.com/Joans/archive/2012/09/14/2685110.html