# 1. 小细节

不要使用否定条件式(这可能会让人感到疑惑)。同时,使用条件式简写来表示 boolean 值。这个无须再强调了,尤其是否定条件式,这不符合正常的思维方式。

不好的:

const isEmailNotVerified = (email) => {
  // 实现
};

if (!isEmailNotVerified(email)) {
  // 做一些事...
}

if (isVerified === true) {
  // 做一些事...
}
1
2
3
4
5
6
7
8
9
10
11

好的:

const isEmailVerified = (email) => {
  // 实现
};

if (isEmailVerified(email)) {
  // 做一些事...
}

if (isVerified) {
  // 做一些事...
}
1
2
3
4
5
6
7
8
9
10
11

# 2. 对于多个条件,使用 Array.includes

假设我们想要在函数中检查汽车模型是 renault 还是 peugeot。那么代码可能是这样的:

const checkCarModel = (model) => {
  if (model === "renault" || model === "peugeot") {
    console.log("model valid");
  }
};

checkCarModel("renault"); // 输出 'model valid'
1
2
3
4
5
6
7

考虑到我们只有两个模型,这么做似乎也还能接受,但如果我们还想要检查另一个或者是几个模型呢?如果我们增加更多 or 语句,那么代码将变得难以维护,且不够整洁。为了让它更加简洁,我们可以像这样重写函数:

const checkCarModel = (model) => {
  if (["peugeot", "renault"].includes(model)) {
    console.log("model valid");
  }
};

checkCarModel("renault"); // 输出 'model valid'
1
2
3
4
5
6
7

上面的代码看起来已经很漂亮了。为了更进一步改善它,我们可以创建一个变量来存放汽车模型:

const checkCarModel = (model) => {
  const models = ["peugeot", "renault"];

  if (models.includes(model)) {
    console.log("model valid");
  }
};

checkCarModel("renault"); // 输出 'model valid'
1
2
3
4
5
6
7
8
9

现在,如果我们想要检查更多模型,只需要添加一个新的数组元素即可。此外,如果它很重要的话,我们还可以将 models 变量定义在函数作用域外,并在需要的地方重用。这种方式可以让我们集中管理,并使维护变得轻而易举,因为我们只需在代码中更改一个位置。

# 3. 匹配所有条件,使用 Array.every 或者 Array.find

在本例中,我们想要检查每个汽车模型是否都是传入函数的那一个。为了以更加命令式的方式实现,我们会这么做:

const cars = [
  { model: "renault", year: 1956 },
  { model: "peugeot", year: 1968 },
  { model: "ford", year: 1977 },
];

const checkEveryModel = (model) => {
  let isValid = true;

  for (let car of cars) {
    if (!isValid) {
      break;
    }
    isValid = car.model === model;
  }

  return isValid;
};

console.log(checkEveryModel("renault")); // 输出 false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

如果你更喜欢以命令式的风格行事,上面的代码或许还不错。另一方面,如果你不关心其背后发生了什么,那么你可以重写上面的函数并使用 Array.every 或者 Array.find 来达到相同的结果。

const checkEveryModel = (model) => {
  return cars.every((car) => car.model === model);
};

console.log(checkEveryModel("renault")); // 输出 false
1
2
3
4
5

通过使用 Array.find 并做轻微的调整,我们可以达到相同的结果。两者的表现是一致的,因为两个函数都为数组中的每一个元素执行了回调,并且在找到一个 false 项时立即返回 false。

const checkEveryModel = (model) => {
  return cars.find((car) => car.model !== model) === undefined;
};

console.log(checkEveryModel("renault")); // 输出 false
1
2
3
4
5

# 4. 匹配部分条件,使用 Array.some

Array.every 匹配所有条件,这个方法则可以轻松地检查我们的数组是否包含某一个或某几个元素。为此,我们需要提供一个回调并基于条件返回一个布尔值。

我们可以通过编写一个类似的 for…loop 语句来实现相同的结果,就像之前写的一样。但幸运的是,有很酷的 JavaScript 函数可以来帮助我们完成这件事。

const cars = [
  { model: "renault", year: 1956 },
  { model: "peugeot", year: 1968 },
  { model: "ford", year: 1977 },
];

const checkForAnyModel = (model) => {
  return cars.some((car) => car.model === model);
};

console.log(checkForAnyModel("renault")); // 输出 true
1
2
3
4
5
6
7
8
9
10
11

# 5. 提前返回而不是使用 if…else 分支

让函数变成多出口提前返回,替换嵌套条件分支。

function del(obj) {
  var ret;
  if (!obj.isReadOnly) {
    // 不为只读的才能被删除
    if (obj.isFolder) {
      // 如果是文件夹
      ret = deleteFolder(obj);
    } else if (obj.isFile) {
      // 如果是文件
      ret = deleteFile(obj);
    }
  }
  return ret;
}

