0%

JavaScript Logical NOT Operator

JavaScript中的逻辑非(Logical NOT)运算符是一个一元运算符,用感叹号(!)表示。它用于对一个值进行逻辑取反操作,即将真值转换为假值,假值转换为真值。

1
2
3
4
const a = true;
const b = false;
console.log(!a); // 输出: false
console.log(!b); // 输出: true

这个操作符通常来说是用来操作布尔值的,但是很多时候大家也用它来操作非布尔值,用来操作非布尔值的时候,JS内部会先将对应的值转换为布尔值,然后再进行取反操作。

非布尔值到布尔值的转换规则

JavaScript内部有一套规则用于将非布尔值转化为布尔值,以下值转换为布尔值的时候是false,我们称这些值为falsy values

  • false
  • 0
  • -0
  • 0n(BigInt的零值)
  • ""(空字符串)
  • null
  • undefined
  • NaN
  • document.all(在某些环境中被视为falsy)

除了falsy values之外的其他值在转换为布尔值时都是true,我们称这些值为truthy values

  • true
  • 1
  • Hello
  • …等等

双重逻辑非!!

有时候我们会看到两个感叹号连在一起使用,例如!!value,这是一个常见的技巧,用于将一个值转换为布尔值。第一个感叹号将值取反,第二个感叹号再次取反,从而得到原始值的布尔表示。

1
2
3
4
5
const value = "Hello";
console.log(!!value); // 输出: true

const emptyString = "";
console.log(!!emptyString); // 输出: false

既然是双重取反操作,那么是不是意味着!!value的结果和value是一致的呢?并不尽然,只有当value是布尔值的时候,!!value的结果才和value一致,如果value是非布尔值,那么!!value的结果就不一定和value一致了。因为类型不一致,如果比较操作用的是严格比较(===),那么结果就是false。

1
2
3
4
5
const value = "Hello";
console.log(!!value === value); // 输出: false

const boolValue = true;
console.log(!!boolValue === boolValue); // 输出: true

还有其他方法吗?

除了使用双重逻辑非(!!)来将一个值转换为布尔值之外,还可以使用Boolean函数:Boolean(value),效果和!!value是一样的。

1
2
3
4
5
const value = "Hello";
console.log(Boolean(value)); // 输出: true

const emptyString = "";
console.log(Boolean(emptyString)); // 输出: false

今天是正月初八,祝大家开工大吉!早日实现财富自由!

Git 如何批量删除分支

批量删除本地分支

假设本地有若干分支都以feature/开头,如果要批量删除这些分支,可以使用如下命令:

1
git branch | grep 'feature/' | xargs git branch -d

解释一下这个命令:

  1. git branch:列出所有本地分支。
  2. grep 'feature/':过滤出以feature/开头的分支。
  3. xargs git branch -d:将过滤后的分支名传递给git branch -d命令进行删除。

普通的删除分支命令格式如下:

1
2
git branch -d <branch-name> # 删除本地分支
git branch -D <branch-name> # 强制删除本地分支

也支持一次性删除多个,但是需要手动输入分支名,例如:

1
git branch -d feature/branch1 feature/branch2 feature/branch3

遗憾的是不支持通配符批量删除。所以xargs命令就派上用场了。

xargs 命令会将前面的输出作为后面命令的参数传递进去,从而实现批量删除。(注意:Windows系统没有grepxargs命令,可以使用Git Bash或者安装GNU工具集来获得这些命令。)

批量删除远程分支

假设远程有若干分支都以feature/开头,如果要批量删除这些分支,可以使用如下命令:

1
git branch -r | grep 'origin/feature/' | sed 's/origin\///' | xargs -I {} git push origin --delete {}

解释一下这个命令:

  1. git branch -r:列出所有远程分支。
  2. grep 'origin/feature/':过滤出以origin/feature/开头的分支。
  3. sed 's/origin\///':去掉分支名前的origin/前缀。
  4. xargs -I {} git push origin --delete {}:将过滤后的分支名传递给git push origin --delete命令进行删除。

普通的删除远程分支命令格式如下:

1
2
3
git push origin --delete <branch-name> # 删除远程分支
git push origin -d <branch-name> # 删除远程分支(简写)
git push origin -d 'feature/*' # 通配符形式(需要git 2.8.0及以上版本)

