「为什么代码要整洁?」——代码整洁度对于项目质量的影响,让我们通过这边文章来教你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工具,这里就不展开说了。


最后


接受第一次愚弄

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


入乡随俗

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


相关文章
|
15天前
|
JavaScript 前端开发 测试技术
在 golang 中执行 javascript 代码的方案详解
本文介绍了在 Golang 中执行 JavaScript 代码的四种方法:使用 `otto` 和 `goja` 嵌入式 JavaScript 引擎、通过 `os/exec` 调用 Node.js 外部进程以及使用 WebView 嵌入浏览器。每种方法都有其适用场景,如嵌入简单脚本、运行复杂 Node.js 脚本或在桌面应用中显示 Web 内容。
50 15
在 golang 中执行 javascript 代码的方案详解
|
26天前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
25 1
JavaScript中的原型 保姆级文章一文搞懂
|
1月前
|
JavaScript
原生js炫酷随机抽奖中奖效果代码
原生js随机抽奖是一个炫酷的根据数据随机抽奖的代码,该网页可进行随机抽取一个数据,页面动画高科技、炫酷感觉的随机抽奖效果,简单好用,欢迎下载!
45 3
原生js炫酷随机抽奖中奖效果代码
|
23天前
|
JavaScript 前端开发 安全
JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择
本文深入探讨了JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择。JavaScript以其灵活性和广泛的生态支持著称,而TypeScript通过引入静态类型系统,提高了代码的可靠性和可维护性,特别适合大型项目。文章还讨论了结合使用两种语言的优势,以及如何根据项目需求和技术背景做出最佳选择。
42 4
|
25天前
|
CDN
如何在项目中使用Moment.js库?
如何在项目中使用Moment.js库?
|
19天前
|
JSON JavaScript 关系型数据库
node.js连接GBase 8a 数据库 并进行查询代码示例
node.js连接GBase 8a 数据库 并进行查询代码示例
|
22天前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
17 0
|
前端开发 JavaScript
8 种技巧让你编写更简洁的 JavaScript 代码
8 种技巧让你编写更简洁的 JavaScript 代码
255 0
8 种技巧让你编写更简洁的 JavaScript 代码
|
Web App开发 JavaScript 前端开发