美文网首页让前端飞
【JS/TS】一些容易导致错误的用法(一)

【JS/TS】一些容易导致错误的用法(一)

作者: 来一斤BUG | 来源:发表于2024-01-14 14:14 被阅读0次

    1. 不要使用undefined判断Map中是否存在某个键

    js中map取值时如果取不到或者本身的值为undefined,都会返回undefined,所以通过map.get(key) === undefined的方式判断是否存在某个键容易埋下隐患。正确的方式是使用map.has(key)

    const map = new Map();
    map.get("a") === undefined; // true
    map.has("a"); // false
    

    同理,判断对象中是否存在某个键时不应该使用obj[key] === undefined,应该使用obj.hasOwnProperty(key)

    const obj = {};
    obj["a"] === undefined; // true
    obj.hasOwnProperty("a"); // false
    

    2. 尽量不要使用==!=

    ==!=会进行类型转换,容易导致一些奇怪的问题,比如:

    [] == ![]; // true
    
    const a = {};
    const b = {};
    a == !b; // false
    // 空数组会被转换成原始值(空字符串),![]为false,"" == false -> "" == 0 -> 0 == 0
    // 但是空对象不会被转换成空字符串,所以a == !b为false
    

    所以平常尽量使用===!==,建议只有在nullundefined的判断时使用==!=

    3. 不要使用for...in遍历数组

    for...in会遍历对象的所有可枚举属性,包括原型链上的属性,所以不要使用for...in遍历数组,而是使用for...of或者forEach

    Object.defineProperty(Array.prototype, "foo", {
        value: "bar",
        enumerable: true, // 设置为了可枚举
    });
    const arr = [1, 2, 3];
    for (const i in arr) {
        console.log(i); // 0, 1, 2, "foo"
        console.log(arr[i]); // 1, 2, 3, "bar"
    }
    for (const ele of arr) {
        console.log(ele); // 1, 2, 3
    }
    // Array的原型链上有一个foo属性,所以使用for...in遍历数组时会遍历到foo属性
    

    4. 使用undefined有风险

    undefined不是一个保留字,在局部作用域下可以被赋值,比如:

    function foo() {
        const undefined = 1;
        console.log(undefined); // 1
    }
    foo();
    

    所以在局部作用域下使用undefined有风险,可以使用void 0代替undefined

    function foo() {
        const undefined = 1;
        console.log(void 0); // undefined
    }
    foo();
    

    5. 尽量不要使用var

    var有很多问题,比如:

    1. 可以重复声明
      var a = 1;
      if (true) {
          var a = 2;
      }
      console.log(a); // 2
      
    2. 会变量提升
      console.log(a); // undefined
      var a = 1;
      
    3. 作用域问题
      for (var i = 0; i < 3; i++) {
          setTimeout(() => {
              console.log(i); // 3, 3, 3
          }, 100);
      }
      
    4. 全局变量自动添加到window上,污染全局
      var a = 1;
      console.log(window.a); // 1
      

    所以尽量使用letconst

    6. 不要使用parseInt()将number类型的浮点数转换成整数

    parseInt()的参数为字符串,如果传入了其他类型,会被先转换成字符串,当一个浮点数的位数比较多时,转换成字符串是会变成科学计数法,比如:

    0.0000001.toString(); // "1e-7"
    parseInt(0.0000001); // 1
    0.0000000000000005.toString(); // "5e-16"
    parseInt(0.0000000000000005); // 5
    

    正确的方式是使用Math.floor()Math.round()或者Math.ceil()

    Math.floor(0.0000001); // 0
    Math.floor(0.0000000000000005); // 0
    

    7. 注意第三方大数库的数据类型转换

    某些第三方大数库可能会提供大数转换成number类型的方法,但是如果数字过大,转换成number类型时会丢失精度,比如:

    // 随便用一个库举例子
    const a = new BigNumber("9007199254740993");
    a.toNumber(); // 9007199254740992
    

    由于9007199254740993超过了Number.MAX_SAFE_INTEGER(9007199254740992),所以转换成number类型时会丢失精度,所以如果想将这个数显示出来,应该使用toString()。(这个问题我已经在工作中遇到过了)

    8. 分清楚||??

    ||??都可以用来做空值合并,但是有一些区别。||实际意思是“或”,当其左边的值为false时,会返回右边的值,所以0""NaNnullundefined都会被当成false??的意思是“空值合并”,只有当其左边的值为null或者undefined时,才会返回右边的值。

    const a = 0;
    const b = a || 1;
    console.log(b); // 1
    
    const c = 0;
    const d = c ?? 1;
    console.log(d); // 0
    
    const e = null;
    const f = e || 1;
    console.log(f); // 1
    

    9. ??==(===)的优先级问题

    ??的优先级比==(===)底,所以a ?? b == c等价于a ?? (b == c),而不是(a ?? b) == c。如果要将??用在相等判断中,大概率需要加括号,比如a == (b ?? c)

    const a = {c: 0};
    console.log((a.b ?? 1) === 1); // true
    console.log(a.c ?? 1 === 1); // 0
    // 由于??的优先级比===底,所以先计算1 === 1,结果为true
    // 然后由于a.c存在且为0,所以1 === 1的值被忽略,最终结果为0
    

    我平时开发经常使用a?.b ?? c处理空值,经常直接和===连用,有时候会忘记加括号,导致出现一些奇怪的问题,所以这个问题需要注意一下。

    10. ts中枚举类型的遍历问题

    ts中枚举类型的值可以是字符串,也可以是数字,但是如果枚举类型的值是数字,那么枚举类型的值会在编译时被添加到枚举类型的属性上(如果值为字符串则没有这个问题),比如:

    enum Foo {
        a = 1,
        b = 2,
    }
    console.log(Foo); // {"1": "a", "2": "b", a: 1, b: 2}
    

    所以如果要遍历枚举类型,应该做一些额外的判断,比如:

    enum Foo {
        a = 1,
        b = 2,
    }
    for (const key in Foo) {
        if (typeof Foo[key] === "number") {
            console.log(key); // a, b
        }
    }
    for (const key in Foo) {
        if (isNaN(Number(key))) {
            console.log(key); // a, b
        }
    }
    

    相关文章

      网友评论

        本文标题:【JS/TS】一些容易导致错误的用法(一)

        本文链接:https://www.haomeiwen.com/subject/iwoqodtx.html