Clean Code JavaScript小记(一)

引言

Clean Code JavaScript 是一个开源项目,该项目旨在帮助 JavaScript 开发者编写可读性强、可重用且易于重构的代码。它不仅仅是一个风格指南,而是一个指导开发者如何编写高质量 JavaScript 代码的实用指南。在实际开发的过程中不必严格遵循其中每条细责,有时候少遵循一些反而会更好。视情况而定即可。本文则是挑选一些细则进行记录。

变量

使用可读性强的变量名或者方法名
js 复制代码
//反例
var a=10;
//正例
var age=10;
使用说明变量
arduino 复制代码
// 1.不需要过于压缩代码,可使用说明变量过渡逻辑,增强可读性。
显式优于隐式
js 复制代码
      let locations=['临安','萧山','淳安']
        // 反例
         locations.forEach(el=>{
             console.log(`location:${el}`)
         })

        // 正例 写location 比 el 可读性更好
        locations.forEach(location=>{
            console.log(`location:${location}`)
        })
避免无意义的判断
js 复制代码
   // 反例
      var breweryName;
      if (name) {
       breweryName = name;
    } else {
       breweryName = 'defaultValue';
       }
    // 正例
    var breweryName = name || 'defaultValue'

函数

限制函数参数 (理想状态下不超过2个);

应该避免三个以上参数的函数,通常情况下参数超过2个意味着函数功能过于复杂需要对函数功能进行拆分优化; 当确实有多个参数的时候考虑封装为一个对象;

js 复制代码
//反例
function createMenu(title, body, buttonText, cancellable) {
  ...
}
//正例
var menuConfig = {
  title: 'Foo',
  body: 'Bar',
  buttonText: 'Baz',
  cancellable: true
}

function createMenu(menuConfig) {
  ...
}
函数应该只做一层抽象

通过分层抽象,让代码更符合人类线性思维习惯,降低认知负担;避免高层描述和具体细节逻辑混合在一起 提升代码的可读性可维护性,简而言之就是"做什么"和"怎么做"的细节逻辑分开。

js 复制代码
// 反例
 async function handleSubmit(formData) {
   // 验证字段
   if (!formData.email.includes("@")) {
     alert("Invalid email");
     return;
   }

   // 发送请求
   const res = await fetch("/api/submit", {
     method: "POST",
     body: JSON.stringify(formData),
   });

   // 处理响应
   const result = await res.json();
   if (result.success) {
     window.location.href = "/success";
   }
 }
 
 //正例
  async function handleSubmit(formData) {
  if (!isFormValid(formData)) return;  // 高层
   const result = await submitForm(formData); // 高层
   if (result.success) redirectToSuccess(); // 高层
 }

// // 底层函数
 function isFormValid(formData) {
   if (!formData.email.includes("@")) {
     alert("Invalid email");
     return false;
   }
   return true;
 }

 async function submitForm(formData) {
   const res = await fetch("/api/submit", {
     method: "POST",
     body: JSON.stringify(formData),
   });
   return res.json();
 }
使用 Object.assign 设置默认对象
js 复制代码
let shop={
addrres:'地址',
openNingTime:null,
closingTime:null,
phone:'1578656589'
}
//反例 
function creatShop(config){
 config.title = config.addrres||'',
  config.openNingTime = config.openNingTime || '10:00'
  config.closingTime = config.openNingTime || '22:00'
 config.phone=config.phone||''
 }
 creatShop(shop)
 //正例
