編寫高質量代碼:Web前端開發修煉之道(四)


這一節是繼上一節高質量的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就指向調用者,只有碰到setTimeoutsetInterval、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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM