53

写在前面

(难得从繁重的业务代码中抽身,更新一下文章)前端框架和技术日益发展,但是不管怎么变,js永远都是最重要的基础,本文记录和总结一些日常开发中常见的js代码技巧和误区,不定期更新。

正文

1. 避免链式声明

后果:可能引入全局变量

    //错误的写法
    var a = b = 0;

这段代码中,b实际上被声明为全局变量。因为操作符优先级是是从右往左,所以该语句相当于:

    var a = (b = 0)

此时b未声明就被直接赋值,所以b成了全局变量

2. 单一var原则

这条规则的意思是,把函数内部的所有变量,放到顶部声明。比如:

    //示例
    function A(){
      var a = 1,
          b = 2,
          c = a + b ;
    }

优点:

  • 便于查找函数内部使用的局部变量
  • 防止变量未定义时就被使用
  • 防止变量声明提升后引发的误解

关于第三点,这里举个例子说明:

  var x = 1;
  function A(){
    console.log(x);//第一处输出 ,注意结果
    var x = 2;
    console.log(x);//第二处输出 2,没问题
  }  

从代码上看,第二处输出肯定没问题,可能会有人认为第一处输出的是1,因为此时在函数内部还没声明变量x,根据作用域链,向外层查找的话,x值为1。但是实际输出的值应该是undefined,因为js允许在函数任何地方声明变量,并且无论在哪里声明都等同于在顶部声明,这就是声明提升。所以上面的代码相当于:

  var x = 1;
  function A(){
    var x;//提升到顶部
    console.log(x);//此时已声明 未赋值
    x = 2;//赋值
    console.log(x);
  }  

3. 使用for循环时,缓存长度值

通常用使用for循环遍历数组时,会采用以下写法:

for(var i = 0;i<arr.length;i++){
 // 具体操作
}

这段代码存在的问题在于,在循环的每个迭代步骤,都必须访问一次arr的长度。如果arr是静态数值还好,但是我们在使用js时可能会碰到arr是dom元素对象,由于dom对象载页面下是活动的查询,这个长度查询就相当耗时

//用len缓存长度值
for(var i = 0,len = arr.length;i<len;i++){
 // 具体操作
}

按照上面的代码,我们在第一次获取长度值时就缓存这个长度值,就可以避免上述问题。

4. 使用for-in时,增加hasOwnProperty()判断

for-in通常用来枚举对象的属性和方法,但是这个方法会枚举范围包括对象和对象的原型对象(对原型对象不了解的可以看看我之前写的文章传送门)此时,利用hasOwnProperty()方法可以帮我们过滤出只在对象本身上的属性和方法,或者只在原型链的属性和方法

for(var key in obj){
   if(obj.hasOwnProperty(key)){
     // 对象本身的属性或者方法
  }
  else{
     // 原型链的属性和方法
  }
}

/* 下面是一个具体的例子 */
 function A(name){
    this.type = 'A类';
    this.name = name || '未命名'
}

var a = new A('a');

function B(name){
  this.subtype = 'B类';
}

// 建立原型链
B.prototype = a;
B.prototype.sayHello = function(){}

var b = new B();

// 遍历属性
for(var key in b){
  //对象自身属性
  if(b.hasOwnProperty(key)){
    console.log('对象自身的属性或方法:'+key) 
  }

  //上述表达式的另一种写法
  if(Object.prototype.hasOwnProperty.call(b,key)){
    console.log('对象自身的属性或方法:'+key)
  }
  else {
    console.log('原型链的属性或方法:'+key)
  }
}

5. 使用===代替==

这个算是比较常见的了,因为js在做比较判断时,会执行强制类型转换,比如false == 0返回true这样的情况,使用===可以执行严格的等价比较,更易于阅读代码(后来阅读的人就不需要判断这个是遗漏还是故意使用强制类型转换简写)

6. 使用parseInt()时,带上第二个参数。

parseInt()用于从字符串中获取数值,第二个参数代表进制,默认是10。我们在使用的时候可能习惯性忽略这个参数,但是在一些情况下会有问题:当字符串的开头为0时,在es3里会被当做是八进制,es5里面仍然当做10进制,为了代码的一致性以及避免不必要的失误,应该每次使用时都带上参数:

var x = parseInt('089',10);//使用时都带上进制参数

7. 大括号的使用

大括号的使用主要是2个方面:

  • 第1,不要省略大括号,即使可以忽略,比如:
for(var i =1;i<10 ;i++)
    console.log(i) //此处原则上可以忽略大括号