function creatShop(config) {
  config = Object.assign({
    addrres: '',
    openNingTime: '10:00',
    closingTime: '22:00',
    phone: ''
  }, config)
 creatShop(shop)
 
避免副作用

// 当函数产生了除了接受一个值或者返回一个值之外的行为时成为该函数产生的副作用。

js 复制代码
// 反例
var name='Ryan McDermott'
function splitName(){
    name=name.split(' ')
}
// 正例
function splitName(name){
    return name.split( ' ')
}
var name='Ryan McDermott'
var newName =splitName(name);
js 复制代码
采用函数式编程

其核心思想是通过纯函数、不可变数据和高阶函数等机制构建程序逻辑。与面向对象编程(OOP)不同,函数式编程更关注"如何描述问题"而非"如何操作步骤",从而提升代码的可维护性和可测试性。

js 复制代码
数组每个元素进行平方操作,命令式编程与函数式如下:

// 反例 
var array = [0, 1, 2, 3]
for(let i = 0; i < array.length; i++) {
    array[i] = Math.pow(array[i], 2)
}
// 正例
array.map(num=>Math.pow(num,2))
避免"否定情况"的判断
js 复制代码
//反例 
if(!show){

}
// 正例  
if(show){

}

正向判断的优点

1.可读性 正向条件通常比反向条件更直观。

2.减少错误 避免在编写否定条件时容易不小心颠倒逻辑。

3.简化维护正向逻辑更容易理解与维护。

避免复杂类型判断

js是弱类型语言,意味着函数可以接受任何类型参数。

js 复制代码
function travelToTex(vehicle){
    if (vehicle instanceof Bicycle) {
    vehicle.peddle(this.currentLocation, new Location('texas'));
  } else if (vehicle instanceof Car) {
    vehicle.drive(this.currentLocation, new Location('texas'));
  }
}
// 正例
function travelToTexas(vehicle) {
  vehicle.move(this.currentLocation, new Location('texas'));
}
避免多态类型判断
js 复制代码
//反例
function combine(val1, val2) {
  if (typeof val1 == "number" && typeof val2 == "number" ||
      typeof val1 == "string" && typeof val2 == "string") {
    return val1 + val2;
  } else {
    throw new Error('Must be of type String or Number');
  }
}

//对于多态类型判断建议使用TypeScript对类型增强校验。

对象和数据结构

使用 getters 和 setters

使用getter和setter对对象的访问进行控制,确保数据的安全性;s6之后可通过定义class类,字面量结合object.defineProperty方法, 使用proxy对象。

js 复制代码
class Person {
  constructor(firstName, lastName) {
    this._firstName = firstName;
    this._lastName = lastName;
  }
 
  // Getter
  get fullName() {
    return `${this._firstName} ${this._lastName}`;
  }
 
  // Setter
  set fullName(value) {
    if (typeof value !== 'string') {
      throw new TypeError('Full name must be a string');
    }
    const parts = value.split(' ');
    this._firstName = parts[0];
    this._lastName = parts[1];
  }
}
 
const person = new Person('John', 'Doe');
console.log(person.fullName); // John Doe
 
person.fullName = 'Jane Smith';
console.log(person.fullName); // Jane Smith


const person = {
  _firstName: 'John',
  _lastName: 'Doe'
};
 
Object.defineProperty(person, 'fullName', {
  get: function() {
    return `${this._firstName} ${this._lastName}`;
  },
  set: function(value) {
    if (typeof value !== 'string') {
      throw new TypeError('Full name must be a string');
    }
    const parts = value.split(' ');
    this._firstName = parts[0];
    this._lastName = parts[1];
  }
});
 
console.log(person.fullName); // John Doe
person.fullName = 'Jane Smith'; // Jane Smith
console.log(person.fullName); // Jane Smith

const person = {
  _firstName: 'John',
  _lastName: 'Doe'
};
 
const handler = {
  get(target, prop) {
    if (prop === 'fullName') {
      return `${target._firstName} ${target._lastName}`;
    } else {
      return Reflect.get(target, prop); // 默认行为,返回属性值或undefined
    }
  },
  set(target, prop, value) {
    if (prop === 'fullName') {
      if (typeof value !== 'string') {
        throw new TypeError('Full name must be a string');
      }
      const parts = value.split(' ');
      target._firstName = parts[0];
      target._lastName = parts[1];
      return true; // 表示成功设置值
    } else {
      return Reflect.set(target, prop, value); // 默认行为,设置属性值或返回false表示失败
    }
  }
};
 
const proxyPerson = new Proxy(person, handler);
console.log(proxyPerson.fullName); // John Doe
proxyPerson.fullName = 'Jane Smith'; // Jane Smith
console.log(proxyPerson.fullName); // Jane Smith
让对象拥有私有成员
js 复制代码
class Person {
    #id; // 私有字段
    #name; // 私有字段
    static #counter = 0; // 静态私有字段
 
    constructor(name) {
        this.#name = name; // 使用私有字段
        this.#id = ++Person.#counter; // 使用静态私有字段
    }
 
    getName() {
        return this.#name; // 访问私有字段
    }
}
 
const person1 = new Person('Alice');
console.log(person1.getName()); // Alice
console.log(person1.#id); // TypeError: Illegal access to private field '#id' of class 'Person' due to encapsulation
相关推荐
博客zhu虎康1 小时前
React Hooks 报错?一招解决useState问题
前端·javascript·react.js
灰海2 小时前
vue中通过heatmap.js实现热力图(多个热力点)热区展示(带鼠标移入弹窗)
前端·javascript·vue.js·heatmap·heatmapjs
码上暴富3 小时前
vue2迁移到vite[保姆级教程]
前端·javascript·vue.js
伍哥的传说4 小时前
Lodash-es 完整开发指南:ES模块化JavaScript工具库实战教程
大数据·javascript·elasticsearch·lodash-es·javascript工具库·es模块·按需导入
@菜菜_达4 小时前
Lodash方法总结
开发语言·前端·javascript
GISer_Jing4 小时前
低代码拖拽实现与bpmn-js详解
开发语言·javascript·低代码
YAY_tyy4 小时前
基于 Vue3 + VueOffice 的多格式文档预览组件实现(支持 PDF/Word/Excel/PPT)
前端·javascript·vue.js·pdf·word·excel
Yvonne爱编码4 小时前
AJAX入门-AJAX 概念和 axios 使用
前端·javascript·ajax·html·js
Pu_Nine_95 小时前
10 分钟上手 ECharts:从“能跑”到“生产级”的完整踩坑之旅
前端·javascript·echarts·css3·html5
li35747 小时前
React 核心 Hook 与冷门技巧:useReducer、useEffect、useRef 及 is 属性全解析
前端·javascript·react.js