三个灵感闪现的 JS 实验,正向和逆向思维齐活了

·  阅读 2185
三个灵感闪现的 JS 实验,正向和逆向思维齐活了

灵感闪现

某天,正在认真的敲代码,看着刚刚写下的这行代码,突发奇想,如果逆向思维去写这行代码会是什么结果。

「一生二,二生三」,我从一行代码联想到数行 JS 代码,何不试试看?

于是,接下来的故事,顺其自然的发生了。接下来的文字,也自然而然的被创作了出来。

文章速读

本文从正向和逆向两个不同的思维点出发,实验了几个不同 JS 功能。

阅读文章,可以有以下收获:

JS 实验开始

includes 不是一劳永逸的法子?

编程欢乐小剧场

一:年轻人,为何唉声叹气。

某:代码改漏了,太不严谨了。

一:哪里,我看看。

某:这里,表单里新增一个表单项,结果条件判断漏加了。

一:确实,分散的条件判断就会有漏改的风险。

某:有改善的方法不。

一:当然。

某:键盘奉上。

一种情况

合并多个条件判断,是使用 includes 最常见的用法。

如下的功能中,需要判断输入的字符串是否包含作者自己的名字。

function func(name) {
  if (name === 'ye' || name === 'yi') {
    return 'self';
  }
  return 'other';
}
let nameStr = 'zhang';
let res = func(nameStr);
console.log(res); // 'other'
复制代码

如果再增加一个判断,是否有作者好友的名字。条件会不断增加,不优雅且容易漏掉。

includes 判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回 false。

function func(name) {
  let list = ['ye', 'yi', 'shang', 'ying', 'yan'];
  if (list.includes(name)) {
    return 'self';
  }
  return 'other';
}
let nameStr = 'shang';
let res = func(nameStr);
console.log(res); // 'self'
复制代码

只需要维护 list 数组即可,不需要不停增加判断条件。这个功能用法还挺常见常用的。

但是用了它,不代表代码可以一劳永逸,因为还有一种情况,不仅增加了判断条件,还增加了条件中需要判断的项。

另一种情况

某:其实我还有个困惑。

一:说说看。

某:有时候改了表单项的 name 值,表单 UI里改了,判断里忘记改了。

一:确实,分散的设置就会有漏改的风险。

某:这种情况也能集中处理?

一:当然。

某:键盘和膝盖双双奉上。

先简单描述一下需求,在一组用户信息中,如果当有订单产生的时候,部分用户信息不能修改。

一般情况下会把不能修改的信息的唯一 key 值放入 keyList 数组中,然后通过其 includes 进行判断。

  • 当 includes 的值是 true 的时候,当前信息类型的 disabled 值设置为 true。
let obj = [
  {
    key: 'name',
    value: 'ye',
  },
  {
    key: 'age',
    value: 18,
  },
  {
    key: 'speciality',
    value: 'tale telling',
  },
  {
    key: 'hobby',
    value: 'read',
  },
];
let keyList = ['speciality', 'hobby'];
let orderFlag = true;
obj.map(e => {
  if (orderFlag && keyList.includes(e.key)) {
    e.disabled = true;
  }
});
console.log(obj);
复制代码

打印结果

但是,由于种种原因,需要改动其中一个 key 值,但是判断的 keyList 数组中的值忘记修改了。这个时候,得到在最终结果就不准确了。

这种情况可以换一种方式。

let obj = [
  {
    key: 'name',
    value: 'ye',
  },
  {
    key: 'age',
    value: 18,
  },
  {
    key: 'speciality',
    value: 'tale telling',
    editJudgeFlag: true, // 是否需要判断可以编辑的布尔值
  },
  {
    key: 'hobbys',
    value: 'read',
    editJudgeFlag: true, // 是否需要判断可以编辑的布尔值
  },
];
let orderFlag = true;
obj.map(e => {
  if (orderFlag && e.editJudgeFlag) {
    e.disabled = true;
  }
});
console.log(obj);
复制代码

数组数据走配置化还是很方便的。不需要维护判断的 keyList 数组,直接为需要进行判断的元素添加 editJudgeFlag。

  • 当 editJudgeFlag 为 true 且满足订单判断条件时,当前信息类型的 disabled 值设置为 true。

打印结果

小结

  • 单一的条件判断,可以用 includes 实现。也可以用数组的 some 方法,可以凭个人喜好进行选择。
  • 如果数据是数组类型或者能重组成数组类型,走配置化更为方便。

finally 听说好用?

编程欢乐小剧场

某:来吧,是时候做个决定了。

一:我不知道怎么回答,多说的讯息。

某:我们的异步请求,到底用不用 finally?让我们来统一下。

一:好问题啊,赞。

某:所以?

一:我们一起来看看,怎么写更合理。

某:键盘奉上。

try...catch and finally

finally 块中的语句会在 try 块和 catch 之后执行。这里有两个而且。

  • 无论是否抛出异常,finally 块中的语句都会执行。
  • 如果抛出异常,即便没有 catch 处理异常,finally 块中的语句也会执行。
