平凡无比的重复劳动也经常会发生一些匪夷所思的现象, 发生的原因可能只是因为一个小疏忽, 可能只是使用场景的改变, 但是再平凡的代码, 也会产生让你耗费大量时间才解决的问题. 本篇就记录下这些细节, 少走些弯路. 长期更新, 有人补充就更好啦.(文中的 $ 代表 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); //5
- add.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
未完待续... 有错误的还望指正.
来源: http://www.jianshu.com/p/77a0e25f96bd