「为什么代码要整洁?」——代码整洁度对于项目质量的影响,让我们通过这边文章来教你js和ts的代码整洁技巧,让你的项目更出众(下)

简介: 警示作用,解释此处不能修改的原因。

警示作用,解释此处不能修改的原因。

// hack: 由于XXX历史原因,只能调度一下。
setTimeout(doSomething, 0)


TODO注释,记录下应该做但还没做的工作。另一个好处,提前写好命名,可以帮助后来者统一命名风格。

class Comment {
 // todo: 删除功能后期实现
 delete() {}
}


没用的代码直接删除,不要注释,反正git提交历史记录可以找回。

// bad: 如下,重写了一遍两数之和的实现方式
// const twoSum = function(nums, target) {
//     for(let i = 0;i<nums.length;i++){
//         for(let j = i+1;j<nums.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 []
};


避免循规式注释,不要求每个函数都要求jsdoc,jsdoc一般是用在公共代码上。

// bad or good?
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
 const twoSum = function(nums, target) {}


对象


多使用getter和setter(getXXX和setXXX)。好处:


在set时方便验证。


可以添加埋点,和错误处理。


可以延时加载对象的属性。


// good
function makeBankAccount() {
 let balance = 0;
 function getBalance() {
   return balance;
 }
 function setBalance(amount) {
   balance = amount;
 }
 return {
   getBalance,
   setBalance
 };
}
const account = makeBankAccount();
account.setBalance(100);



使用私有成员。对外隐藏不必要的内容。

// bad
  const 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
  // good
  function 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
  }
  // good
  class 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,其实应该是20
     rectangle.render(area);
   });
  }
  const rectangles = [new Rectangle(), new Rectangle(), new Square()];// 这里替换了
  renderLargeRectangles(rectangles);
  // good: 取消正方形和长方形继承关系,都继承Shape
  class 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) - 定义是客户不应被迫使用对其而言无用的方法或功能。常见的就是让一些参数变成可选的。


// bad
 class Dog {
   constructor(options) {
     this.options = options;
   }
   run() {
     this.options.run(); // 必须传入 run 方法,不然报错
   }
 }
 const dog = new Dog({}); // Uncaught TypeError: this.options.run is not a function
 dog.run()
 // good
 class Dog {
   constructor(options) {
     this.options = options;
   }
   run() {
   if (this.options.run) {
     this.options.run();
     return;
   }
     console.log('跑步');
   }
 }


依赖倒置原则(DIP) - 程序要依赖于抽象接口(可以理解为入参),不要依赖于具体实现。这样可以减少耦合度。


// bad
 class OldReporter {
   report(info) {
     // ...
   }
 }
 class Message {
   constructor(options) {
     // ...
     // BAD: 这里依赖了一个实例,那你以后要换一个,就麻烦了
     this.reporter = new OldReporter();
   }
   share() {
     this.reporter.report('start share');
     // ...
   }
 }
 // good
 class 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工具,这里就不展开说了。


最后


接受第一次愚弄

让程序一开始就做到整洁,并不是一件很容易的事情。不要强迫症一样地反复更改代码,因为工期有限,没那么多时间。等到下次需求更迭,你发现到代码存在的问题时,再改也不迟。


入乡随俗

每个公司、项目的代码风格是不一样的,会有与本文建议不同的地方。如果你接手了一个成熟的项目,建议按照此项目的风格继续写代码(不重构的话)。因为形成统一的代码风格也是一种代码整洁。


相关文章
|
19天前
|
JavaScript
短小精悍的js代码
【10月更文挑战第17天】
118 58
|
7天前
|
JavaScript
原生js炫酷随机抽奖中奖效果代码
原生js随机抽奖是一个炫酷的根据数据随机抽奖的代码,该网页可进行随机抽取一个数据,页面动画高科技、炫酷感觉的随机抽奖效果,简单好用,欢迎下载!
19 3
原生js炫酷随机抽奖中奖效果代码
|
12天前
|
JavaScript 前端开发 开发者
如何在 Visual Studio Code (VSCode) 中使用 ESLint 和 Prettier 检查代码规范并自动格式化 Vue.js 代码,包括安装插件、配置 ESLint 和 Prettier 以及 VSCode 设置的具体步骤
随着前端开发技术的快速发展,代码规范和格式化工具变得尤为重要。本文介绍了如何在 Visual Studio Code (VSCode) 中使用 ESLint 和 Prettier 检查代码规范并自动格式化 Vue.js 代码,包括安装插件、配置 ESLint 和 Prettier 以及 VSCode 设置的具体步骤。通过这些工具,可以显著提升编码效率和代码质量。
136 4
|
13天前
|
JSON 移动开发 数据格式
html5+css3+js移动端带歌词音乐播放器代码
音乐播放器特效是一款html5+css3+js制作的手机移动端音乐播放器代码,带歌词显示。包括支持单曲循环,歌词显示,歌曲搜索,音量控制,列表循环等功能。利用json获取音乐歌单和歌词,基于html5 audio属性手机音乐播放器代码。
64 6
|
10天前
|
JavaScript 前端开发 开发者
如何在 Visual Studio Code (VSCode) 中使用 ESLint 和 Prettier 检查代码规范并自动格式化 Vue.js 代码
随着前端开发技术的快速发展,代码规范和格式化工具变得尤为重要。本文介绍如何在 Visual Studio Code (VSCode) 中使用 ESLint 和 Prettier 检查代码规范并自动格式化 Vue.js 代码。通过安装和配置这些工具,可以确保代码风格一致,提高代码质量和可读性。
36 1
|
19天前
|
JavaScript 测试技术 API
跟随通义灵码一步步升级vue2(js)项目到vue3版本
Vue 3 相较于 Vue 2 在性能、特性和开发体验上都有显著提升。本文介绍了如何利用通义灵码逐步将 Vue 2 项目升级到 Vue 3,包括备份项目、了解新特性、选择升级方式、升级依赖、迁移组件和全局 API、调整测试代码等步骤,并提供了注意事项和常见问题的解决方案。
|
JSON JavaScript 前端开发
JavaScript 项目构建工具 Grunt 实践:合并文件
   Grunt 是一个基于任务的 JavaScript 项目命令行构建工具,运行于 Node.js 平台。Grunt 能够从模板快速创建项目,合并、压缩和校验 CSS & JS 文件,运行单元测试以及启动静态服务器。
1045 0
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
92 2
下一篇
无影云桌面