介绍

在日常编程中,我们经常需要在一个数组中查找某个元素,查找元素的方式有很多,大多数人喜欢用===来进行比较,比如下面的代码,从用户列表中找出用户名为Philip的用户:

用户列表如下:

1
2
3
4
5
const users = [
{ name: 'Alice' },
{ name: 'Bob' },
{ name: 'Philip' },
];

使用for...of进行查找

1
2
3
4
5
6
7
8
9

function findUserByName(users, name) {
for (const u of users) {
if (u.name === name) {
return u;
}
}
return null; // 其实这句可以省略,如果没有找到则自动返回 undefined
}

使用find进行查找

其实上面的代码可以使用find来实现,find会返回数组中第一个满足条件的元素,如果没有找到则返回undefined

1
2
3
function findUserByName(users, name) {
return users.find(u => u.name === name);
}

使用find的好处是代码更简洁,更加符合函数式编程风格,更容易阅读和维护。

介绍

今天我们一起来学习一下Ant Design(简称AntD)中的AutoComplete组件。AutoComplete组件是一个非常实用的输入框组件,它可以根据用户输入的内容,动态地显示匹配的选项,提升用户体验。为什么要学习这个组件呢?因为我之前一直没有使用过它,而且总是将这个组件和Select组件搞混,一直以为他们干的是一件事,今天就测底解决这个疑惑。

首先上结论,看看AutoComplete和Select的区别:

  • AutoComplete:主要用于输入和选择,用户可以输入部分内容,组件会根据输入内容动态显示匹配的选项。
  • Select:主要用于选择,用户只能从下拉列表中选择一个选项,不能输入其他内容(除非定制Select的行为)。

也就是说Select组件是在一组固定的值中选择一个,而AutoComplete组件则是可以输入内容并从动态生成的选项中选择。AutoComplete的作用主要是做辅助输入。

如果大家不明白AutoComplete是如何工作的,可以尝试去百度、必应或者谷歌等搜索引擎随便搜点东西,当我们在搜索框中输入内容时,搜索引擎会根据我们输入的内容,动态地显示匹配的搜索建议,这就是AutoComplete的典型应用场景。

如何使用AutoComplete组件

下面是一个输入邮箱的例子,当用户输入邮箱前缀时,组件会动态显示匹配的邮箱地址后缀。使用AutoComplete组件非常简单,一般都是相应onSearch事件,然后根据输入的值动态生成选项。

在下面的代码中,我们定义了一个handleSearch函数,当用户输入内容时,这个函数会被调用。我们根据输入的值动态生成选项,并通过setOptions函数将这些选项传递给AutoComplete组件。

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
import React from 'react';
import { AutoComplete } from 'antd';
import type { AutoCompleteProps } from 'antd';

const App: React.FC = () => {
const [options, setOptions] = React.useState<AutoCompleteProps['options']>([]);

const handleSearch = (value: string) => {
setOptions(() => {
if (!value || value.includes('@')) {
return [];
}

return ['gmail.com', '163.com', 'qq.com'].map((domain) => ({
label: `${value}@${domain}`,
value: `${value}@${domain}`,
}));
});
};

return (
<AutoComplete
style={{ width: 200 }}
onSearch={handleSearch}
placeholder="input here"
options={options}
/>
);
};

export default App;

大小写无关的搜索

如果要实现大小写无关的搜索,可以响应filterOption方法。

