哈尔滨的网站建设公司哪家好,平面设计的网站,域客式单页网站能申请域名吗,云南住房和城乡建设厅网站首页为什么代码要整洁#xff1f;
代码质量与整洁度成正比。有的团队在赶工期的时候#xff0c;不注重代码的整洁#xff0c;代码写的越来越糟糕#xff0c;项目越来越混乱#xff0c;生产力也跟着下降#xff0c;那就必须找更多人来提高生产力#xff0c;开发成本越来越高…为什么代码要整洁
代码质量与整洁度成正比。有的团队在赶工期的时候不注重代码的整洁代码写的越来越糟糕项目越来越混乱生产力也跟着下降那就必须找更多人来提高生产力开发成本越来越高。
整洁的代码是怎样的
清晰表达意图、消除重复、简单抽象、能通过测试。 换句话说具有可读性、可重用性和可重构性。
命名 名副其实不使用缩写、不使用让人误解的名称不要让人推测。 // bad: 啥const yyyymmdstr moment().format(YYYY/MM/DD);// bad: 缩写const cD moment().format(YYYY/MM/DD);// good:const currentDate moment().format(YYYY/MM/DD); const locations [Austin, New York, San Francisco];// bad推测l是locations的项locations.forEach(l doSomeThing(l));// goodlocations.forEach(location doSomeThing(location)); 使用方便搜索的名称避免硬编码对数据用常量const记录。 // bad: 86400000指的是setTimeout(goToWork, 86400000);// good: 86400000是一天的毫秒数const MILLISECONDS_PER_DAY 60 * 60 * 24 * 1000;setTimeout(goToWork, MILLISECONDS_PER_DAY); 类名应该是名词方法名应该是动词。 // badfunction visble() {}// goodfunction getVisble() {} 多个变量属于同一类型的属性那就他们整合成一个对象。同时省略多余的上下文。
// bad可以整合
const carMake Honda,
const carModel Accord,
const carColor Blue,// bad: 多余上下文
const Car {carMake: Honda,carModel: Accord,carColor: Blue,
};// good
const Car {make: Honda,model: Accord,color: Blue,
};
其他 不要写多余的废话比如theMessage的the可以删除。 统一术语。比如通知一词不要一会在叫notice一会叫announce。 用读得通顺的词语。比如getElementById就比 useIdToGetElement好读。
函数方法 删除重复的代码dont repeat yourself。很多地方可以注意dry比如偷懒复制了某段代码、try...catch或条件语句写了重复的逻辑。 // badtry {doSomeThing();clearStack();} catch (e) {handleError(e);clearStack();}// goodtry {doSomeThing();} catch (e) {handleError(e);} finally {clearStack();} 形参不超过三个对测试函数也方便。多了就使用对象参数。 同时建议使用对象解构语法有几个好处 1. 能清楚看到函数签名有哪些熟悉
2. 可以直接重新命名
3. 解构自带克隆防止副作用
4. Linter检查到函数未使用的属性。// bad
function createMenu(title, body, buttonText, cancellable) {}// good
function createMenu({ title, body, buttonText, cancellable }) {} 函数只做一件事代码读起来更清晰函数就能更好地组合、测试、重构。
// bad: 处理了输入框的change事件并创建文件的切片并保存相关信息到localStorage
function handleInputChange(e) {const file e.target.files[0];// --- 切片 ---const chunkList [];let cur 0;while (cur file.size) {chunkList.push({chunk: file.slice(cur, cur size)});cur size;}// --- 保存信息到localstorage ---localStorage.setItem(file, file.name);localStorage.setItem(chunkListLength, chunkList.length);
}// good: 将三件事分开写同时自顶而下读很舒适
function handleInputChange(e) {const file e.target.files[0];const chunkList createChunk(file);saveFileInfoInLocalStorage(file, chunkList);
}
function createChunk(file, size SLICE_SIZE) {const chunkList [];let cur 0;while (cur file.size) {chunkList.push({chunk: file.slice(cur, cur size)});cur size;}return chunkList
}
function saveFileInfoInLocalStorage(file, chunkList) {localStorage.setItem(file, file.name);localStorage.setItem(chunkListLength, chunkList.length);
} 自顶向下地书写函数人们都是习惯自顶向下读代码如为了执行A需要执行B为了执行B需要执行C。如果把A、B、C混在一个函数就很难读了。(看前一个的例子)。 不使用布尔值来作为参数遇到这种情况时一定可以拆分函数。 // badfunction createFile(name, temp) {if (temp) {fs.create(./temp/${name});} else {fs.create(name);}}// goodfunction createFile(name) {fs.create(name);}function createTempFile(name) {createFile(./temp/${name});} 避免副作用。 副作用的缺点出现不可预期的异常比如用户对购物车下单后网络差而不断重试请求这时如果添加新商品到购物车就会导致新增的商品也会到下单的请求中。 集中副作用遇到不可避免的副作用时候比如读写文件、上报日志那就在一个地方集中处理副作用不要在多个函数和类处理副作用。 其它注意的地方 常见就是陷阱就是对象之间共享了状态使用了可变的数据类型比如对象和数组。对于可变的数据类型使用immutable等库来高效克隆。 避免用可变的全局变量。 // bad注意到cart是引用类型const addItemToCart (cart, item) {cart.push({ item, date: Date.now() });};// goodconst addItemToCart (cart, item) {return [...cart, { item, date: Date.now() }];}; 封装复杂的判断条件提高可读性。 // badif (!(obj obj ! null typeof obj[Symbol.iterator] function)) {throw new Error(params is not iterable)}// goodconst isIterable obj obj ! null typeof obj[Symbol.iterator] function;if (!isIterable(promises)) {throw new Error(params is not iterable)} 在方法中有多条件判断时候为了提高函数的可扩展性考虑下是不是可以使用能否使用多态性来解决。 // 地图接口可能来自百度也可能来自谷歌const googleMap {show: function (size) {console.log(开始渲染谷歌地图, size));}};const baiduMap {render: function (size) {console.log(开始渲染百度地图, size));}};// bad: 出现多个条件分支。如果要加一个腾讯地图就又要改动renderMap函数。function renderMap(type) {const size getSize();if (type google) {googleMap.show(size);} else if (type baidu) {baiduMap.render(size);}};renderMap(google)// good实现多态处理。如果要加一个腾讯地图不需要改动renderMap函数。// 细节函数作为一等对象的语言中作为参数传递也会返回不同的执行结果也是“多态性”的体现。function renderMap (renderMapFromApi) {const size getSize();renderMapFromApi(size);}renderMap((size) googleMap.show(size)); 其他 如果用了TS没必要做多余类型判断。 注释 一般代码要能清晰的表达意图只有遇到复杂的逻辑时才注释。 // good由于函数名已经解释不清楚函数的用途了所以注释里说明。// 在nums数组中找出 和为目标值 target 的两个整数并返回它们的数组下标。const twoSum function(nums, target) {let map new Map()for (let i 0; i nums.length; i) {const item nums[i];const index map.get(target - item)if (index ! undefined){return [index, i]}map.set(item, i)}return []};// bad加了一堆废话const twoSum function(nums, target) {// 声明map变量let map new Map()// 遍历for (let i 0; i nums.length; i) {const item nums[i];const index map.get(target - item)// 如果下标为空if (index ! undefined){return [index, i]}map.set(item, i)}return []};警示作用解释此处不能修改的原因。 // hack: 由于XXX历史原因只能调度一下。setTimeout(doSomething, 0) TODO注释记录下应该做但还没做的工作。另一个好处提前写好命名可以帮助后来者统一命名风格。 class Comment {// todo: 删除功能后期实现delete() {}} 没用的代码直接删除不要注释反正git提交历史记录可以找回。 // bad: 如下重写了一遍两数之和的实现方式// const twoSum function(nums, target) {// for(let i 0;inums.length;i){// for(let j i1;jnums.length;j){// if (nums[i] nums[j] target) {// return [i,j]// }// }// }// };const twoSum function(nums, target) {let map new Map()for (let i 0; i nums.length; i) {const item nums[i];const index map.get(target - item)if (index ! undefined){return [index, i]}map.set(item, i)}return []}; 避免循规式注释不要求每个函数都要求jsdocjsdoc一般是用在公共代码上。 // bad or good?/*** param {number[]} nums* param {number} target* return {number[]}*/const twoSum function(nums, target) {}对象 多使用getter和settergetXXX和setXXX。好处 在set时方便验证。 可以添加埋点和错误处理。 可以延时加载对象的属性。 // goodfunction makeBankAccount() {let balance 0;function getBalance() {return balance;}function setBalance(amount) {balance amount;}return {getBalance,setBalance};}const account makeBankAccount();account.setBalance(100); 使用私有成员。对外隐藏不必要的内容。 // badconst Employee function(name) {this.name name;};Employee.prototype.getName function getName() {return this.name;};const employee new Employee(John Doe);delete employee.name;console.log(employee.getName()); // undefined// goodfunction makeEmployee(name) {return {getName() {return name;}};}
类
solid 单一职责原则 (SRP) - 保证“每次改动只有一个修改理由”。因为如果一个类中有太多功能并且您修改了其中的一部分则很难预期改动对其他功能的影响。 // bad设置操作和验证权限放在一起了class UserSettings {constructor(user) {this.user user;}changeSettings(settings) {if (this.verifyCredentials()) {// ...}}verifyCredentials() {// ...}}// good: 拆出验证权限的类class UserAuth {constructor(user) {this.user user;}verifyCredentials() {// ...}}class UserSettings {constructor(user) {this.user user;this.auth new UserAuth(user);}changeSettings(settings) {if (this.auth.verifyCredentials()) {// ...}}} 开闭原则 (OCP) - 对扩展放开但是对修改关闭。在不更改现有代码的情况下添加新功能。比如一个方法因为有switch的语句每次出现新增条件时就要修改原来的方法。这时候不如换成多态的特性。 // bad: 注意到fetch用条件语句了不利于扩展class AjaxAdapter extends Adapter {constructor() {super();this.name ajaxAdapter;}}class NodeAdapter extends Adapter {constructor() {super();this.name nodeAdapter;}}class HttpRequester {constructor(adapter) {this.adapter adapter;}fetch(url) {if (this.adapter.name ajaxAdapter) {return makeAjaxCall(url).then(response {// transform response and return});} else if (this.adapter.name nodeAdapter) {return makeHttpCall(url).then(response {// transform response and return});}}}function makeAjaxCall(url) {// request and return promise}function makeHttpCall(url) {// request and return promise}// goodclass AjaxAdapter extends Adapter {constructor() {super();this.name ajaxAdapter;}request(url) {// request and return promise}}class NodeAdapter extends Adapter {constructor() {super();this.name nodeAdapter;}request(url) {// request and return promise}}class HttpRequester {constructor(adapter) {this.adapter adapter;}fetch(url) {return this.adapter.request(url).then(response {// transform response and return});}} 里氏替换原则 (LSP) 两个定义 如果S是T的子类则T的对象可以替换为S的对象而不会破坏程序。 所有引用其父类对象方法的地方都可以透明的替换为其子类对象。 也就是保证任何父类对象出现的地方用其子类的对象来替换不会出错。下面的例子是经典的正方形、长方形例子。 // bad: 用正方形继承了长方形class Rectangle {constructor() {this.width 0;this.height 0;}setColor(color) {// ...}render(area) {// ...}setWidth(width) {this.width width;}setHeight(height) {this.height height;}getArea() {return this.width * this.height;}}class Square extends Rectangle {setWidth(width) {this.width width;this.height width;}setHeight(height) {this.width height;this.height height;}}function renderLargeRectangles(rectangles) {rectangles.forEach(rectangle {rectangle.setWidth(4);rectangle.setHeight(5);const area rectangle.getArea(); // BAD: 返回了25其实应该是20rectangle.render(area);});}const rectangles [new Rectangle(), new Rectangle(), new Square()];// 这里替换了renderLargeRectangles(rectangles);// good: 取消正方形和长方形继承关系都继承Shapeclass Shape {setColor(color) {// ...}render(area) {// ...}}class Rectangle extends Shape {constructor(width, height) {super();this.width width;this.height height;}getArea() {return this.width * this.height;}}class Square extends Shape {constructor(length) {super();this.length length;}getArea() {return this.length * this.length;}}function renderLargeShapes(shapes) {shapes.forEach(shape {const area shape.getArea();shape.render(area);});}const shapes [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];renderLargeShapes(shapes); 接口隔离原则 (ISP) - 定义是客户不应被迫使用对其而言无用的方法或功能。常见的就是让一些参数变成可选的。 // badclass Dog {constructor(options) {this.options options;}run() {this.options.run(); // 必须传入 run 方法不然报错}}const dog new Dog({}); // Uncaught TypeError: this.options.run is not a functiondog.run()// goodclass Dog {constructor(options) {this.options options;}run() {if (this.options.run) {this.options.run();return;}console.log(跑步);}} 依赖倒置原则DIP - 程序要依赖于抽象接口(可以理解为入参)不要依赖于具体实现。这样可以减少耦合度。 // badclass OldReporter {report(info) {// ...}}class Message {constructor(options) {// ...// BAD: 这里依赖了一个实例那你以后要换一个就麻烦了this.reporter new OldReporter();}share() {this.reporter.report(start share);// ...}}// goodclass Message {constructor(options) {// reporter 作为选项可以随意换了this.reporter this.options.reporter;}share() {this.reporter.report(start share);// ...}}class NewReporter {report(info) {// ...}}new Message({ reporter: new NewReporter });
其他 优先使用 ES2015/ES6 类而不是 ES5 普通函数。 多使用方法链。 多使用组合而不是继承。
错误处理 不要忽略捕获的错误。而要充分对错误做出反应比如console.error()到控制台提交错误日志提醒用户等操作。 不要漏了catch promise中的reject。
格式
可以使用eslint工具这里就不展开说了。
最后
接受第一次愚弄
让程序一开始就做到整洁并不是一件很容易的事情。不要强迫症一样地反复更改代码因为工期有限没那么多时间。等到下次需求更迭你发现到代码存在的问题时再改也不迟。