1. 程式人生 > >我對封裝的理解

我對封裝的理解

希望能自己獨立的寫出這個小冊。在部落格園的第一篇博文,還是寫關於技術方面的,但願語言組織好點。

自己也不算是初級小白了,畢竟學習前端知識很長一段時間了。兩個月前也嘗試寫過一些封裝,但對封裝質量並不滿意,後來讀了一本書,叫《JavaScript設計模式與開發實踐》,從中受益很多。作者是我們國內的騰訊前端工程師,他用通俗易懂的語言組織方式讓我明白了很多開發技巧。

 

封裝一個方法或者外掛是很有必要的。在實際工作開發中,對於出現頻率很高的技術,完全可以把核心技術封裝起來,這樣做是很節約時間成本的,因為以後再碰到就不用在花費時間再寫一遍。當然,如何很好的來封裝是很有考驗性的。封裝一個東西,我們要考慮到很多方面。比如邊界條件是否滿足、程式碼可複用如何、再高點,對於效能的要求等等。

我認為,封裝好一個外掛,不僅要多方面考慮,更重要的是其封裝思想(當然自己封裝能力很一般)。大部分設計模式的主題是將不變的部分和變化的部分分隔開。我們把不變的部分封裝起來,把可變的部分儘量更好的組織,這是很有必要的。

比如,寫一個驗證暱稱的功能,當輸入完畢,點選提交,然後驗證輸入的暱稱是否合法,這個小功能完全可以寫成外掛的形式。

按照那句話:將不變的部分和變化的部分分隔開。我們把不變的部分封裝起來。

開始找認為可變和不可變的部分,我覺得可變的是合法或不合法的字元,而不可變的是驗證的這一過程(無非是一個正則匹配過程),最重要的是對哪個元素進行驗證,當然還要獲取到提交按鈕。於是進行分離。

最原始的做法是把可變的用函式引數代替:

 1         let testRegular = (reg,btnElem,txtElem) => {
 2             btnElem.addEventListener('click',() => {
 3                 let val = txtElem.value;
 4                 if (val.match(reg)) {
 5                     alert('輸入暱稱非法!');
 6                 } else {
 7                     alert('輸入通過!');
8 } 9 },false); 10 }

但是這樣是遠遠不夠的,因為函式引數多的話我們不好進行管理。不如把引數改為物件的形式傳入。這樣使程式碼更加美觀,把可變的都放在一個物件裡,更好管理。

 1         let testRegular = (obj) => {
 2             obj.submitBtn.addEventListener('click',() => {
 3                 let val = obj.txtElem.value;
 4                 if (val.match(obj.testReg)) {
 5                     alert('輸入暱稱非法!');
 6                 } else {
 7                     alert('輸入通過!');
 8                 }
 9             },false);
10         }

但是,我們並不滿足於此。我想把驗證暱稱的功能擴充套件到可以驗證表單中的輸入框,比如事件型別,我不但可以點選提交,還可以敲擊鍵盤enter提交,失去焦點時就會驗證等等,於是進行改進:

 1         let obj = {
 2             txtElem: document.querySelector('#nickName'),
 3             submitBtn: document.querySelector('#submit'),
 4             testReg: /\s/g
 5         };      // obj 物件是可變的部分
 6 
 7         let testRegular = (obj) => {        // 不變的部分
 8 
 9             return function(){
10                     let val = obj.txtElem.value;      // 不好的是,如果是點選事件,只能驗證一個輸入框。
11                     if (val.match(obj.testReg) || val == '') {
12                         alert('輸入暱稱非法!');      // 可把其中的操作(也是可變的)寫入一個函式中,呼叫即可
13                     } else {
14                         alert('輸入通過!');
15                     }
16             }
17         }
18         obj.oEven = testRegular(obj);
19      // 可變的事件操作 
20         obj.submitBtn.addEventListener('click',obj.oEven);
21         obj.txtElem.addEventListener('keydown',function(e){
22             if(e.keyCode === 13){
23                 obj.oEven();
24             }
25         });
26         obj.txtElem.addEventListener('blur',obj.oEven);

當然在真正的開發中,肯定沒那麼簡單,特別是驗證後的操作,不可能只是彈個窗的效果,所以,條件語句內的程式碼也是可變的部分。這時候可以把條件語句裡的程式碼,寫在一個個的函式裡,判斷後,呼叫即可。