try {
  await request({ url: `http://localhost:${server.address().port}` });
} finally {
  server.close();
}
复制代码

如上,server 都会执行关闭操作。

正向思维

借 finally 的特质合并「按钮防重&&数据重置」功能

现实项目中,新增/编辑数据的弹窗,是不可重复提交的,且提交之后,无论提交成功与否,表单数据需要置空(实际情况可能比举例中更复杂一些)。

通常,我们的功能处理如下:

http(params)
  .then(() => {
    message.success('操作成功');
  })
  .finally(() => {
    // 重置数据
    setData({});
    // 设置按钮不可点击
    setConfirmLoading(false);
  });
复制代码

既然 finally 块中的语句也会执行,便可以把 try...catch 中相同的处理,提炼到 finally 块中,可减少重复的设置。

逆向思维

如果 try 语句中有 return,finally 依旧会执行吗?

function func() {
  try {
    return 'try';
  } finally {
    return 'finally';
  }
}
let res = func();
console.log('res:', res);
复制代码

打印结果

// > res: finally
复制代码

通过打印结果,不难发现,try 语句中有 return,finally 块中的语句依旧是执行了的。我们因此又得到一条规则:

如果 finally块 语句中返回一个值,那么这个值将会成为整个 try-catch-finally 的返回值。

为什么这么说呢,等我改造一下上面的代码:

function func() {
  try {
    console.log(1);
    return 'try';
  } catch (e) {
    console.log(2);
  } finally {
    console.log(3);
    return 'finally';
  }
}
let res = func();
console.log('res:', res);
复制代码

打印结果

// > 1
// > 3
// > res: finally
复制代码

再看现在的打印结果,try 语句中是执行的,只不过,finally 中的 return「覆盖了」try 中的 return。

小结

  • 无论是否抛出异常,无论有没有 catch 处理异常,finally 块中的语句都会执行。
  • 借助 finally 的特质可以帮助合并按钮防重和数据重置的功能。
  • 如果 finally块 语句中返回一个值,那么这个值将会成为整个 try-catch-finally 的返回值。

** 运算符,相同运算符还要小括号?

编程欢乐小剧场

某:咦?

一:干什么?

某:咦?咦?

一:干什么?干什么?

某:这个运算的小括号是不是多余了?

一:你猜呢。

某:这样说,那应该不是多余的。

运算的结合性

运算的结合性指的是具有同等优先级的运算符将按照怎样的顺序进行运算。分为左结合和右结合两种。

左结合

一般优先级相同的运算符做运算的时候 ,结合性是左结合。

即:a + b + c 等于 (a + b) + c

右结合

一般优先级不同的运算符做运算的时候,结合性是右结合。

即:a + b * c 等于 a + (b * c)

幂运算(**)

幂运算符返回第一个操作数取第二个操作数的幂的结果。

结合性

幂运算符是右结合。

即:a ** b ** c 等于 a ** (b ** c)

看几个运算结果

console.log(2 ** 3 ** 2)   // > 512
console.log(2 ** (3 ** 2)) // > 512
console.log((2 ** 3) ** 2) // > 64
复制代码

所以如果计算的时候,想从左向右运算,需要加小括号。

优先级

MDN 文档中没有直接说幂运算的优先级,但是我可以试:

console.log(2 * 3 ** 2); // 18
console.log(3 ** 2 * 2); // 18
console.log(2 + 3 ** 2); // 11
console.log(3 ** 2 + 2); // 11
console.log(2 - 3 ** 2); // -7
console.log(3 ** 2 - 2); // 7
console.log((-3) ** 2); // 9
console.log(-(3 ** 2)); // -9
复制代码

根据这个结果,不难得出结论:

  • 幂运算的优先级高于基础的加减乘除运算符。
  • 不能将一元运算符(+/-/~/!/delete/void/typeof)放在底数之前。(这条是 MDN 文档里写的)

小结

  1. 当不同的运算符进行运算的时候,优先级会影响你想要的运算结果。所以有必要进行适当了解。
  2. 幂运算是右结合,所以计算的时候注意顺序,如果有必要可以添加小括号。另外它其实等价于 Math.pow()。

总结

一生二,二生三,三生万物。 —— 老聃

前面看了几天源码,从源码中学习优秀的开发者的设计思维,减少低质量代码的产出。而前几天的文章中提到过,学习优秀开源项目的源码可以帮助扩展开发思维。

所以有了很不错的收获。

举例场景方面,本篇除了结合实际开发场景,部分实现方案其实也是来自源码中的代码。

实现思维方面,分享了正向和逆向这两个不同的思维面,也分享了开发过程中不错的功能联想。

如果觉得文章还不错,就 点赞 + 收藏 一波,持续产出技术分享。

本文正在参加「金石计划 . 瓜分6万现金大奖」

收藏成功!
已添加到「」, 点击更改