我對封裝的理解

来源:https://www.cnblogs.com/MH-wang-blog/archive/2018/12/02/10053053.html
-Advertisement-
Play Games

希望能自己獨立的寫出這個小冊。在博客園的第一篇博文,還是寫關於技術方面的,但願語言組織好點。 自己也不算是初級小白了,畢竟學習前端知識很長一段時間了。兩個月前也嘗試寫過一些封裝,但對封裝質量並不滿意,後來讀了一本書,叫《JavaScript設計模式與開發實踐》,從中受益很多。作者是我們國內的騰訊前端 ...


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

自己也不算是初級小白了,畢竟學習前端知識很長一段時間了。兩個月前也嘗試寫過一些封裝,但對封裝質量並不滿意,後來讀了一本書,叫《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的數組),有值時是一個定積分,會返回結果。 

 

對於對象的方法的擴展還有很多。可以寫成個文件,保存起來。初入博客園,先寫那麼多。自己水平有限,入博客園的目的就是為了提高自己的代碼編寫質量和寫作能力,日後還要多加努力!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • frameset 定義 W3C是這樣定義frameset框架的,通過使用框架,你可以在同一個瀏覽器視窗中顯示不止一個頁面。每份HTML文檔稱為一個框架,並且每個框架都獨立於其他的框架。註意,這是HTML框架,不是前端框架,與node.js,vue.js等不同。 垂直切割 屬性為cols。例如: <f ...
  • 先說幾個概念: 1、js代碼從上往下執行 2、變數提升: 變數提升是瀏覽器的一個功能,在運行js代碼之前,瀏覽器會給js一個全局作用域叫window,window分兩個模塊,一個叫記憶體模塊,一個叫運行模塊,記憶體模塊找到當前作用域下的所有帶var和function的關鍵字,執行模塊執行js代碼,從上到 ...
  • 聲明 本系列文章內容全部梳理自以下幾個來源: 《JavaScript權威指南》 "MDN web docs" "Github:smyhvae/web" "Github:goddyZhao/Translation/JavaScript" 作為一個前端小白,入門跟著這幾個來源學習,感謝作者的分享,在其基 ...
  • 什麼是作用域:瀏覽器給js的生存環境叫作用域。 什麼是變數提升: Js代碼執行前,瀏覽器會給一個全局作用域window Window分兩個模塊一個是存儲模塊一個是執行模塊 存儲模塊找到所有的var和function 關鍵字給這些變數添加記憶體地址 執行模塊,代碼從上到下執行,遇到變數就會去存儲模塊查找 ...
  • 1.事件 瀏覽器客戶端上客戶觸發的行為都稱為事件 所有的事件都是天生自帶的,不需要我們去綁定,只需要我們去觸發。 通過 obj.事件名=function(){} 事件名:onmouseover 滑鼠懸浮 onmouseout 滑鼠移除 onmousedown滑鼠按下 onmouseup 滑鼠抬起 o ...
  • 元素的屬性 div.attributes 是所有標簽屬性構成的數據集合 div.classList 是所有class名構成的數組集合 在classList的原型鏈上看以看到add()和remove() 1.client系列 (1) clientWidth/clientHeight 是我們設置的寬和高 ...
  • @[toc] .val()實例方法的三種用法 1. $('xxx').val() : 獲取匹配的元素集合中第一個元素的當前value屬性值(property) 2. $('xxx').val(value) : 設置匹配的元素集合中每個元素的value屬性值(property) 3. $('xxx') ...
  • 在列表展示中,經常會使用卡片的內容展示形式,為了美觀,常常要求各卡片間的間隙是一致的。 卡片內容不一樣可能高度不等,但一般來說為了整體的一致性,會限制每個卡片的寬高都相等。 本文就基於寬高一致的多個卡片,在不同屏幕大小下,每行卡片數量可能有調整,考量如何實現等間隙的佈局。 點我預覽 放置一張張卡片項 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...