寫完後,發現這個例子並不恰當。。。初衷是驗證驗證暱稱是否合法。如果要對錶單進行驗證,會更復雜。畢竟是第一篇博文,以後會更加嚴謹,,,

封裝不單指封裝外掛,還可以擴充套件物件方法。比如:寫一個自己的pop陣列方法,傳入引數(索引),就能刪除該陣列索引處的值:

 1         Array.prototype.myPop = function(num){
 2             let ary = [];
 3             this.forEach((item,index) => {
 4                 if(num !== index){
 5                     ary.push(item);
 6                 }
 7 
 8             });
 9             return ary;
10         }

完善邊界條件:如:輸入負值時刪除的是倒數的索引值;超出索引時報錯;非數字型別引數也報錯

 1         Array.prototype.myPop = function(num){
 2             let ary = [];
 3             let len = this.length;
 4             if(typeof(num) !== 'number' && (num < -len || num >= len)){
 5                 console.error("須輸入數字,且大小不得超過陣列長度!");
 6             }
 7             if(num < 0 && num >= -len){
 8                 num = num + len;
 9             }
10             this.forEach((item,index) => {
11                 if(num !== index){
12                     ary.push(item);
13                 }
14             });
15             return ary;
16         }

當然,可以把開方法擴充套件到字串:只需把字串先變成陣列(split方法),對陣列操作,在拼接起來就行了(join)。

 1         String.prototype.myPop = function(num){
 2             let strArr = this.split(''),
 3                 newArr = [],
 4                 len = strArr.length;
 5             if(typeof(num) !== 'number' && (num < -len || num >= len)){
 6                 console.error("須輸入數字,且大小不得超過陣列長度!");
 7             }
 8             if(num < 0 && num >= -len){
 9                 num = num + len;
10             }
11             strArr.forEach((item,index) => {
12                 if(num !== index){
13                     newArr.push(item);
14                 }
15             });
16             let str = newArr.join('');
17             return str;
18         }

對應的 --->  myPush() 方法:在指定索引後插入指定的值:索引是正時在索引前插入,索引是負時,在索引之後插入。以陣列為例:

 1         Array.prototype.myPush = function(n,val,position = 'after'){            // 在索引位置之後插入指定的數字 num
 2             let arr = [],
 3                 len = this.length;
 4             
 5             if (typeof (n) !== 'number' && (n < -len || n >= len)) {
 6                 console.error("須輸入數字,且大小不得超過陣列長度!");
 7             }
 8             if (n < 0 && n >= -len) {
 9                 n = n + len;
10             }
11             if(position === 'after'){
12                 this.forEach((item, index) => {
13                     arr.push(item);
14                     if (n === index) {
15                         arr.push(val);
16                     }
17                 });
18             }else if(position === 'before'){
19                 this.forEach((item, index) => {
20                     if (n === index) {
21                         arr.push(val);
22                     }
23                     arr.push(item);
24                 });
25             }
26             return arr;
27         }

對Math物件的擴充套件:比如對普通的一元多項式的積分;(Math物件沒有prototype)

 1         Math.integral = function(ratioAry,rangeAry = null){
 2             let result = 0;
 3             let isAry = Object.prototype.toString;
 4             
 5             if(isAry.call(ratioAry) != '[object Array]' && (rangeAry != null || isAry.call(rangeAry) != '[object Array]')){
 6                 console.error("須輸入陣列形式的引數!");
 7             }
 8 
 9             ratioAry.forEach(item => {
10                 item[1] = item[1] + 1;
11                 item[0] = item[0] / item[1];
12             });
13             if(!rangeAry){
14                 return ratioAry;
15             }else{
16                 let upper = rangeAry[0],
17                     lower = rangeAry[1];
18                 
19                 ratioAry.forEach(item => {
20                     result += item[0] * (Math.pow(upper,item[1])) - item[0] * (Math.pow(lower, item[1]));
21                 });
22                 return result;
23             }
24 
25         }

呼叫時,第一個引數需輸入二維陣列 [[1,1],[2,-2],....]  每一個長度為2的子陣列表示一個項,第一位表示係數,第二位表示指數。而函式的第二個引數也是一個數組(如 [2,6] 是個一維長度為2的陣列),有值時是一個定積分,會返回結果。 

 

對於物件的方法的擴充套件還有很多。可以寫成個檔案,儲存起來。初入部落格園,先寫那麼多。自己水平有限,入部落格園的目的就是為了提高自己的程式碼編寫質量和寫作能力,日後還要多加努力!