平凡无比的重复劳动也经常会发生一些匪夷所思的现象,发生的原因可能只是因为一个小疏忽,可能只是使用场景的改变,但是再平凡的代码,也会产生让你耗费大量时间才解决的问题。本篇就记录下这些细节,少走些弯路。长期更新,有人补充就更好啦。(文中的$代表jQuery,原谅我还做着jQuery项目= =)
1、清除定时器 clearTimeout
经常会维护一个变量,来控制定时器。
let timer = setTimeout(()=>{ alert('我是定时器')},1000) clearTimeout(timer);
上面的代码是不会弹出任何东西的,因为定时器被你清除了。那么这样呢
let timer = setTimeout(()=>{ alert('我是定时器') },1000) timer = setTimeout(()=>{ alert('我还是定时器') },1000); clearTimeout(timer);
弹出了‘我是定时器’。因为起了2个定时器,clearTimeout是针对定时器的ID来工作,而此时的ID已经指向第二个定时器,所以无法把第一个定时器清除。
有人会有疑问,谁会这么傻,起两个定时器,这不是活该吗?
实战中还是很有可能会发生的,毕竟往往调用别人的函数,很容易一不小心起了两个定时器。比如有人这么写了个函数
let timer = null;function showLoading(flag,time=10000){ //显示遮罩的函数 if(flag){ $('#load').show(); //显示遮罩 timer = setTimeout(()=>{ //显示一定时间后隐藏遮罩 $('#load').hide(); },timer) }else{ $('#load').hide(); //隐藏遮罩并去除定时器 clearTimeout(timer); } }
该函数的目的是显示遮罩,并且可以控制显示时间。
如果调用一次showLoading(true,30000)后在没有调用showLoading(false)的情况下再调用了一次,会发现第二次可能不到30S遮罩就消失了,原因就是起了两个定时器,而第一个定时器把你的遮罩取消了。
所以实际开发中起定时器前一般要先清除,这样别人就算调了两次你的函数,也不会有问题,写公共函数的同学尤其注意了
let timer = null;function showLoading(flag,time=10000){ //显示遮罩的函数 if(flag){ $('#load').show(); //显示遮罩 if(timer !== null){ //判断是否有定时器 clearTimeout(timer); } timer = setTimeout(()=>{ //显示10S后隐藏遮罩 $('#load').hide(); },timer) }else{ $('#load').hide(); //隐藏遮罩并去除定时器 clearTimeout(timer); timer = null; //清除后变为null,表示该定时器已经被清除 } }
靠timer是否等于null来判断是否有定时器在工作
2、call,apply
非常需要注意,call和apply后面需要先写一个对象,代表谁来调用这个函数,之后才是函数的参数,千万不能把上下文对象漏了,否则后果很严重。
function add(a,b){ return a+b; } add.call(null,2,3); //5add.call(2,3); //NaN
像这种情况还行,错误很明显。如果是在处理延迟对象的时候漏掉了。
//defArr是用来存放延迟对象的数组$.when.apply(defArr).done(function(){}); //此时相当于$.when().done()
done的函数就起不到回调函数作用,它会马上执行,不等待延迟对象的返回结果。有时这错误还很难发现,所以要特别注意。赶紧检查下你们代码中有没有漏掉第一个参数的地方。
3、removeEventListener
document.body.addEventListener('click',function(){ alert(1); })document.body.removeEventListener('click',function(){ alert(1); })
此时点击body还是会弹出1。因为移除时传入的参数必须与添加处理程序时使用的参数完全相同。两个匿名函数,虽然作用一样,但是却不相等。所以要这样
var ev = function(){ alert(1); }document.body.addEventListener('click',ev);document.body.removeEventListener('click',ev);
习惯jquery的用户是不是惊呆了。赶紧记住!
4、活用Deferred.then
这个点针对jquery用户,当然也可以套用到ES6 的Promise上。
then不仅可以简写done和fail,同时还有个更强大的功能,就是返回一个新的Deferred对象。
比如,有这么一个库函数
function readFile(){ return $.Deferred((def)=>{ setTimeout(()=>{ def.resolve({ params:1 }) },1000); }) }
新手使用readFile的时候可能会这样写
function test(){ return $.Deferred((def)=>{ readFile().done(json=>{ def.resolve(json.params) }) }) } test().done(json=>{ alert(json); })
没毛病,的确处理了readFile的返回结果。但是额外在readFile外面包了一层,多了一层嵌套。如果使用then的话会简洁很多
function test(){ return readFile().then(json=>{ //注意这里是then不是done哦 return json.params; }) } test().done(json=>{ alert(json); })
then会返回一个新的Deferred对象,活用它代码会简洁很多。如果不是很清楚,可以去百度下jquery done和then的区别,这里不做详细解释。
ES6的Promise也适用,不用每次都去调用Promise构造函数生成新的Promise,而是选择使用then。
5、sort()排序
let arr = [3,2,1]; arr.sort(); //1,2,3
sort()函数按升序排列数组项。但是它会调用每个数组项的toString()方法,然后比较得到的字符串,所以如果出现两位数就达不到效果了。
let arr = [3,2,13]; arr.sort(); //13,2,3
13跑到最前面了。因为"13"<"2"。
所以正确的写法是在参数里写上判断函数。
有人可能会这么写
let arr = [3,2,1]; arr.sort((a,b)=> a>b); //1,2,3
数组里全是数字的情况可以胜用。但是如果数组里的元素是字符串就会失败。
let arr = ["3","2","13"]; arr.sort((a,b)=> a>b); //"13","2","3"
因为有很多情况,期望是数字的值,实际上是字符串。比如从input中获取值。所以以下代码是最靠谱的
let arr = ["3","2","13"]; arr.sort((a,b)=> a-b); //"2","3","13"
通过a-b来控制升序。"13"<"2"但是"13" - "2" > 0
6、setTimeout和箭头函数
大家都知道箭头函数是可以将this绑定为定义时的对象,但是我在Vue开发中却遇到了个匪夷所思的现象,this还是指向了window。不信邪的我赶紧写个测试代码压压惊
let a = 1;let obj = { a:2, foo(){ setTimeout(()=>{ console.log(this.a); },2000) } } obj.foo(); //2
舒了口气,看来没学错。
再仔细看看,发现定时器实际生效时,组件被销毁了,于是加了个keep-alive,缓存组件,this终于成功指向了该组件。
未完待续。。。有错误的还望指正。
作者:闪闪发光的狼
链接:https://www.jianshu.com/p/77a0e25f96bd