我对封装的理解
2018-12-02 06:15:05来源:博客园 阅读 ()
希望能自己独立的写出这个小册。在博客园的第一篇博文,还是写关于技术方面的,但愿语言组织好点。
自己也不算是初级小白了,毕竟学习前端知识很长一段时间了。两个月前也尝试写过一些封装,但对封装质量并不满意,后来读了一本书,叫《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是如何实现继承的 2020-02-25
- js工厂函数创建对象与对象构造函数的理解 2019-08-14
- JS变量、作用域 2019-08-14
- call,apply,bind的理解 2019-08-14
- prototype,__proto__,constructor理解 2019-08-14
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash