优化 if else-if else 语句的使用

目录

1. 单个 if 语句优化策略

2. 单个 if else 语句优化策略

2.1 提前 return

2.2 使用条件三目运算符

2.2.1 if else 分支下简短返回值

2.2.2 if else 分支下简短赋值

2.2.3 if else 分支下调用单个函数

3. 多个 if else 嵌套优化策略

3.1 当发现无效条件时,提前返回

3.2 try catch 适合严谨又图省事的前端

3.3 可选链 optional chaining

4. 多个 else if 分支优化策略

4.1 仅需判断一个条件

4.1.1 借助对象的 { key, value } 结构 进行优化

4.1.2 借助 ES6 Map 数据结构 进行优化

4.2 需判断多个条件

4.2.1 使用 Map 结构优化多元判断(将多个查询条件,拼接成字符串)

4.2.2 使用 Map 结构优化多元判断(将多个查询条件,拼接成对象) 

4.2.3 使用 Map 结构优化多元判断(将多个处理逻辑,抽成公共方法) 

4.2.4 使用 Map 结构优化多元判断(将多个查询条件,抽成正则表达式)  

4.2.5 总结不同的条件判断时,采用的优化方式

5. 使用数组新特性优化逻辑判断

5.1 使用 includes 优化代码逻辑

5.2 使用 every 优化代码逻辑

5.3 使用 some 优化代码逻辑

6. 默认值优化

7. 使用策略模式优化分支逻辑

8. 总结与思考


1. 单个 if 语句优化策略

需求:当条件为真时,打印出日志内容

优化前

let flag = true;
if (flag) {
  log();
}

function log() {
  console.log("如果 flag 值为真,打印这段文字");
}

优化后

let flag = true;
flag && log();

function log() {
  console.log("如果 flag 值为真,打印这段文字");
}

2. 单个 if else 语句优化策略

2.1 提前 return

需求:执行登录操作,如果用户名和密码输入框为空,则提示用户”用户名和密码不能为空”;如果都不为空,则执行登录的操作

优化前

let user = "lyrelion";
let password = "solution";

if (user && password) {
  // 执行登录操作
} else {
  return "用户名和密码不能为空";
}

优化后

排非策略,先排除为 false 的情形,在执行其他操作(干掉 else 分支)

let user = "lyrelion";
let password = "solution";

if (!user || !password) {
  return "用户名和密码不能为空";
}
// 执行登录操作

2.2 使用条件三目运算符

使用场景:在不影响可读性的情况下,处理if else分支下简短的返回值、单个简短赋值语句、调用单个相应函数时,建议使用三目运算符

2.2.1 if else 分支下简短返回值

优化前

function demo(flag) {
  if (flag) {
    return "true";
  } else {
    return "false";
  }
}

优化后

function demo(falg) {
  return flag ? "true" : "false";
}

2.2.2 if else 分支下简短赋值

优化前

function demo(flag) {
  let val = "";
  if (flag) {
    val = "true";
  } else {
    val = "false";
  }
}

优化后

function demo(flag) {
  let val = flag ? "true" : "false";
}

2.2.3 if else 分支下调用单个函数

优化前

function demo(flag) {
  if (flag) {
    success();
  } else {
    fail();
  }
}

优化后

function demo(flag) {
  flag ? success() : fail();
}

3. 多个 if else 嵌套优化策略

需求:后端大哥说,给你返回的数据里面如果有  userInfo 字段,并且 userInfo下面有hobby 字段并且有值,就显示  hobby 里面的内容,否则页面  hobby 不显示

let result = {
  status: 200,
  msg: "success",
  data: {
    userInfo: {
      name: "doudou",
      hobby: ["吃饭", "睡觉", "打豆豆"],
    },
  },
};

优化前

if (result.data) {
  if (result.data.userInfo) {
    if (Array.isArray(result.data.userInfo.hobby)) {
      if (result.data.userInfo.hobby.length) {
        // 进行业务逻辑操作
      } else {
        return "hobby字段为空";
      }
    } else {
      return "hobby字段不是一个数组";
    }
  } else {
    return "userInfo字段不存在";
  }
} else {
  return "data字段不存在";
}

if else 一般不建议嵌套超过三层,如果一段代码存在过多的 if else 嵌套,代码的可读性就会急速下降,后期维护难度也大大提高,应该尽量避免过多的 if else 嵌套

3.1 当发现无效条件时,提前返回

if (!result.data) return "data字段不存在";
if (!result.data.userInfo) return "userInfo字段不存在";
if (!Array.isArray(result.data.userInfo.boddy)) return "hobby字段不是一个数组";
if (result.data.userInfo.hobby.length) {
  // 进行业务逻辑操作
}

3.2 try catch 适合严谨又图省事的前端

try {
  // 有可能出现错误的代码写在这里
  if (result.data.userInfo.hobby.length) {
    // 进行业务逻辑操作
  }
} catch (error) {
  // 出错后的处理写在这里
}

如果 try 中的代码没有出错,则程序正常运行 try 中的内容后,不会执行 catch 中的内容

如果 try 中的代码出错,程序立即跳入 catch 中继续执行,try 中出错后的程序就不再执行了

3.3 可选链 optional chaining

对一个空值进行属性读取时,程序会抛出异常;在多级嵌套的对象中取属性值的时候,更容易出现这个问题

为了保证程序的健壮性,就需要确保对象不为空时,再读取下一级的值

// 可选链优化
if (result?.data?.userInfo?.hobby?.length) {
  // 进行业务逻辑操作
}

操作符 *?.* 会检查操作符左边的值是否是空值。如果是 null 或 undefined,这个表达式就终止然后返回 undefined。否则,这个表达式继续执行检查

4. 多个 else if 分支优化策略

4.1 仅需判断一个条件

需求:有多个按钮,点击按钮后,执行不同的业务逻辑

优化前 

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 */

const onButtonClick = (type) => {
  if (type === "1") {
    showLog("女装");
    jumpTo("womenPage");
  } else if (type === "2") {
    showLog("男装");
    jumpTo("menPage");
  } else if (type === "3") {
    showLog("童装");
    jumpTo("childPage");
  } else if (type === "4") {
    showLog("美妆");
    jumpTo("makeupPage");
  } else if (type === "5") {
    showLog("箱包");
    jumpTo("bagPage");
  } else {
    showLog("推荐好物");
    jumpTo("recommendPage");
  }
};

switch 优化(没从根本解决问题,看着还是很多):

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 */
const onButtonClick = (type) => {
  switch (type) {
    case "1":
      showLog("女装");
      jumpTo("womenPage");
      break;
    case "2":
      showLog("男装");
      jumpTo("menPage");
      break;
    case "3":
      showLog("童装");
      jumpTo("childPage");
      break;
    case "4":
      showLog("美妆");
      jumpTo("makeupPage");
      break;
    case "5":
      showLog("箱包");
      jumpTo("bagPage");
      break;
    default:
      showLog("推荐好物");
      jumpTo("recommendPage");
  }
};

注意:

不要忘记在每个 case 语句后放一个 break,case 语句只是指明了想要执行代码的起点,并没有指明终点;

如果没有在 case 语句中添加 break 语句,没有明确的中断流程,在每次条件判断后,都会执行下次判断条件,可能会造成逻辑混乱

4.1.1 借助对象的 { key, value } 结构 进行优化

借助对象结构,把判断条件作为对象的属性名,把处理逻辑要传入的参数作为对象的属性值;

在执行按钮点击事件时,通过查询对象中的键,获取到键对应的值,然后执行对应的处理逻辑

const actions = {
  1: ["女装", "womenPage"],
  2: ["男装", "menPage"],
  3: ["童装", "childPage"],
  4: ["美妆", "makeupPage"],
  5: ["箱包", "bagPage"],
  default: ["推荐好物", "recommendPage"],
};

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 */
function onButtonClick(type) {
  let action = actions[type] || actions["default"];
  // 打印日志
  showLog(action[0]);
  // 跳转页面
  jumpTo(action[1]);
}

4.1.2 借助 ES6 Map 数据结构 进行优化

