分享一次失败的项目实践经验
一、开篇
最近在网上看到了一款canvas实现网页涂鸦效果的作品,感觉这个效果比较奇特而且在以前没有学习canvas这样的功能是不可思议的,所以本人秉着程序员的那一份执着,花了两三个小时的时间来研究了一下canvas涂鸦作品的代码,发现里面代码比较精辟,但是美中不足的是有些代码的结构会比较的混乱,让人感觉层次上面有点不太分明。所以本人就打算对这个代码结构进行重构使其更具有可读性。但是理想是丰满的现实是骨感的,经过这一次代码的分析和实践下来,对我也是打击蛮大的,以前从来都是认为前端只要能够熟悉调试工具就行了,但是这一次调试工具居然“失灵”了,具体的失灵原因最终也没有找到,失灵表现在:在调试上面去监控元素会发现得到的值是正确的,但是最终运行的结果却是错误。打击虽大,但是我也是挺高兴的,至少我现在懂得了学会了一些代码重构的方法和理解清楚了什么是this的指向之类的问题
二、代码结构的编写
由于我为了巩固之前在深入学习JavaScript(二)中提到的继承,所以采用的方法是将执行的方法都书写在new方法(对应下面的canvas)的父类中,然后在window.onload事件中实例化canvas类,canvas类中是将传入的参数传递给canvas的父类来的
简化后的JavaScript结构代码如下:
window.onload=function(){ const A=1; var B=2; new canvas(A,B); } function canvas(a,b){
var c=3;
console.log(this.c): this.init.apply(this,arguments); } canvas.prototype={ init:function(A,B){ console.log(A+","+B); } }
这里面我们需要注意的是:this.init.apply(this,arguments);这句话主要是用来this指的是当前调用的对象,也就是canvas对象(通过new canvas(A,B)来调用),但是你有没有注意到将变量c打印出来的结果是undefined,但是我们说this指的是canvas对象,这是为什么呢?其实我们在深入学习JavaScript(一)中就已经介绍过了,只不过没有在实际的项目中进行详解,因为通过var创建的变量c,是在window对象中声明的,不是在canvas对象中,所以我们只需要将var c=3改为this.c=3这样就可以正常运行了。还有这个时候this所指代的canvas对象包含有它的父类。总的一句话:调用这个方法或者是类的时候调用的对象是什么,那么这里面的this指代的就是什么。如果在调用对象不明确的时候,我们可以在调用对象的上级查找,因为如果是当前的对象没有发生变化的时候是会继承上级的this对象到下级的,这样会导致当前的对象比较难发现,示例:
window.onload=function(){ const A=1; var B=2; console.log(this); new canvas(A,B); } function canvas(a,b){ this.c=3; console.log(this.c); this.init.apply(this,arguments); } canvas.prototype={ init:function(A,B){ console.log(A+","+B); this.test(); }, test:function(){ alert('this is test'); } }
分析:在this.test()这个执行语句中,我们单从这个语句很难分析出this指代的是什么,所以我们需要向上查找,在canvas对象中的apply方法调用了init这个函数,由于这个canvas对象是被new canvas(A,B)初始化的,所以canvas对象中的this指代的是canvas对象,即canvas对象中的方法和变量。因为this对象没有改变,所以this会一级一级的向下继承,最终this.test()中的this就是指代canvas对象
PS:apply的用法跟call的用法相似,但是只要的用途是用来将当前传入的参数传递给父类,这里我们以apply为例来进行讲解
在上面的例子中我们对apply这个方法传入的参数是:A=1;B=2,这一步相信地球人都可以看得出来。所以关键还是理解arguments这个代表什么意思,arguments这个会被编译器解析为不定长的数组(其实在JavaScript中的数组都是不定长的,这个有别于其他的高级语言,例如:C#、java),然后会分别将A=1,B=2分别存放在数组的0号位上和1号位上,所以arguments=[1,2],call的作用是一样一样的,但是不是通过arguments这个参数来代表要传入的参数,而是直接将参数写在里面,例如:call(this,A,B)
三、其他一些需要注意的细节和知识点回顾
CSS 精灵技术
CSS精灵技术这个对于比较年老的程序员来说应该都听说过吧,但是新一代的程序员中使用的比较少
个人认为主要的原因是:
1、制作一张精灵图花费的时间比较久(但是node.js的出现已经有了生成图片的工具)
2、精灵图不是矢量图,所以不能够适应现在的响应式布局
3、精灵要求在CSS定位上面的操作准确,这个会增大程序员的时间成本
但是CSS精灵技术也是有自己的有点的
1、精灵图其实就是将图标集成为一张图片,所以在浏览器向服务器发送请求的时候,服务器也就只需要返回一次数据就够了(当然这里没有包括JavaScript、HTML、CSS文件和其他文件),这个也是最重要的。
PS:这里面的CSS精灵技术权当温故知新,如果没兴趣的同学可以跳过。本人也认为现在的SVG图标已经基本上代替了以前的这种CSS精灵技术,而且更加方便
好的,我们这就来开始精灵技术的案例
我们先准备一张雪碧图
然后HTML代码是这样的
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSS精灵技术DEMO</title> <style> #showArea{ } .btn{ width:200px; height:65px; background:url(DEMO.png); background-position:0px -65px; } .btn:hover{ background-position:0px -130px; } </style> </head> <body> <div id="showArea"> <div class="btn"></div> </div> </body> </html>
运行后的结果是:
几个获取坐标的API之间的辨析
这里我们以宽相关的来做辨析,获取长的基本上一模一样
offsetX,clientX,offsetLeft,window.screen.width
offsetX:所在的DOM对象中的坐标,以DOM对象的左上角坐标为原点
clientX:获取的是相对于WINDOW对象的宽度
如果是DOM对象与左边距为0的时候,那么clientX=offsetX
但是如果是存在margin-left这个值得时候,那么clientX=offsetX+offsetLeft
相信说到这里大家都猜到offsetLeft指的是什么吧,没错offetLeft指的是DOM对象相对于WINDOW的偏移量
window.screen.width:这个具体指的是屏幕的分辨率宽度,一般在移动设备上使用
四、相关的源码
这里面提供了一个制作失败的代码供大家引以为鉴,里面的代码中有很多功能都没有实现,主要的问题是在画布中画笔和线段不能同步,这个也是导致失败的原因,希望各位大神看一看怎样修改,知道的麻烦再留言中告诉我一下,在此不胜感激,以上就是在这个项目中的体会所得。下载地址