目录
4.1.1 借助对象的 { key, value } 结构 进行优化
4.2.1 使用 Map 结构优化多元判断(将多个查询条件,拼接成字符串)
4.2.2 使用 Map 结构优化多元判断(将多个查询条件,拼接成对象)
4.2.3 使用 Map 结构优化多元判断(将多个处理逻辑,抽成公共方法)
4.2.4 使用 Map 结构优化多元判断(将多个查询条件,抽成正则表达式)
需求:当条件为真时,打印出日志内容
优化前
- let flag = true;
- if (flag) {
- log();
- }
-
- function log() {
- console.log("如果 flag 值为真,打印这段文字");
- }
优化后
- let flag = true;
- flag && log();
-
- function log() {
- console.log("如果 flag 值为真,打印这段文字");
- }
需求:执行登录操作,如果用户名和密码输入框为空,则提示用户”用户名和密码不能为空”;如果都不为空,则执行登录的操作
优化前
- let user = "lyrelion";
- let password = "solution";
-
- if (user && password) {
- // 执行登录操作
- } else {
- return "用户名和密码不能为空";
- }
优化后
排非策略,先排除为 false 的情形,在执行其他操作(干掉 else 分支)
- let user = "lyrelion";
- let password = "solution";
-
- if (!user || !password) {
- return "用户名和密码不能为空";
- }
- // 执行登录操作
使用场景:在不影响可读性的情况下,处理
if else
分支下简短的返回值、单个简短赋值语句、调用单个相应函数时,建议使用三目运算符
优化前
- function demo(flag) {
- if (flag) {
- return "true";
- } else {
- return "false";
- }
- }
优化后
- function demo(falg) {
- return flag ? "true" : "false";
- }
优化前
- function demo(flag) {
- let val = "";
- if (flag) {
- val = "true";
- } else {
- val = "false";
- }
- }
优化后
- function demo(flag) {
- let val = flag ? "true" : "false";
- }
优化前
- function demo(flag) {
- if (flag) {
- success();
- } else {
- fail();
- }
- }
优化后
- function demo(flag) {
- flag ? success() : fail();
- }
需求:后端大哥说,给你返回的数据里面如果有
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 嵌套
- 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) {
- // 进行业务逻辑操作
- }
- try {
- // 有可能出现错误的代码写在这里
- if (result.data.userInfo.hobby.length) {
- // 进行业务逻辑操作
- }
- } catch (error) {
- // 出错后的处理写在这里
- }
如果 try 中的代码没有出错,则程序正常运行 try 中的内容后,不会执行 catch 中的内容
如果 try 中的代码出错,程序立即跳入 catch 中继续执行,try 中出错后的程序就不再执行了
对一个空值进行属性读取时,程序会抛出异常;在多级嵌套的对象中取属性值的时候,更容易出现这个问题
为了保证程序的健壮性,就需要确保对象不为空时,再读取下一级的值
- // 可选链优化
- if (result?.data?.userInfo?.hobby?.length) {
- // 进行业务逻辑操作
- }
操作符 *?.* 会检查操作符左边的值是否是空值。如果是 null 或 undefined,这个表达式就终止然后返回 undefined。否则,这个表达式继续执行检查
需求:有多个按钮,点击按钮后,执行不同的业务逻辑
优化前
- /**
- * 按钮点击事件
- * @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 语句,没有明确的中断流程,在每次条件判断后,都会执行下次判断条件,可能会造成逻辑混乱
借助对象结构,把判断条件作为对象的属性名,把处理逻辑要传入的参数作为对象的属性值;
在执行按钮点击事件时,通过查询对象中的键,获取到键对应的值,然后执行对应的处理逻辑
- 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]);
- }
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 对象:
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值
- Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是
- Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算
需求:在点击按钮时不仅要判断 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 的数量就增加到一元判断条件的二倍,代码看着更臃肿了
那么对于二元的条件判断,我们应该怎么去优化它们呢?
- 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 查询,这种方法非常适合于二元或多元条件判断
如果你不喜欢把查询条件拼接为字符串使用,这还有一种方法,把查询条件作为对象,借助 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
假如在 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],
- // ……
- ]);
上面的写法虽然 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 后的优势就更加的明显了,符合正则的条件的公共逻辑都会执行
一元条件判断:存到 Object 中。
一元条件判断:存到 Map 中。
二元或多元判断:将判断条件拼接成字符串存到 Object 中
二元或多元判断:将判断条件拼接成字符串存到 Map 中
多元判断时:将判断条件写成 Object 存到 Map 中
多元判断时:将判断条件写成正则存到 Map 中
在工作中,巧妙的使用 ES6 中提供的数组新特性,也可以达到轻松优化逻辑判断的效果
需求:判断 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("可爱的小动物");
- }
- }
需求:判断 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
- }
需求:判断 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
- }
优化前
- 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); // {}
- }
需求:咱马上要过国庆节啦,得打折清仓呀,有的商品 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
对象,保存了所有的策略名对应的方法使用策略优化的好处:
更少的嵌套,尽早 return
倾向于使用对象或使用 map 结构来优化if else,而不是 Switch 语句
多重判断时使用 Array.includes
对 所有/部分 判断使用 Array.every & Array.some
使用默认参数和解构
当一个项目中需要大量算法,大量匹配模式时,可以考虑策略模式