• 几个JavaScript编写技巧


    几个JavaScript编写技巧

    太多关于 JavaScript 技巧的文章只涵盖了数组函数的基础知识或对代码的明显改进。本文将更加深入,帮助改进每天编写的代码。

    promise

    有时,我们想等待某件事发生。虽然这个任务可能会变得复杂(例如,使用非阻塞循环),但对于大多数等待问题有一个简单的解决方案:Promise

    可以在给定的超时后解决:

    new Promise((resolve) => { 
      setTimeout(() => { 
        // 做一些事情
        resolve(); 
      }, 1000); 
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这个promise将在大约 1 秒后解决。还可以将其存储在变量中并使用await 阻塞一秒钟(注意潜在的用户体验问题)。虽然我们早在日常开发中很容易找到上面代码片段的用例,但它意味着一个更有用的技巧。

    可以使用 Promise 作为信号量:有时,我们想要执行异步、长时间运行的流程。但用户可以一次又一次地触发此过程。因此,我们需要确保正在运行的进程必须先完成,然后用户才能再次启动它。就是这样:

    let processStatus = null;
    function myProcess() {
      if (processStatus) {
        return;
      }
      processStatus = new Promise(resolve => {
        // 长任务
        setTimeout(() => {
          resolve();
        }, 5000);
      })
        .then(() => {
          processStatus = null;
        });
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    用户只能在没有活动进程时单击此按钮。这有助于避免多次获取相同的数据。

    使用 async 优化 JavaScript 中的循环

    我们可能经常通过forEach方法来处理数组,但是它还有一个特别强大的能力:异步循环。

    const asyncArr = [ 
      new Promise(resolve => setTimeout(resolve.bind(this, 1), 2000)), 
      new Promise(resolve => setTimeout(resolve.bind(this, 2), 500)), 
      new Promise(resolve => setTimeout(resolve.bind(this, 3), 5000)), 
      new Promise(resolve => setTimeout(resolve.bind(this, 4), 1000)), 
    ];
    asyncArr.forEach(async (el) => { 
      const i = await el; 
      console.log(i); 
    });
    // 2,4,1,3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    虽然使用 for-of 循​​环也可以做到这一点,但使用 await 读起来更加优雅。这是使用 for-of 循​​环的相同示例。

    for (const el of asyncArr) { 
      el.then(console.log); 
    }
    
    • 1
    • 2
    • 3

    不要让 for-of 循​​环更简洁这一事实欺骗了您。在此示例中,我们不使用 进行任何计算i。then然而,想象一下在for-const 循环使用的函数体中进行更多计算。

    虽然示例中的for...of看起来更简洁,是因为没有对i进行更复杂的运算,但是我们想象一下如果在then方法里做更多的运算是不是更复杂一点。

    使用forEach可能会有两个问题:

    首先,它没有返回值。这意味着我们可能要更改原始数组,或者根本不更改。如果决定修改数组,则会产生副作用,如果我们根本不更改它,这些副作用可能很难调试。

    第二,不稳定。如果不同步循环所有元素,我们无法按照原始数组的顺序记录结果。

    为了避免这种情况,我们可以Promise.allmap, 这将产生一个新数组,其中异步调用接收到的值的顺序与原始数组相同。

    const asyncArr = [
      new Promise(resolve => setTimeout(resolve.bind(this, 1), 2000)),
      new Promise(resolve => setTimeout(resolve.bind(this, 2), 500)),
      new Promise(resolve => setTimeout(resolve.bind(this, 3), 5000)),
      new Promise(resolve => setTimeout(resolve.bind(this, 4), 1000)),
    ]
     
    Promise.all(asyncArr)
     .then(console.log);
    // [1,2,3,4]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    尽可能避免使用 else

    这个技巧简单但功能强大。在很多情况下,物品们会发现自己编写了 else 块,而只需 2 秒钟的思考就可以避免这种情况。

    提前return

    利用 return 语句可以帮助我们消除第一组不必要的 else 块。请参阅下面的示例:

    function myFun() {
      if (x > 10) {
        // do something
      } else {
        // do something else
      }
      return someVar;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这可以重构为

    function myFun() {
      if (x > 10) {
        // do something
        return someVar;
      }
      // do something else
      return someVar;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    大家可能会反对:这些例子的大小相差不大——何必呢?这与大小无关,而是与可读性和降低复杂性有关。

    任何 ifelse 语句都会增加函数的复杂性。当遇到else块时,它有多长?if 块有多长?

    这里有一条经验法则:使用 if 语句处理错误并尽快返回。然后,该函数应该在任何 if/else 之外执行它应该执行的操作。

    违反单一职责原则(SRP)

    大家可能会有疑问:我什么时候知道我违反了单一责任原则?我什么时候意识到我的函数不止做一件事?if-else 可以作为一个指标!

    function myFun() {
      if (x > 10) {
        // do something
      } else {
        // do something else
      }
    
      if (y < 100) {
        // do something
      } else {
        // do something else
      }
      return someVar;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    特别是当一个函数中有多个 if-else 块时,很可能违反了该函数的 SRP。上面的例子可以重构为

    function myFunc() {
      const xValid = checkX(x);
      const yValid = checkY(y);
      return xValid && yValid;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当然,重构很大程度上取决于代码的语义。然而,这是将上面的示例重写为更清晰、更易读的函数的一种可能方法。

    默认值

    大家觉得这很熟悉吗?可能有时候我们都会这样写:

    let x;
    if (someVar === "something") {
      x = 1;
    } else {
      x = somethingElse;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    考虑一下这个重构:

    let x = 1;
    if (someVar !== "something") {
      x = somethingElse;
    }
    
    • 1
    • 2
    • 3
    • 4

    或者是这个:

    const x = someVar !== "something"
      ? somethingElse 
      : 1;
    
    • 1
    • 2
    • 3

    用Array.from处理可迭代对象

    让我们看看下面这个代码

    const as = document.querySelectorAll("a");
    
    • 1

    as具体是什么类型:

    首先,我们可能认为它是一个数组,但事实并非如此:

    Array.isArray(as);
    // false
    
    • 1
    • 2

    因此,不能使用as.map(...)

    其次,Chrome 将其显示为数组,这可能会让很多人感到困惑。但是,请注意“NodeList

    在这里插入图片描述
    这意味着它是所谓的“类数组对象”(或可迭代对象)。因此,每当遇到这种对象时,都可以从中创建一个数组。

    const asArray = Array.from(as);
    
    • 1

    这确实是一个数组。现在,我们可以在asArray上使用mapfilter或任何其他数组函数。

    摆脱引用

    引用可能会在代码中引起各种副作用。意识到何时处理引用以及何时仅处理值是编写无错误软件的关键。在使用对象和数组时,我们可能会遇到这些问题:

    const a = { key: "value" };
    const b = a;
    b.key = "something else";
    console.log(a.key);
    // something else
    
    • 1
    • 2
    • 3
    • 4
    • 5

    b只是对a的引用,因此每当b更改引用的对象键时,它也会反映在a上。我们可以是使用下面的方法来创建一个新对象b,而无需使用a

    解构

    这个已经流行了一段时间了。解构会删除所有引用。

    const a = { key: "value" };
    const b = {...a};
    b.key = "something else";
    console.log(a.key); // value
    console.log(b.key); // something else
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Object.assign

    解构是 Object.assign 的语法糖,因此也可以使用此技术删除引用:

    const b = Object.assign({}, a);
    
    • 1

    结果与解构相同。

    Array.from

    如果我们正在处理数组,那么可以使用Array.from来摆脱引用:

    比如

    const arr1 = [1,2,3,4];
    const arr2 = arr1;
    arr2[0] = 5;
    console.log(arr1); // [5, 2, 3, 4]
    
    • 1
    • 2
    • 3
    • 4

    可以用Array.from解决:

    const arr1 = [1,2,3,4];
    const arr2 = Array.from(arr1);
    arr2[0] = 5;
    console.log(arr1); // [1, 2, 3, 4]
    console.log(arr2); // [5, 2, 3, 4]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    当然,解构也适用于数组。另一件需要注意的事情是:Array.from不仅适用于“类数组对象”,而且也适用于数组。

    最后的手段:JSON.stringify

    作为最后的手段,我们可以将对象字符串化并再次解析它。所有引用都将被清除。

    const a = { key: "value" };
    const b = JSON.parse(JSON.stringify(a));
    b.key = "something else";
    console.log(a.key); // value
    console.log(b.key); // something else
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是,请注意 JSON.stringify 还会清除所有类型信息。这可能会给您带来日期和其他对象的一些麻烦。

  • 相关阅读:
    BPA仿真软件需要购买吗,BPA电力仿真软件教程
    【机器学习】LSTM 讲解
    webserver项目
    苏宁API:一键搜索,海量商品任你选!
    如何做好线上服务质量保障
    SpringBoot高频面试题
    D-Link确认数据泄露:员工成为钓鱼攻击受害者
    如何构建线性回归模型 - 机器学习示例
    详解预处理(2)
    第一行代码Android 第九章9.4-9.5(解析JSON格式,网络编程最佳实践:发送HTTP请求的代码)
  • 原文地址:https://blog.csdn.net/qq_42880714/article/details/133759039