1
2
3
4
5
6
7
8
9
10
<AutoComplete
style={{ width: 200 }}
onSearch={handleSearch}
placeholder="input here"
options={options}
filterOption={(inputValue, option) =>
option!.value.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
/>

典型的应用场景

  1. 搜索建议,如搜索引擎的搜索框。
  2. 表单输入,比如要填写邮箱地址时,我们可以将常见的邮箱地址后缀(如@gmail.com, @yahoo.com等)作为选项,用户输入邮箱前缀时,组件会动态显示匹配的邮箱地址后缀。

总之,AutoComplete组件的核心作用是输入提示,在选项不固定的时候非常有用。

CSS属性选择器

下面是一个属性选择器的示例,选择具有属性title且其值为"Sports"的元素

1
const elements = document.querySelectorAll('[title="Sports"]');

JS中如何交换两个变量?

可以使用ES6的解构赋值来交换变量的值。

1
2
3
4
5
6
7
8
9
10
11
let a = 1;
let b = 2;

// 交换前
console.log('交换前:', a, b);

// 交换
[a, b] = [b, a];

// 交换后
console.log('交换后:', a, b);

React中如何使用条件渲染

所谓条件渲染是指满足某个条件时才渲染某个组件。比如当用户登录后,可以渲染用户的个人信息页面,否则渲染登录页面。

1
{isLoggedIn ? <Home /> : <Login />}

Ant Design中Form.Item的valuePropName属性

通常来说,Ant Design中的控件,比如InputSelect等,其值都是通过value属性来控制的,但是有一些控件没有value属性,比如SwitchCheckbox等。这时就需要使用valuePropName属性来指定控件的值属性。否则提交的时候无法取得对应的控件值。

看下面的代码,这是一个简单的登录框,包含用户名、密码、记住我和提交按钮,你会发现,无论记住我是否选中,当点击提交按钮时,打印的结果中的remember都是false

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
const Login: React.FC = () => {
const handleSubmit = async (values: LoginParams) => {
console.log('登录信息:', values);
};
return (
<Form
name="login"
initialValues={{ remember: true }}
onFinish={handleSubmit}
>
<Form.Item
name="username"
rules={[{ required: true, message: '请输入用户名' }]}
>
<Input placeholder="用户名" />
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true, message: '请输入密码' }]}
>
<Input.Password placeholder="密码" />
</Form.Item>
<Form.Item name="remember" noStyle>
<Checkbox>记住我</Checkbox>
</Form.Item>
<Form.Item>
<Button htmlType="submit">登录</Button>
</Form.Item>
</Form>
);
};

原因就是Checkbox不是使用value来记录它的值,它使用的是checked(html底层如此,详情看这里)

解决办法,只需要给Form.Item添加valuePropName="checked"属性即可,代码如下,这时我们再点击提交按钮,打印的结果中remember的值就会根据复选框的选中状态而变化了。

1
2
3
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>记住我</Checkbox>
</Form.Item>

还有哪些控件需要使用valuePropName属性呢?比如RateSlider等控件也需要使用valuePropName来指定值属性。

  • Switch
  • Rate
  • Slider
  • Transfer

Form.Item的工作原理

使用Ant Design中的Form.Item包裹组件,且在Form.Item中指定name属性后,Form.Item会自动将组件的值和Form进行绑定。也就是说,组件的值会自动同步到Form中,Form的值也会自动同步到组件中。

  • 不能使用组件的onChange收集value,应该使用Form级别的onValuesChange来收集表单的值变化。

  • 不能使用defaultValue属性来设置初始值,应该使用initialValues属性来设置。

  • 不能在Form.Item中直接使用value属性来设置组件的值,应该使用form.setFieldsValue来设置组件的值。

  • 使用htmlType='submit'的按钮来提交表单,点击此按钮会触发表单的onFinish事件。onFinish函数中的values参数会接收到表单的所有值。

Form.Item如何寻找内部控件

本以为Form.Item会自动寻找第一个子元素作为控件。但是第三个列子说明不是这样的。

这样不可以,username的值始终是undefined

1
2
3
4
<Form.Item name="username">
<div>This is title</div>
<Input placeholder="用户名" />
</Form.Item>

这样可以:

1
2
3
4
5
<Form.Item name="username">
<div>
<Input placeholder="用户名" />
</div>
</Form.Item>

这样也可以

1
2
3
4
5
6
<Form.Item name="username">
<div>
<div>This is title</div>
<Input placeholder="用户名" />
</div>
</Form.Item>

介绍

我们在使用Ant Design中的Table组件时,经常会遇到文本过长导致内容溢出的情况,今天我们来看看如何使用Ellipsis text来解决这个问题,所谓Ellipsis text,就是当文本内容超出指定长度时,显示省略号,为了功能的完整性,还需要添加一个鼠标悬停显示完整文本的功能,效果如下。

ellipsis-text

CSS中的Ellipsis

其实Ellipsis text是CSS中的一个功能,比如下面定义了一个ellipsis-text样式,当文本长度超过200px时,会自动显示省略号。