Map 对象保存键值对,任何类型值(对象或者原始值) 都可以作为一个键或一个值

const actions = new Map([
  ["1", ["女装", "womenPage"]],
  ["2", ["男装", "menPage"]],
  ["3", ["童装", "childPage"]],
  ["4", ["美妆", "makeupPage"]],
  ["5", ["箱包", "bagPage"]],
  ["default", ["推荐好物", "recommendPage"]],
]);

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 */
function onButtonClick(type) {
  let action = actions.get(type) || actions.get("default");
  showLog(action[0]);
  jumpTo(action[1]);
}

ES6 Map 对象 VS Object 对象:

  1. 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值
  2. Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是
  3. Map  的键值对个数可以从 size 属性获取,而  Object  的键值对个数只能手动计算

4.2 需判断多个条件

需求:在点击按钮时不仅要判断 type,还要判断用户的身份 —— 男用户 or 女用户

优化前

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 * @param { string } sex // 男用户or女用户
 */
function onButtonClick(type, sex) {
  if (sex === "women") {
    if (type === "1") {
      // do something
    } else if (type === "2") {
      // do something
    } else if (type === "3") {
      // do something
    } else if (type === "4") {
      // do something
    } else if (type === "5") {
      // do something
    } else {
      // do something
    }
  } else if (sex === "men") {
    if (type === "1") {
      // do something
    } else if (type === "2") {
      // do something
    } else if (type === "3") {
      // do something
    } else if (type === "4") {
      // do something
    } else if (type === "5") {
      // do something
    } else {
      // do something
    }
  }
}

从上方示例代码中可以看出,如果判断条件变为二元条件判断时,if else 的数量就增加到一元判断条件的二倍,代码看着更臃肿了

那么对于二元的条件判断,我们应该怎么去优化它们呢?

4.2.1 使用 Map 结构优化多元判断(将多个查询条件,拼接成字符串)

const actions = new Map([
  [
    "women_1",
    () => {
      /* do something */
    },
  ],
  [
    "women_2",
    () => {
      /* do something */
    },
  ],
  [
    "women_3",
    () => {
      /* do something */
    },
  ],
  [
    "women_4",
    () => {
      /* do something */
    },
  ],
  [
    "women_5",
    () => {
      /* do something */
    },
  ],
  [
    "men_1",
    () => {
      /* do something */
    },
  ],
  [
    "men_2",
    () => {
      /* do something */
    },
  ],
  [
    "men_3",
    () => {
      /* do something */
    },
  ],
  [
    "men_4",
    () => {
      /* do something */
    },
  ],
  [
    "men_5",
    () => {
      /* do something */
    },
  ],
  [
    "default",
    () => {
      /* do something */
    },
  ],
]);

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 * @param { string } sex // 男用户 or 女用户
 */
function onButtonClick(type, sex) {
  let action = actions.get(`$(sex)_$(type)`) || actions.get("default");
  action.call(this);
}

上面这种处理思想是,把条件判断拼接成字符串作为键,每个分支下的处理逻辑作为值,在使用时传入参数使用 Map 查询,这种方法非常适合于二元或多元条件判断

4.2.2 使用 Map 结构优化多元判断(将多个查询条件,拼接成对象) 

如果你不喜欢把查询条件拼接为字符串使用,这还有一种方法,把查询条件作为对象,借助 Map 数据结构实现

const actions = new Map([
  [
    { sex: "women", type: "1" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "women", type: "2" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "women", type: "3" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "women", type: "4" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "women", type: "5" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "men", type: "1" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "men", type: "2" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "men", type: "3" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "men", type: "4" },
    () => {
      /* do something */
    },
  ],
  [
    { sex: "men", type: "5" },
    () => {
      /* do something */
    },
  ],
  [
    "default",
    () => {
      /* do something */
    },
  ],
]);

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 * @param { string } sex // 男用户or女用户
 */
function onButtonClick(type, sex) {
  // 根据条件使用filter查询
  let action = [...actions].filter(
    ([key, value]) => key.sex === sex && key.type === type
  );
  action.forEach(([key, value]) => value.call(this));
}

