前端开发注意细节
此博文内容比较浅显,但是就目前在项目开发中,仍然发现这些细节被同学们所忽略,写此文权当给给同学们做个总结,有则改之,无则加勉,做的更好的同学请在文后做个评论,以供大家学习参考。
js设置默认值
在ES6之前,没有设置默认值的语法,大家通常使用以下方式设置默认值:
function fn(num) {
// 设置默认值
num = num || 1;
console.log(num);
}
但是问题在于,fn函数的参数num为0、false、null、undefined、空字符串的时候,num都会被赋予1的默认值,显然:传参为0时,并非如我们所愿设置为默认值1。
此时最好以一下判断方式,来赋予默认值
function fn(num) {
num = typeof num === 'undefined' ? 1 : num;
console.log(num);
}
默认值,顾名思义,实在没有给函数传递参数是,为该参数默认的值,翻译成js代码,就是该参数为undefined的时候,才使用该值。因此,以undefined为目标来判断相对比较科学,因为其他几个类型的数据,虽然,从其字面值上来讲在某些时候确实可以和undefined相等(),但是从数据上来讲,其实有实质内容的,也可以说是占有实际内存空间的,并不能和undefined完全等价(=)。
forEach的优化
在开发过程中,数组forEach这个API也是经常被用到的,主要用来遍历数组,但是有些使用场景还是有必要优化的。比如,有一个数组,只要需要判断其中是否存在大于4的元素,可能很多人第一联想的是遍历该数组,去逐个判断是否符合条件。代码如下:
var a = [1, 2, 3, 4, 5, 6, 7];
var result = false;
a.forEach(function(item) {
if(item > 4) {
result = true;
}
});
console.log('是否存在大于4的元素:', result);
上述代码是否有问题呢?其实是没有问题的,但是,其遍历次数和数组的长度是一样的,其实遍历到第5个元素时,就可以得到结果,没有必要再进行后面了遍历了。示例中的数组元素比较少,可能还体现不出来性能的问题,但是如果数组元素有上千个时,节省一部分循环,还是能其他一些优化效果的。可以使用for循环,在得到结果之后break即可。或者直接使用数组的some方法。
多个条件并列
一般在业务比较复杂的地方,经常会出现多个条件并列的if语句,很多同学的做法是多个条件表达式同是并列显示一行,这样执行起来当然没有问题,但是非常不方便阅读,编辑器没有设置代码自动换行,需要拖动很长的水平滚动条,等看完后面的条件,前面的也忘记的差不多了;也不符合每行不超过80个字符的编码风格,并且如果配置了eslint等也会提示警告。
其实完全可以把多个条件,多行并列显示,如下:
...
if(
a === 1 &&
b === 1 &&
c === 1 ||
....
) {
// do something
}
...
每个条件语句垂直并列展示,if条件行代码也不会过长,看上去非常清晰、已读,条件语句和上下文的关系也很容易阅读、记忆。
同理,在使用web component方式开发时,经常需要向组件传递一堆数据,如:
<Component data1={data1} data2={data1} data3={data3} data4={data4} data5={data5} ... />
把很多数据传递写到一行代码中,同样也会造成上述阅读、记忆不便的问题,稍加改造,分行编写,就好清晰很多,如:
<Component
data1={data1}
data2={data1}
data3={data3}
data4={data4}
data5={data5}
...
/>
模板语言缩进问题
现在很多项目使用mvvm的框架来开发,像AngularJS、RegularJS、VueJS都有其自己的模板系统,也自有一套语法规则,一般情况下,模板语言都将会有处理条件分支的if-else语句,也有遍历数据的each语句,在开发过程中,有些同学为了体现html的结构,在编写if-else和each语句是,不缩进其下层代码,如(模板语法参考RegularJS):
<div>
{#each list as item}
<span>{item}</span>
{/each}
</div>
有部分同学,可能为了体现div和span父子关系,在标签所在行,并没有进行层级缩进,上述代码只有一层each语句,看起来,还能接受,但是如果再加上条件判断,如:
<div>
{#each list as item}
{#if item < 10}
<span>小于10的数字:{item}</span>
{#else}
<span>小于10的数字:{item}</span>
{/if}
{/each}
</div>
一眼看上去,each和if-else搅和在一起,可读性特别差。从我理解来说,组件模板中,不知能只注意html的结构,因为除了html,逻辑处理也是我们表达程序目的内容,如果将只为了程序html结构,导致代码可读性很差,代码整体交给不明了,无疑会增加代码维护成本。特别是对于患有强迫症的程序员,眼里根本容不得一个多余的空格,哪怕是在行尾,也要将其干掉。
我推荐的模板代码格式,逻辑块(each、if、else等)和组件标、html标签层级分明,对于逻辑要输出的内容一目了然。如:
<div>
{#each list as item}
{#if item < 10}
<span>小于10的数字:{item}</span>
{#else}
<span>小于10的数字:{item}</span>
{/if}
{/each}
</div>
回调函数的判断
js开发中,回调函数使用频率极高,很多情况下,回调函数最为一个函数或者方法的可选参数,既然是可选,肯定就存在没有传参的情况,为了容错在执行回调函数之前,都会对其判断,如下:
function fn(callback) {
// ...do something...
if(callback) {
callback();
}
// ...do something...
}
fn(() => {console.log('Hello')})
很多情况下,对回调函数的判断都是使用:if(callback),其实,这样是不严谨的,大家知道js是若类型的语言,函数的参数类型可以随意设置,默写开发中才使用fn函数时,没有注意或者不熟悉,可能会导入一个其他类型的值,if中的条件依旧成立,但此时在在执行如上代码时,则会报错,如下:
function fn(callback) {
// ...do something...
if(callback) {
callback();
}
// ...do something...
}
fn(100); // 报错,因为100这个参数非Function类型
因此,if(callback)判断容错并不严谨,既然是回调函数,一定要确定该参数为Function类型,才可以去调用执行,所以,回调函数容错判断应该改为:if(typeof callback === 'function')最为合理、严谨,如下:
function fn(callback) {
// ...do something...
if(typeof callback === 'function') {
callback();
}
// ...do something...
}
fn(100); // 不会报错
fn(() => {console.log('Hello')});
可以考虑使用一些数据结构
最初学习编程的时候,老师就说:程序设计=数据结构+算法,不过后来在业务开发中,很少去直接使用数据结构和算法,这些东西都被编程语言封装过了,但是少用并不代表不用,特别是当下云计算、大数据、人工智能这些技术的星期必然算法密不可分,扯的有点远了,其实在日常业务开发中,如果能留意还是能发现一些数据结构的应用场景的。
如二叉树进行排序、寻找临界值(最大、最小值),如果有这方面的需求也可以考虑;还有队列,在开发买票业务是有排队场景的时候也可以用上。还有其他数据结构,如堆、栈也有其运用的场景。
之前,在开发某个业务系统,有个前端的需求,需要对一个列表(数组)进行拖动排序,如:拖动A到B的位置,其过程是:第一步将A从列表中移除,第二步将A插入到B所在的位置。上述过程,看似简单,如果纯粹使用JS数组来处理的话,此处需要将数组元素A拿掉,A之后的元素向前移动;第二步,将A插入到B的位置,可以分两步,第一B包括之后的元素,向后移,然后把A放到B的位置,使用程序处理起来还是比较复杂的,此处如果将该列表构造成一个链表,在移除和插入的位置,只需要修改目标位置的前后和目标元素的前后指针即可,运算次数会降低不少。
当然,数据比较的少的时候,JS数组的API就能简单的处理,使用数据结构解决问题,有点大炮打蚊子的感觉,但是我认为还是有必要的,数据是一个动态的东西,现在少未来未必少,比如上述中的需求的,后期列表元素的数量就上升到了几百个。
局部loading
这个问题可能是交互设计的问题,不过也可以是前端问题。局部loading的名字是我自己起的,和其对应的是全局loading,在当下单页应用横行的时代,数据都是前后端分离,所有的数据都是用过ajax请求获取,为了交互同意,很多系统在设计的时候,请求loading的过程都会弹出一个全局遮罩层上面有个loading图标,在请求时屏蔽页面上所有的操作,这就是我所谓的全局loading。而局部loading则与之相反,那个操作点发出的请求,只需要在对应的操作点上显示loading即可如:点击按钮A,发送一个请求更新列表,只需要在按钮A上面显示loading或者loading的文案。
按照我的观点,是反对所有地方都使用全局loading的,因为这样违背了异步请求的初衷,异步的目的就是避免阻塞,请求过程不影响其他的操作,等拿到响应数据,再把数据更新到前台,而全局loading,把整个前台界面全部屏蔽遮罩,无法进行任何操作,比如:页面滚动加载数据,此时页面被全局loading屏蔽、遮挡,页面内容不能完整查看,所有操作都收到限制,如果请求时间过长,体验很很差。
XXS攻击
关于XXS攻击的内容,网上很多,也是日常web开发中经常要注意的一个安全问题,并且现在很多前端框架对此也有一定的而防范。我要说的,也是一个XXS相关且容易忽略的点:从页面URL中获取参数,并把参数的内容输出到页面上,比如,URL中有一个a参数,在前端代码中,使用js直接获取参数后,没有通过XSS相关编码的处理,直接或者间接的输出到页面上,此时a参数中含有JS脚本的代码,就好造成脚本注入的安全隐患,开发过程中,如果出现此场景还是,要三思而后行。