1
2
3
4
5
6
.ellipsis-text {
width: 200px; /* 文本宽度200像素 */
white-space: nowrap; /* 不换行 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 末尾显示... */
}

下面来应用这个样式,其中title属性用来显示tooltip,其值和p标签的内容完全相同。

1
2
3
<p class="single-line" title="This is a very long line of text">
This is a very long line of text
</p>

Ant Design Table组件中的Ellipsis

Ant Design的Table组件中,Column组件提供了一个ellipsis属性来实现这个功能,非常的方便,只要在需要的地方添加ellipsis属性即可。

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
import { Table } from 'antd';

const columns = [
// ...
{
title: 'Description',
dataIndex: 'description',
ellipsis: true, // 这里开启了ellipsis功能
},
];

const data = [
{
key: '1',
name: 'John Brown',
description: 'This is a very long description that will be truncated',
},
{
key: '2',
name: 'Jim Green',
description: 'This is another very long description that will be truncated',
},
];

const App = () => (
<Table columns={columns} dataSource={data} />
);

就这么简单,但是这个功能有些限制,对于纯文本内容来说还可以,但是如果你的列有自定义渲染器的话,这个功能可能会失效,比如我想将description列渲染成link button,在点击的时候显示一个弹框。这时候仅添加ellipsis属性就不够了,我们需要自定义一个组件。

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
import React from 'react';
import { Tooltip } from 'antd';
type EllipsisTextProps = {
children: React.ReactNode;
maxLength?: number;
}
export default function EllipsisText(props: EllipsisTextProps) {
const { children, maxLength = 30 } = props;
const text = typeof children === 'string' ? children : String(children);
if (text.length <= maxLength) {
return <span>{text}</span>;
}
return (
<Tooltip title={text}>
<span
style={{
display: 'inline-block',
maxWidth: `${maxLength}ch`,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{text}
</span>
</Tooltip>
)
}

上面我们自定义了一个EllipsisText组件,在这个组件中首先判断传入的文本是否超过指定长度(30),如果没有超过,那么直接渲染文本,如果超过了,则使用Tooltip组件包裹,并且应用了CSS的ellipsis样式。

使用方法如下,在Table组件的Column中使用自定义的EllipsisText组件。在这里我们将EllipsisText组件放在了一个link button中,这样用户点击按钮时可以触发一些操作,比如显示一个弹框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
title: '描述',
dataIndex: 'description',
key: 'description',
search: false,
ellipsis: true,
render: renderDescription,
},

const renderDescription = (text: ReactNode) => {
return (
<Button type='link' onClick={onClickHandler}>
<EllipsisText>{text}</EllipsisText>
</Button>
);
};

今天就到这里了,祝大家编程愉快,我们下期再见!

8月大杂烩

使用类选择器时,如何指定多个类名?

比如我们想要选择的元素有三个类名classA, classB, classC,那么选择器可以这样写:

1
document.querySelector('.classA.classB.classC')

只要将三个类名连接起来就行,中间不需要加空格。

为什么我的请求在Chrome开发者工具中看不到?

今天和同事调试代码时发现一个奇怪的现象,有一个api调用,在浏览器开发者工具中的network面板中竟然看不到它的response,研究了半天也没找到原因,后来发现是因为调用api之后,使用了window.location.href = path - 这句代码会导致页面进行一个强制刷新,所以api调用的记录也被刷掉了,response面板中也看不到。

如果要修复这个问题,可以使用useNavigate代替,代码如下:

1
2
3
4
5
import { useNavigate } from 'react-router-dom';

const navigate = useNavigate();
const path = xxx;
navigate(path, {replace: true});

使用JSON.parse时一定要注意异常处理

一个好的习惯是,始终用try-catch包裹JSON.parse,以防止解析失败导致的错误。例如:

1
2
3
4
5
6
try {
const data = JSON.parse(jsonString);
// 处理解析后的数据
} catch (error) {
console.error('JSON解析失败:', error);
}

JSON.parse非常容易出错,当传入的参数是空字符串, undefined或不合法的JSON字符串时,都会抛出异常。因此,始终使用try-catch来捕获这些异常是一个好的习惯。

1
2
3
JSON.parse(undefined); // Uncaught SyntaxError: "undefined" is not valid JSON
JSON.parse(''); // Uncaught SyntaxError: Unexpected end of JSON input
JSON.parse(null); // null

要点:

  1. 永远不要传递undefinednull空字符串JSON.parse
  2. 如果要解析空字符串,应该用JSON.parse('""')来代替。
  3. 虽然null不会直接导致异常,但解析结果是null,这可能导致后续代码出错,因为我们总要访问解析结果中的字段。

如何对指定目录运行单元测试?

随着项目的进行,测试用例会越来越多,跑一次完整的测试需要的时间也水涨船高,如果我们只修改了某一个功能,那么可以只对这个功能进行单元测试。

下面的命令只对user目录运行单元测试,这个命令有个前提,那就是你的测试用例都放到src/__tests__/目录下。

1
npm run test -- src/__tests__/pages/user

如果你的测试用例是跟随业务功能代码的结构放置的,那么可以使用以下命令:

1
npm run test -- src/pages/user

也就是说,无论使用哪种方式,都要保证指定的目录下有对应的测试用例。

Ant Design Table中如何隐藏某一列?

1
2
3
4
5
6
7
8
const columns = [
// ...
{
title: '作者',
dataIndex: 'author',
hidden: someCondition, // someCondition = true时隐藏该列
},
];

‘Button’ refers to a value, but is being used as a type here. Did you mean ‘typeof Button’?

组件代码如下,就这么简单的一个组件,竟然能出现上面的问题。真是服了!

1
2
3
4
5
import { Button } from 'antd';

export default function Users() {
return <Button>提交</Button>;
}

仔细检查了半天也没有发现问题所在,后来一看是文件名不对!React组件文件的扩展名应该是jsx或者tsx,而不是js或者ts。而我这个组件的扩展名恰恰是.ts,将它改为.tsx后问题就解决了。

限制输入框文本长度

有两种方式,一种是在Form.Item中的rules中添加max属性,另一种是在Input组件中添加maxLength属性。

在Form.Item中添加max属性

这种方式的好处是,如果超出长度限制,会自动显示提示信息,但是用户仍然可以继续输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Form.Item
name={dataIndex}
style={{ margin: 0 }}
rules={[
{
required: true,
message: `Please Input ${title}!`,
},
{
max: 10,
message: `Max length is 10`,
}
]}
>
{inputNode}
</Form.Item>

在Input组件中添加maxLength属性

这种方式的好处是,用户输入超过长度限制时,会被直接阻止输入。弊端是没有提示信息。

1
<Input maxLength={10} />

如果既要限制输入长度,又要提供提示信息,可以结合使用两种方式,在实际使用中,可以将MaxLength提取成公共变量,这样可以避免重复代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const MaxLength = 10;

<Form.Item
name={dataIndex}
style={{ margin: 0 }}
rules={[
{
max: MaxLength,
message: `Max length is ${MaxLength}`,
}
]}
>
<Input maxLength={MaxLength} />
</Form.Item>

使用默认参数代替||运算

不知道大家是否见到过如下这样的代码。

1
2
3
4
function foo(userList: string[]) {
const users = userList || [];
return users.map(user => user.toUpperCase());
}

这段代码中使用|| []进行了一种防御性编程,因为传入的userList可能是undefined。使用|| []以后,就可以避免在undefined上调用map函数出错,其实我们可以使用javascript的默认参数来实现这个功能。

1
2
3
function foo(userList: string[] = []) {
return userList.map(user => user.toUpperCase());
}

需要注意的是,默认参数只能处理undefined, 对于null或其他假值(如0''等)是无法替代的。因此在使用默认参数时,需要确保传入的参数是undefined。否则还是用||比较保险一些。

介绍

git是每天都要用的工具,在使用过程中,难免遇到一些问题,姑且罗列一下供日后参考

fatal: not a git repository (or any of the parent directories): .git

这个错误一般是因为当前目录不是一个git仓库,解决方法是切换到正确的git仓库目录,或者使用git init命令初始化一个新的git仓库。

大家注意到了吗?这个错误消息最后有一个.git, .git是每一个git仓库根目录下都有的文件夹,git在当前目录下找不到这个文件夹,而且在当前目录的所有父目录下也找不到这个文件夹,所以就报了这个错误。

error: pathspec ‘feature/123456’ did not match any file(s) known to git

这个错误一般是因为你要checkout的分支或者文件不存在,解决方法是检查拼写是否正确,可以使用git branch命令查看本地分支列表.

如果是在某个分支的基础上创建一个新的分支,比如在develop分支上创建feature分支。那么可以先切换到develop分支,然后再通过-b选项创建feature分支,命令如下:

1
2
git checkout develop
git checkout -b feature/123456

这里-b选项表示创建一个新的分支并切换到该分支。

fatal: reference is not a tree: xxxx

这里xxx对应一个commit id,这是昨天遇到的一个问题,因为同事的代码有问题需要大家研究一下,但是这些代码还在develop分支上,所以我就使用下面的命令来对指定的commit进行checkout操作,于是就出现了上面的问题。

1
git checkout -b my-branch <commit_id>

经过一番研究,发现原因是没有更新代码,因为我是在自己的分支上运行这个命令的,此时我的分支并没有包含这个commit。解决方法就是先切换到develop分支,然后执行git pull命令更新代码,最后再运行上面这个命令就行了。

介绍

最近在做Code review的时候,发现很多使用Array.prototype.push的代码,都可以使用Array.prototype.map来重构。使用map可以使代码更简洁、更易读。

示例

下面是一个使用push来返回User对象的例子,逻辑很简单,根据传入的userList,返回一个简化的User对象列表。函数内部首先定义了一个空数组result,然后使用forEach遍历userList,如果满足条件,就将简化的User对象推入result数组中。最后返回这个result数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
const buildUserList = (userList: User[]) => {
let result: SimplifiedUser[] = [];
userList.forEach((user: User) => {
if (user.accountCode === '' || user.accountCode === null) {
result.push({
department: user.department,
userCode: user.username,
userName: user.fullName
});
}
});
return result;
}

这种写法很常见,也算中规中矩吧,但是有一些弊端:

  1. 需要额外维护一个结果数组result
  2. 数据的过滤和转换混合在了一起。
  3. 属于命令式编程的写法。

现在我们使用map来改写一下,对于第一个if判断,我们可以使用filter来提取满足条件的用户,然后使用map来转换成简化的User对象。这样就可以直接返回一个新的数组,而不需要手动创建和推入元素。

1
2
3
4
5
6
7
8
9
const buildUserList = (userList: User[]) => {
return userList
.filter((user: User) => user.accountCode === '' || user.accountCode === null)
.map((user: User) => ({
department: user.department,
userCode: user.username,
userName: user.fullName
}));
};

现在代码简洁了不少,但是filter中的条件有点长,我们可以将其提取成一个函数来提高可读性。

1
2
3
4
5
6
7
8
9
10
11
12
13
const isUserWithoutAccountCode = (user: User) => {
return user.accountCode === '' || user.accountCode === null;
};

const buildUserList = (userList: User[]) => {
return userList
.filter(isUserWithoutAccountCode)
.map((user: User) => ({
department: user.department,
userCode: user.username,
userName: user.fullName
}));
};

甚至map中的转换逻辑也可以提取成一个函数,这样代码会更清晰。

1
2
3
4
5
const simplifyUser = (user: User): SimplifiedUser => ({
department: user.department,
userCode: user.username,
userName: user.fullName
});

完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const isUserWithoutAccountCode = (user: User) => {
return user.accountCode === '' || user.accountCode === null;
};

const simplifyUser = (user: User): SimplifiedUser => ({
department: user.department,
userCode: user.username,
userName: user.fullName
});

const buildUserList = (userList: User[]) => {
return userList
.filter(isUserWithoutAccountCode)
.map(simplifyUser);
};

重构后的代码有如下好处:

  1. 逻辑清晰,简洁易懂,读起来更加顺畅。
  2. 数据的过滤和转换分离,职责单一,方便测试和维护。
  3. 使用了函数式编程的风格,符合现代JavaScript的编程习惯。

总结

什么是好代码?大概就是这个样子吧,为什么有些人的代码读起来通俗易懂,而有些人的代码读起来却像天书?这就是差距。我喜欢将其成为代码气质,我们一定要培养出自己的代码气质。

昨天组里聚餐,喝了几瓶啤酒,总算睡了一个好觉,这几天隔壁的猫天天叫,我每晚只能睡四五个小时,白天困得不行,已经好几天没有提PR了,下周要加油了!