/**
 * 按钮点击事件
 * @param { string } type // 1女装2男装3童装4美妆5箱包
 * @param { string } sex // 男用户or女用户
 */
function onButtonClick(type, sex) {
  // 根据条件使用find查询
  let action = [...actions].find(
    ([key, value]) => key.sex === sex && key.type === type
  );
  action[1].call(this);
}

上方根据条件查询相应执行逻辑时,提供了 filter 和 find 两种查询方式

个人觉得使用 filter 更为正式,使用 find 更容易阅读

从这里我们就看出了使用 Map 相对于 Object 存在的优势了 → Map 数据结构可以以任意类型的值作为 key

4.2.3 使用 Map 结构优化多元判断(将多个处理逻辑,抽成公共方法) 

假如在 women 情况下,type 为 1,2,3,4 时的处理逻辑都一样时,该怎么写呢?

const logicA = () => {
  /* 执行A逻辑 */
};

const logicB = () => {
  /* 执行B逻辑 */
};

const actions = new Map([
  [{ sex: "women", type: "1" }, logicA],
  [{ sex: "women", type: "2" }, logicA],
  [{ sex: "women", type: "3" }, logicA],
  [{ sex: "women", type: "4" }, logicA],
  [{ sex: "women", type: "5" }, logicB],
  // ……
]);

4.2.4 使用 Map 结构优化多元判断(将多个查询条件,抽成正则表达式)  

上面的写法虽然 Map 中结构清晰了,日常需求的话可以这么写也是没什么问题的

但是,如果以后增加需求,women 条件下 type 为 6、7、8、10、11……的逻辑处理都是一样的,难道还要用上方那样的写法吗?

function actions() {
  const logicA = () => {
    /* 执行A逻辑 */
  };

  const logicB = () => {
    /* 执行B逻辑 */
  };

  const action = new Map([
    [/^women_[1-4]$/, logicA],
    [/^women_5$/, logicB],
    // ……
  ]);
}

利用正则进行判断条件匹配后,代码又清爽了许多。并且这里使用 Map 后的优势就更加的明显了,符合正则的条件的公共逻辑都会执行

4.2.5 总结不同的条件判断时,采用的优化方式

一元条件判断:存到 Object 中。

一元条件判断:存到 Map 中。

二元或多元判断:将判断条件拼接成字符串存到 Object 中

二元或多元判断:将判断条件拼接成字符串存到 Map 中

多元判断时:将判断条件写成 Object 存到 Map 中

多元判断时:将判断条件写成正则存到 Map 中

5. 使用数组新特性优化逻辑判断

在工作中,巧妙的使用 ES6 中提供的数组新特性,也可以达到轻松优化逻辑判断的效果

5.1 使用 includes 优化代码逻辑

需求:判断 animal 是否属于 cute 类型

优化前

const cuteAnimal = ["dog", "cat", "bird", "panda"];

function animalJudge(animal) {
  if (
    animal === "dog" ||
    animal === "cat" ||
    animal === "bird" ||
    animal === "panda"
  ) {
    console.log("可爱的小动物");
  }
}

优化后 

const cuteAnimal = ["dog", "cat", "bird", "panda"];

function animalJudge(animal) {
  if (cuteAnimal.includes(animal)) {
    console.log("可爱的小动物");
  }
}

5.2 使用 every 优化代码逻辑

需求:判断 animals 数组中的动物是否都属于 cute 类型

every():判断数组的每一项是否都满足条件,都满足条件返回 true,否则返回 false

优化前 

const animals = [
  {
    name: "dog",
    type: "cute",
  },
  {
    name: "cat",
    type: "cute",
  },
  {
    name: "elephant",
    type: "tall",
  },
];

function type() {
  let isAllCute = true;

  // 判断条件:animals中的动物是否都是cute类型
  for (let animal of animals) {
    if (!isAllCute) break;
    isAllRed = animal.type === "cute";
  }

  console.log(isAllCute); // false
}

优化后

const animals = [
  {
    name: "dog",
    type: "cute",
  },
  {
    name: "cat",
    type: "cute",
  },
  {
    name: "elephant",
    type: "tall",
  },
];

