我对封装的理解

2018-12-02 06:15:05来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

希望能自己独立的写出这个小册。在博客园的第一篇博文,还是写关于技术方面的,但愿语言组织好点。

自己也不算是初级小白了,毕竟学习前端知识很长一段时间了。两个月前也尝试写过一些封装,但对封装质量并不满意,后来读了一本书,叫《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的数组),有值时是一个定积分,会返回结果。 

 

对于对象的方法的扩展还有很多。可以写成个文件,保存起来。初入博客园,先写那么多。自己水平有限,入博客园的目的就是为了提高自己的代码编写质量和写作能力,日后还要多加努力!

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:写小程序的一些小坑

下一篇:关于javascript闭包(Closure)和return之间的暧昧关系