几个JavaScript编写技巧

几个JavaScript编写技巧

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

promise

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

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

new Promise((resolve) => { 
  setTimeout(() => { 
    // 做一些事情
    resolve(); 
  }, 1000); 
});

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

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

let processStatus = null;
function myProcess() {
  if (processStatus) {
    return;
  }
  processStatus = new Promise(resolve => {
    // 长任务
    setTimeout(() => {
      resolve();
    }, 5000);
  })
    .then(() => {
      processStatus = null;
    });
}

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

使用 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

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

for (const el of asyncArr) { 
  el.then(console.log); 
}

不要让 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]

尽可能避免使用 else

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

提前return

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

function myFun() {
  if (x > 10) {
    // do something
  } else {
    // do something else
  }
  return someVar;
}

这可以重构为

function myFun() {
  if (x > 10) {
    // do something
    return someVar;
  }
  // do something else
  return someVar;
}

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

任何 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;
}

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

function myFunc() {
  const xValid = checkX(x);
  const yValid = checkY(y);
  return xValid && yValid;
}

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

默认值

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

let x;
if (someVar === "something") {
  x = 1;
} else {
  x = somethingElse;
}

考虑一下这个重构:

let x = 1;
if (someVar !== "something") {
  x = somethingElse;
}

或者是这个:

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

用Array.from处理可迭代对象

让我们看看下面这个代码

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

as具体是什么类型:

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

Array.isArray(as);
// false

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

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

几个JavaScript编写技巧_第1张图片
这意味着它是所谓的“类数组对象”(或可迭代对象)。因此,每当遇到这种对象时,都可以从中创建一个数组。

const asArray = Array.from(as);

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

摆脱引用

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

const a = { key: "value" };
const b = a;
b.key = "something else";
console.log(a.key);
// something else

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

Object.assign

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

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

结果与解构相同。

Array.from

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

比如

const arr1 = [1,2,3,4];
const arr2 = arr1;
arr2[0] = 5;
console.log(arr1); // [5, 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]

当然,解构也适用于数组。另一件需要注意的事情是: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

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

你可能感兴趣的:(javascript,javascript,前端)