function animals() {
  // 判断条件:animals中的动物是否都是cute类型
  const isAllCute = animals.every((animal) => animal.type === "cute");

  console.log(isAllCute); // false
}

5.3 使用 some 优化代码逻辑

需求:判断 animals 中的动物是否存在有 tall 类型的

some():对数组中每一项运行特定函数,如果有一项符合条件,则返回 true,都不符合条件返回 false

const animals = [
  {
    name: "dog",
    type: "cute",
  },
  {
    name: "cat",
    type: "cute",
  },
  {
    name: "elephant",
    type: "tall",
  },
];

function animals() {
  // 判断条件:animals中的动物是否含有tall类型
  const isHasTall = animals.some((animal) => animal.type === "tall");

  console.log(isHasTall); // true
}

6. 默认值优化

优化前

function request(options) {
  let method = options.method ? options.method : "GET";
  let data = options.data ? options.data : {};
  //...
}

优化后

function request(options) {
  let method = options.method || "GET";
  let data = options.data || {};
  //...
}

基于 ES6 优化后

// 解析解构和默认参数搭配使用
function request({ method, data } = { method: "GET", data: {} }) {
  //...
  console.log(method); // GET
  console.log(data); // {}
}

7. 使用策略模式优化分支逻辑

需求:咱马上要过国庆节啦,得打折清仓呀,有的商品 5 折,有的 7 折,有的 9 折~

优化前

function percent50(price) {
  // 五折的算法
}

function percent70(price) {
  // 七折的算法
}

function percent90(price) {
  // 九折的算法
}

function calculatePrice(price) {
  if (五折的商品) {
    return percent50(price);
  }

  if (七折的商品) {
    return percent50(price);
  }

  if (九折的商品) {
    return percent50(price);
  }
}

calculatePrice(price);

写到这里需求又来了,那以后的中秋节、元旦、情人节、元宵节……都要促销呀!再来个满 300 减 50,满 500 减 80,vip 用户满 500-150 上不封顶!对于这种越来越多的需求,还要深入函数内部增加if分支吗?

策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换,策略模式是一种对象行为型模式。

其实这个定义的基本意思就是说,根据策略选择要执行的函数,每一个策略都会有一个标识名,可以称为 key,而策略名对应的函数,可以称为 value(其实就是使用 key 寻找 value,然后执行 vlaue 的过程)

使用思路:定义一个对象封装不同的行为,提供选择接口,在不同条件下调用相应的行为

// 策略类
let strategy = {
  // 5折
  percent50(price) {
    return price * 0.5;
  },
  // 7折
  percent70(price) {
    return price * 0.7;
  },
  // 9折
  percent90(price) {
    return price * 0.9;
  },
  // 满300-50
  fullReduce50(price) {
    // 执行满300-50的业务逻辑
  },
  // 满300-80
  fullReduce80(price) {
    // 执行满300-80的业务逻辑
  },
  // vip用户五折
  vip50(price) {
    // vip用户五折的业务逻辑
  },
};

// 调用策略类中的方法
// 环境类
function calculatePrice(strategyName, price) {
  return strategy[strategyName] && strategy[strategyName](price);
}

console.log(calculatePrice("percent50", 100)); // 50

上面例子中:

  • 策略类 是指 strategy 对象,保存了所有的策略名对应的方法
  • 环境类 是用接收策略名和其它参数,然后调用对应的策略方法

使用策略优化的好处:

  • 有效的避免了多重条件选择语句。
  • 策略模式提供了对 开放-封闭原则 的完美支持,将算法独立封装在 strategy 中,使得这些算法易于切换、易于理解、易于扩展

8. 总结与思考

更少的嵌套,尽早 return 

倾向于使用对象或使用 map 结构来优化if else,而不是 Switch 语句

多重判断时使用 Array.includes 

对 所有/部分 判断使用 Array.every  &  Array.some 

使用默认参数和解构

当一个项目中需要大量算法,大量匹配模式时,可以考虑策略模式

你可能感兴趣的:(JS/TS,JavaScript,if,else)