上述语句并没有问题,但是如果后期函数体内增加了其他语句的时候,很容易忘记补上大括号,因此建议都带上大括号;

  • 第2,大括号的必须跟在前一个语句的同一行

这个地方为什么加粗了呢?因为这个问题非常容易被忽略,通常我们都觉得大括号是跟在语句的同一行还是下一行只是习惯问题,但是实际上不是的!看下面这个例子:

function func(){
  return 
  {
    name:'xxx'
  }
}
var res = func()
console.log(res)//输出undefined

是不是觉得很奇怪,看代码第一感觉应该是输出一个包含name属性的对象。请注意,由于js的分号插入机制:如果语句没有使用分号结束,会自动补充分号,因此上面的代码实际相当于如下写法:

function func(){
  return undefined;//自动插入分号
  {
    name:'xxx'
  }
}

正确的写法应该是:

function func(){
  return {
    name:'xxx'
  }
}
var res = func()
console.log(res)//输出{name:'xxx'}

----------------------06.15更新------------------------

8 . 判断语句的优化

在业务中经常会遇到类似对请求结果判断后读取的情况。比如:

requset().then(function(res){
    if(res){
        //对返回数据进行操作的代码
    } else{
        
    }
})

这种写法没有问题,但是有时候逻辑比较长,嵌套的大括号比较多。此时可以采用另一种写法

requset().then(function(res){
    // 先判断!res
    if(!res){
        ///
    }
    //对返回数据进行操作的代码
})

这种写法可以减少一次嵌套,在逻辑判断较多时,嵌套层数减少可以增加代码可读性。

----------------------06.20更新------------------------

9 . 对url进行encode

此条主要是在这两天修复一个遗留bug时候发现,一个上传组件在ie下失效,其中一个原因是上传的请求url参数中包含中文参数,但是未手动encodeURI,原先的开发者可能只考虑了chrome下的情况。由于chrome会自动对url进行encode,所以很容易忽略其他浏览器,为了避免此类的兼容问题,建议总是手动做好编码处理。

----------------------08.16更新------------------------

10.空对象判断(要考虑不可枚举属性)

先上代码:

   function X() {

  }
  //原型上的属性
  X.prototype.age = '22'
  var x = new X()
  Object.defineProperty(x, 'name', {
    enumerable: false,
    value: '张三'
  })
  console.log(x)

  //方法1 使用for...in遍历
  function isEmptyObject1(obj) {
    for (var attr in obj) {
      return false
    }
    return true
  }

  //方法2 使用JSON.stringify
  function isEmptyObject2(obj) {
    return (JSON.stringify(obj) === '{}')
  }

  //方法3 使用Object.keys
  function isEmptyObject3(obj) {
    return Object.keys(obj).length === 0
  }

  //方法4 使用getOwnPropertyNames

  function isEmptyObject4(obj) {
    return Object.getOwnPropertyNames(obj).length === 0
  }
  console.log(isEmptyObject1(x)) //true
  console.log(isEmptyObject2(x)) //true
  console.log(isEmptyObject3(x)) //true
  console.log(isEmptyObject4(x)) //false

----------------------2019.01.17更新------------------------

11.封装请求函数时,考虑使用对象代替数组传参

// 1.使用数组方式传参,需要严格按照参数顺序,并且对于多个可选参数的情况不好处理
const getMethod = (id,uid,option1,option2)=>{
    const config = {
        // 参数处理
    } 
    request.get(config)
}
// 如果此时可选参数1不传,却要传入可选参数2,则必须传入空值占位
getMethod('e3ruewruwieru','231423asd',null,'asdasd')

// 2. 使用对象传参时,可以不按照顺序传入参数
const getMethod  = (query) =>{
    const { id, uid, option1,option2} = query //直接解构出需要的参数
    const config = {
        option1:option1 ||'' //即使多个可选参数,处理起来也很方便
    } 
    request.get(config)
}
// 使用比较灵活 并且不用考虑参数顺序
const query = {
    id:'123',
    uid:'asd',
    option2:'goodjob'
}
getMethod(query)

小结

本文会长期更新和补充(如果我没被淹没在业务中的话),欢迎读者提出建议和意见。

然后依然是每次都一样的结尾,如果内容有错误的地方欢迎指出;如果对你有帮助,欢迎点赞和收藏,转载请征得同意后著明出处,如果有问题也欢迎私信交流,主页添加了邮箱地址~溜了


安歌
7k 声望5.5k 粉丝

目前就职于Ringcentral厦门,随缘答题, 佛系写文章,欢迎私信探讨.