function del(obj) {
  if (obj.isReadOnly) {
    // 反转if 表达式
    return;
  }
  if (obj.isFolder) {
    return deleteFolder(obj);
  }
  if (obj.isFile) {
    return deleteFile(obj);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 6. 使用索引或者映射,而不是 switch 语句

假设我们想要基于给定的国家获取汽车模型。

const getCarsByState = (state) => {
  switch (state) {
    case "usa":
      return ["Ford", "Dodge"];
    case "france":
      return ["Renault", "Peugeot"];
    case "italy":
      return ["Fiat"];
    default:
      return [];
  }
};

console.log(getCarsByState()); // 输出 []
console.log(getCarsByState("usa")); // 输出 ['Ford', 'Dodge']
console.log(getCarsByState("italy")); // 输出 ['Fiat']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上诉代码可以重构,完全去除 switch 语句。

const cars = new Map()
  .set("usa", ["Ford", "Dodge"])
  .set("france", ["Renault", "Peugeot"])
  .set("italy", ["Fiat"]);

const getCarsByState = (state) => {
  return cars.get(state) || [];
};

console.log(getCarsByState()); // 输出 []
console.log(getCarsByState("usa")); //输出 ['Ford', 'Dodge']
console.log(getCarsByState("italy")); // 输出 ['Fiat']

{
  /*
或者,我们还可以为包含可用汽车列表的每个国家创建一个类,并在需要的时候使用。
不过这个就是题外话了,本文的主题是关于条件句的。更恰当的修改是使用对象字面量。
*/
}

const carState = {
  usa: ["Ford", "Dodge"],
  france: ["Renault", "Peugeot"],
  italy: ["Fiat"],
};

const getCarsByState = (state) => {
  return carState[state] || [];
};

console.log(getCarsByState()); // 输出 []
console.log(getCarsByState("usa")); // 输出 ['Ford', 'Dodge']
console.log(getCarsByState("france")); // 输出 ['Renault', 'Peugeot']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 7. 使用自判断链接和空合并

让我们用一些例子来支撑上面的结论。一开始,我们还是用以前的老方法:

const car = {
  model: "Fiesta",
  manufacturer: {
    name: "Ford",
    address: {
      street: "Some Street Name",
      number: "5555",
      state: "USA",
    },
  },
};

// 获取汽车模型
const model = (car && car.model) || "default model";
// 获取厂商地址
const street =
  (car &&
    car.manufacturer &&
    car.manufacturer.address &&
    car.manufacturer.address.street) ||
  "default street";
// 请求一个不存在的属性
const phoneNumber =
  car &&
  car.manufacturer &&
  car.manufacturer.address &&
  car.manufacturer.phoneNumber;

console.log(model); // 输出 'Fiesta'
console.log(street); // 输出 'Some Street Name'
console.log(phoneNumber); // 输出 undefined

// 因此,如果我们想要知道厂商是否来自 USA 并将结果打印,那么代码是这样的:

const checkCarManufacturerState = () => {
  if (
    car &&
    car.manufacturer &&
    car.manufacturer.address &&
    car.manufacturer.address.state === "USA"
  ) {
    console.log("Is from USA");
  }
};

checkCarManufacturerState(); // 输出 'Is from USA'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

我们来看一下新的方法:

// 获取汽车模型
const model = car?.model ?? "default model";
// 获取厂商地址
const street = car?.manufacturer?.address?.street ?? "default street";

// 检查汽车厂商是否来自 USA
const checkCarManufacturerState = () => {
  if (car?.manufacturer?.address?.state === "USA") {
    console.log("Is from USA");
  }
};
1
2
3
4
5
6
7
8
9
10
11

使用 ?? 而不是 || 。

自判断链接同样支持 DOM API,意味着你可以这么做:

const value = document.querySelector("input#user-name")?.value;
1

# 8.合并重复的条件片段

如果一个函数体内有一些条件分支语句,而这些条件分支语句内部散布了一些重复的代码,那么就有必要进行合并去重工作。

// 合并前
function main(currPage) {
  if (currPage <= 0) {
    currPage = 0;
    jump(currPage); // 跳转
  } else if (currPage >= totalPage) {
    currPage = totalPage;
    jump(currPage); // 跳转
  } else {
    jump(currPage); // 跳转
  }
}

// 合并后
function main(currPage) {
  if (currPage <= 0) {
    currPage = 0;
  } else if (currPage >= totalPage) {
    currPage = totalPage;
  }
  jump(currPage); // 把jump 函数独立出来
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 9. 把条件分支语句提炼成函数

// 根据不同季节决定打折力度
function getPrice(price) {
  var date = new Date();
  if (date.getMonth() >= 6 && date.getMonth() <= 9) {
    // 夏天
    return price * 0.8;
  }
  return price;
}

// 是否是夏天
function isSummer() {
  var date = new Date();
  return date.getMonth() >= 6 && date.getMonth() <= 9;
}
// 提炼条件后
function getPrice(price) {
  if (isSummer()) {
    return price * 0.8;
  }
  return price;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 10.合理使用循环

// 判断是什么浏览器
function getBrowser(){
    const str = navigator.userAgent;
    if (str.includes('QQBrowser')) {
 return 'qq';
    } else if (str.includes('Chrome')) {
 return 'chrome';
    } else if (str.includes('Safari')) {
        return 'safri';
    } else if (str.includes('Firefox')) {
        return 'firefox';
    } else if(explorer.indexOf('Opera') >= 0){
        return 'opera';
    } else if (str.includes('msie')) {
        return 'ie';
    } else {
        return 'other';
    }
};


// 循环判断,将对应关系抽象为配置,更加清晰明确
function getBrowser(){
    const str = navigator.userAgent;
    const list = [
        {key: 'QQBrowser', browser: 'qq'},
        {key: 'Chrome', browser: 'chrome'},
        {key: 'Safari', browser: 'safari'},
        {key: 'Firefox', browser: 'firefox'},
        {key: 'Opera', browser: 'opera'},
        {key: 'msie', browser: 'ie'},
    ];
    for (let i = 0; i < list.length; i++) {
        const item = list[i];
        if (str.includes(item.key)) {return item.browser};
    }
    return 'other';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37