说明:含义不清晰,不能做到「望文生义」
BadCode
var l = data.length;
GoodCode
// 闭包只有一行代码,可以使用单字变量
data.map(d => d.length)
说明:含义不清晰,不能做到「望文生义」
BadCode
var obj = {};
var obj2 = {...obj, key: 1};
GoodCode
var obj = {};
var objWithKey = {...obj, key: 1};
此处动词没有包含时态
变量名应该是名词或者名词短语。
例外:
说明:
BadCode
// 以名词开头,看不明白什么有什么功能
function option() {}
// 时态不对
function updatedTime() {}
GoodCode
function selectOption() {}
function updateTime() {}
例外:
说明:含义不清晰,不能做到「望文生义」
BadCode
var uo = function updateOrder(){}
var as = [].slice;
var ex = Object.extends;
var nu = number
GoodCode
// weixin/wx 是专有名词
var weixinUser = {};
var wx = weixin;
// POI 是专有名词
var poi = {};
例外:专有 api,如 alert
说明:在上层作用域下的代码,会被更多函数使用到。其名称应该尽量长或者通用,以保证能够搜索到。
BadCode
// 在全局变量上定义了一个 item 变量,但是很难从命名上理解其作用是什么。
window.item = {}
GoodCode
window.primaryProductItem = {};
说明:变量中加符号,往往是为了约定其优先级或者作用域。符号应该在变量名前面。
BadCode
function getDot_(){}
function privateFn$$ (){}
GoodCode
function _getDot() {}
function $$privateFn() {}
说明:类作为实例的所属,其名称表达的含义要一脉相承
BadCode
class Person() {}
var dog = new Person(); // dog is a Person ?
GoodCode
class Person() {}
var jack = new Person();
说明:粗暴的翻译,更容易造成误解,还不如写拼音
BadCode
// 渲染「页面顶部的执行人」
// 还是渲染「执行砍头的人」?
function renderHeadExecutantPeople(){}
GoodCode
function renderHeader() {}
说明:避免通一个概念在不同的代码用多种不同的单词描述。
BadCode
// 远程请求这个概念,先后用了 get/fetch/query
// 三个名词去描述
function getUserInfo() {}
function fetchProductInfo() {}
function queryPayment() {}
GoodCode
// 统一用 get 描述
// 阅读代码的人能体会到其中的共性
function getUserInfo() {}
function getProductInfo() {}
function getPayment() {}
例外:专有词
说明:双关语容易引起歧义
BadCode
// 订单类型,还是排序类型?
var orderType
GoodCode
var sortType
说明:命名往往是实现的隐喻,如果存在差异则会让阅读者看不懂代码。
BadCode
// empty 的命名含义和实现截然相反
// (我真的见过这种代码)
function getProduct(id) {
axios.delete('/product', {id});
}
GoodCode
function deleteProduct(id) {
axios.delete('/product', {id});
}
说明:布尔变量的名称中,如果加上 「not」之类的否定词,则相当于做了一次了逻辑判断。
BadCode
const notEmpty = !!array.length;
GoodCode
const empty = !array.length;
说明:代码太长说明做的事情不够专一,同时也会让阅读变得很困难。
说明:同样功能的代码不要重复三次
说明:代码里的逻辑分支要尽量少,只做一件事情,并且要处理好边界和异常情况。
说明:函数的输入越多,往往就代表功能约复杂
说明:对于比较奇怪的业务逻辑,或者因为系统、接口原因而写的比较奇怪的逻辑。要通过注释标注出来
BadCode
framework.doSomeThing();
framework.reset(); // 阅读者内心 OS:这里为啥要做一次 reset?
framework.continueSomeThing();
GoodCode
framework.doSomeThing();
// framework 有个 bug,这里必须要做一次 rest 附链接: http://github.com/issuse/***
framework.reset();
framework.continueSomeThing();
说明:纯函数比较好测试,逻辑也比较清晰,可以放心的引入和删除。
BadCode
let status;
function method() {
if (status) { ... }
}
GoodCode
function method(status) {
if (status) { ... }
}
说明:修改参数会导致函数的作用变得不可预测
BadCode
function updateObj(obj, value) {
obj.key = value;
return obj;
}
GoodCode
function updateObj(obj, value) {
return {...obj, key: value};
}
说明:this 的指向经常不固定,会导致代码难以理解。如果调用方不熟悉的话,很容易引起 Bug。
BadCode
function method() {
console.log(this.value);
}
method() // 报错
var obj = { method, value: 1}
obj.method() // 输出 1
GoodCode
function method(value) {
console.log(value);
}
说明:错误也是一种逻辑分支,如果不处理的话,代码就不够健壮。前端代码处理错误的方式一般为提示用户有异常发生。如果错误不影响业务流程,则写入日志里并上报。
BadCode
function method(data) {
try { return JSON.parse(data) }
catch (e) {}
}
GoodCode
function method(data) {
try { return JSON.parse(data) }
catch (e) {
alert('数据处理失败')
}
}
说明:magic number 是指直接在代码中硬编码的数字,往往具有一些业务含义。
这样会导致:
BadCode
if (status === 1) {
...
} else if (type === 4) {
...
}
GoodCode
enum Status {
Closed
}
enum Type {
Array
}
if (status === Status.Closed) {
...
} else if (type === Type.Array) {
...
}
说明:原子性意味着独立,且不可分割。其它属性都由原子业务属性推导、计算而来,这样能保证状态的一致。
BadCode
// 当 status 为 open 的时候展示弹窗
// 其它状态则隐藏弹窗
{
data() {
return {
showAlert: false,
status: 'closed',
}
},
onStatusChange() {
if (status === 'open') {
this.showAlert = true;
} else {
this.showAlert = false;
}
}
}
GoodCode
// showAlert 为非原子的状态
// 其状态可以由 status 推导而来
{
data() {
return {
status: 'closed',
}
},
computed: {
showAlert() {
return this.status === 'open';
}
}
}
说明:
BadCode
// 在一个列表中,用户可以对数据做多选
// 然后删除他们
class extends React.Component {
async componentDidMount() {
const listData = getData();
this.setState({ listData })
}
check = (item) => {
const listData = this.state.listData.map(i => {
if (i === item) {
return {...item, checked: true}
}
return i;
});
this.setState({ listData });
}
delete() {
// 返回给后端的数据结构,会多出一个 checked 字段
deleteItems(this.state.listData.filter(i => i.checked));
}
render() {
const list = this.state.listData.map(item => {
const className = ['item'];
if (item.checked) className.push('active');
return <label
className={className}
onClick={() => this.check(item)}
>{item.naem}</label>;
});
return <>
{list}
<button onClick={this.delete}>delete</button>
</>
}
}
GoodCode
// 在一个列表中,用户可以对数据做多选
// 然后删除他们
class extends React.Component {
async componentDidMount() {
const listData = getData();
// 使用独立的 selected 来保存 UI 状态
this.setState({ listData, selected: [] })
}
check = (item) => {
let { selected } = this.state;
selected = selected.findOrInsert(s => s.id, item);
this.setState({ selected });
}
delete() {
const { selected, listData } = this.state;
deleteItems(listData.filter(i => selected.includes(i.id))));
}
render() {
const { selected, listData } = this.state;
const list = listData.map(item => {
const className = ['item'];
if (selected.includes(item.id)) className.push('active');
return <label
className={className}
onClick={() => this.check(item)}
>{item.naem}</label>;
});
return <>
{list}
<button onClick={this.delete}>delete</button>
</>
}
}
说明:react 的 render 应该是纯函数,在 render 里运行 setState 会导致重复渲染,或者死循环。
BadCode
// 如果 type 为 http 的话,则自动转换为 https
class extends React.Component {
render() {
const { type } = this.state;
if (type === 'http') {
this.setState({ type: 'https'})
}
return <label>{type}</label>;
}
}
GoodCode
// 如果 type 为 http 的话,则自动转换为 https
class extends React.Component {
get type() {
const { type } = this.state;
if (type === 'http') return 'https';
return type;
}
render() {
const type = this.type;
return <label>{type}</label>;
}
}
说明:
BadCode
// foo 和 bar 互相依赖,导致了死循环
{
data() {
return {
foo: 1,
};
},
computed: {
bar() {
return this.foo + 1;
}
},
watch() {
bar() {
this.foo = this.bar + 1;
},
}
}
说明:比如用双等号做判断
BadCode
const foo = '0';
const bar = 0
// 做数据判断时不能用双等号
foo == bar // true
foo ? 1 : 2 // 1
bar ? 1 : 2 // 2
// 仅通过变量有没有 length 来判断是否为数组
if(obj.length) {
obj.forEach(...)
}
GoodCode
const foo = '0';
const bar = 0
foo === bar // false
if (Array.isArray(obj)) {
obj.forEach(...)
}
说明:这样做会导致数据的异常。如果需要做这种操作,最好使用数组函数,或者操作拷贝数据。
BadCode
const array = [1,2,3,4,5,6,7,8,9,10];
// 删除数组中的偶数
for (var i = 0; i < array.length; i++) {
if (array[i] % 2 == 0) array.splice(i);
}
// array 变成了 [1]
GoodCode
const array = [1,2,3,4,5,6,7,8,9,10];
array.filter(a => !(a % 2))
说明:
大多数场景下,setTimeout 后面传递一个时间是为了先执行后续的 A 代码,再延后执行代码闭包里的 B 代码,如右边示例代码。
但如果随着业务迭代,A 被改成异步,或者执行时间很长的话。之前做的延迟执行的防御措施就时效了,也许反而 B 会比 A 先执行。
BadCode
// 代码的本来意图是让 B 延后执行
setTimeout(() => {
B();
}, 1000);
A();
// 代码的意图是让 render 在下一帧执行
// 但是不同设备,一帧时间是不固定的
setTimeout(() => {
render()
}, 16);
GoodCode
// A 函数内要想办法使用 Promise 串接起来
await A();
b();
// 使用系统提供的 API 执行动画帧
requestAnimationFrame(() => {
render();
});
说明:陈旧的 API 往往有很多问题,比如安全、性能、不易读等。
BadCode
// 判断是否为数组
Object.prototype.toString.call(array) === "[object Array]"
// 查找数组里第一个偶数
for (var i = 0; i < array.length; i++) {
if (array[i] % 2 === 0) return array[i];
}
// 遍历对象的 key
for (var key in obj) {
console.log(key);
}
// 判断字符串/数组是否包含
'some text'.indexOf('some') >= 0
// 去除首位空格
' some text '.replace(/(^\s+|\s+$)/g, '')
// 新建对象/数组
const array = new Array();
const obj = new Object();
GoodCode
Array.isArray(array)
array.find(a => a % 2 === 0);
Object.keys(obj).forEach(console.log)
'some text'.includes('some')
' some text '.trim()
const array = [];
const obj = {};
说明:
React Ref 一般是用来处理和原生 dom 交互的场景,比如 canvas。
大部分对于 React ref 的使用都是错误的,大多都拿来用来控制子元素。这种场景我们更推荐用数据流(redux,mobx)或者用状态提升去做。
React 官方有对「状态提升」的描述 https://react.docschina.org/d...
BadCode
class List extends React.Component {
async refresh() {
this.setState({
items: getItems(),
});
}
render() {
return this.state.items.map(i => <label>{i}</label>);
}
}
class extends React.Component {
onRefresh = () => {
// 用 ref 去调用子元素的方法
this.list.refresh();
}
render() {
return <>
<List ref={l => this.list = l}></List>
<button onClick={this.onRefresh}/>
</>;
}
}
GoodCode
class List extends React.Component {
render() {
return this.props.items.map(i => <label>{i}</label>);
}
}
class extends React.Component {
// 把数据状态提升到父组件做操作
refresh = async () => {
this.setState({
items: getItems(),
});
}
render() {
return <>
<List items{this.state.items}></List>
<button onClick={this.refresh}/>
</>;
}
}
说明:
字符串拼接 url 需要处理 encode 或者 decode 的情况,还有对于 ?和 # 的判断不对的话,很容易造成漏洞或者 Bug。
目前浏览器和 Node 都已经提供了标准的 URL 解析方法。
https://developer.mozilla.org...
BadCode
// 这段代码既没有对 key、value 做 encode
// 也没有考虑 url 中 # 出现的情况
const url = location.href;
if (url.indexOf('?') >= 0) {
return url + key + '=' + value;
} else {
return url + '?' + key + '=' + value;
}
GoodCode
// 使用标准的 URL 解析,风险会降低很多
const url = new URL(urlStr);
url.searchParams.set(key, value);
return url.toString();
说明:
我们应该期望 if 条件内是个「真」值,而不是一个「假」值。
第二种情况会导致代码不易理解。
解决办法参考 布尔逻辑
BadCode
// if 条件内期望的是一个「假」值
if (!(status !== Closed) { ... }
if (!(status !== Closed || type !== Array)) { ...}
GoodCode
if (status === Closed) { ... }
if (status === Closed && type === Array) { ... }
例外:if 条件里可以被 「且」(&&)逻辑拆分成多个子条件
说明:复杂的条件判断会让代码不易理解,逻辑上有漏洞的话容易引起 Bug。
解决办法:声明中间变量
BadCode
if (srcElem != dropElem && (srcElem.nextSibling || srcElem.nextElementSibling) != dropElem) {...}
if (selectedItem || (selectedEmployee && selectedEmployee.empId && selectedEmployee) || employee) { ... }
GoodCode
const nextSibling = srcElem.nextSibling || srcElem.nextElementSibling
if (srcElem != dropElem && nextSibling != dropElem ) {
...
}
// 复杂的逻辑判断可以通过 && 做拆分
if (
!Array.isArray(cur)
&& cur != null
&& typeof src[key] === 'object'
&& typeof cur === 'object'
) { ... }
说明:
人们阅读嵌套三元表达式时,容易混淆语法的优先级,进而导致理解错代码的含义。
对于这种情况,建议改成 if else。
如果是在 react render 里,则建议独立成函数。
BadCode
function render(props) {
const value = props.value;
return <>
{value < 10 ? value > 0 ? value : 200 - value : 100 - value}
</>;
}
GoodCode
function getValue(value) {
if (value < 10) {
if (value > 0) return value;
return 200 - value;
}
return 100 - value;
}
function render(props) {
const value = props.value;
return <>
{getValue(value)}
</>;
}
说明:过深的嵌套会导致理解困难。
解决办法:合并判断条件,或者独立成函数。
BadCode
if (status = Opened) {
if (type = 'array') {
if (code = Success) {
doSomething();
}
}
}
GoodCode
if (status = Opened && type = 'array' &&code = Success) {
doSomething();
}
来自:https://segmentfault.com/a/1190000018723993
一个系统可以维持5年,10年,甚至20年以上,但是代码和设计模式的生命周期非常短,当对一个解决方案使用不同的方法进行迭代的时候,通常只能维持数月,数日,甚至几分钟的时间
良好的编程习惯涉及到很多方面,但在软件行业内,大多数的公司或组织都不会把良好的编程习惯列为主要关注点。 例如,具有可读性和可维护性的代码比编写好的测试代码或使用正确的工具更有意义,前者的意义在于可以让代码更易于理解和修改。
减少嵌套会让代码可读性更好,同时也能更容易的找出bug,开发人员可以更快的迭代,程序也会越来越稳定。简化代码,让编程更轻松!
Google为了那些还不熟悉代码规范的人发布了一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。代码规范并不是一种编写正确JavaScript代码的规则,而是为了保持源代码编写模式一致的一种选择。
程序员似乎忘记了软件的真正目的,那就是解决现实问题。您编写的代码的目的是为了创造价值并使现有世界变得更美好,而不是满足您对自我世界应该是什么的以自我为中心的观点。有人说:如果你拥有的只是一把锤子,那么一切看起来都像钉子一样
TinyMCE是一个轻量级的基于浏览器的所见即所得编辑器,由JavaScript写成。它对IE6+和Firefox1.5+都有着非常良好的支持。功能方强大,并且功能配置灵活简单。另一特点是加载速度非常快的。
函数式编程对应的是命令式编程, 函数式编程的核心当然是对函数的运用. 而高阶函数(Higher-order)是实现函数式编程的基本要素。高阶函数可以将其他函数作为参数或者返回结果。所以JS天生就支持函数式编程
朋友发表了一条说说:入职新公司,从重构代码到放弃”,我就问他怎么了?他说,刚进一家新公司,接手代码太烂,领导让我先熟悉业务逻辑,然后去修复之前项目中遗留的bug,实在不行就重构
页面实现关键词高亮显示:在项目期间遇到一个需求,就是搜索关键词时需要高亮显示,主要通过正则匹配来实现页面关键词高亮显示。在搜索结果中高亮显示关键词:有一组关键词数组,在数组中筛选出符合关键字的内容并将关键字高亮
软件工程学什么? 学计算机,写程序,做软件,当程序员。听说学计算机很辛苦? 是的,IT行业加班现象严重。在计算机世界里,技术日新月异,自学能力是程序员最重要的能力之一。选了这个专业,就要时刻保持好奇心和技术嗅觉,不能只满足于完成课内作业。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!