当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 我对封装的理解

我对封装的理解

2018年12月03日  | 移动技术网IT编程  | 我要评论

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

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

 

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网