查看原文
其他

前端小白看过来,快速让你的代码变得优雅

前端大全
2024-08-23

推荐关注↓

作者:尘落笔记

https://juejin.cn/post/7235547967113429029

本篇汇总了平时在工作开发中常遇到的业务逻辑的优雅写法,也汇总了自己还是新人时,拿到一个业务不知道怎么下手的痛点,依稀记得那时候总感觉自己写的代码不规范。

写完之后,感觉还是美好的,又学到东西了。

正文开始

为什么要写一手优雅的代码?

采用简洁的语法和结构,遵循一致的命名规范,具有良好的代码组织和注释,同时表达了代码的意图和逻辑。

主要有以下几个优点:

  1. 可读性:易于阅读和理解。清晰的命名、简洁的语法和良好的代码结构可以使代码的意图更加明确,降低理解代码的难度,提高代码的可读性。

  2. 可维护性:易于维护。当代码逻辑清晰、结构简洁时,开发者可以更快速地定位和修复bug,进行功能扩展或修改。同时,可读性高的代码也有助于后续的代码重构和优化。

  3. 可扩展性:更具有扩展性和灵活性。清晰的代码结构和简洁的代码风格使得添加新功能、修改现有功能或扩展代码更加容易。此外,简洁的代码往往更少依赖于特定的实现细节,提供了更多的灵活性和可替换性。

  4. 错误减少和调试时间:更容易写出正确的逻辑,减少了出错的概率。同时,当代码出现问题时,优雅简洁的代码也更容易进行调试和定位错误。

  5. 性能优化:简洁的代码通常更高效,减少了不必要的计算和资源消耗。精简的代码结构和算法可以提高代码的执行效率和性能

基本功

1. 使用箭头函数简化函数定义

// 传统函数定义
function add(a, b) {
    return a + b;
  }
  
  // 箭头函数简化
  const add = (a, b) => a + b;

2. 使用解构赋值简化变量声明

// 传统变量声明
const firstName = person.firstName;
const lastName = person.lastName;

// 解构赋值简化
const { firstName, lastName } = person;

3. 使用模板字面量进行字符串拼接

// 传统字符串拼接
const greeting = 'Hello, ' + name + '!';

// 模板字面量简化
const greeting = `Hello, ${name}!`;

4. 使用展开运算符进行数组和对象操作

// 合并数组
const combined = [...array1, ...array2];

// 复制对象
const clone = { ...original };

5. 使用数组的高阶方法简化循环和数据操作

// 遍历数组并返回新数组
const doubled = numbers.map(num => num * 2);

// 过滤数组
const evens = numbers.filter(num => num % 2 === 0);

6. 使用条件运算符简化条件判断

// 传统条件判断
let message;
if (isSuccess) {
  message = 'Operation successful';
else {
  message = 'Operation failed';
}

// 条件运算符简化
const message = isSuccess ? 'Operation successful' : 'Operation failed';

7. 使用对象解构和默认参数简化函数参数

// 传统参数设置默认值
function greet(name) {
    const finalName = name || 'Guest';
    console.log(`Hello, ${finalName}!`);
  }
  
  // 对象解构和默认参数简化
  function greet({ name = 'Guest' }) {
    console.log(`Hello, ${name}!`);
  }

8. 使用函数式编程概念如纯函数和函数组合

// 纯函数
function add(a, b) {
    return a + b;
  }
  
  // 函数组合
  const multiplyByTwo = value => value * 2;
  const addFive = value => value + 5;
  const result = addFive(multiplyByTwo(3));

9. 使用对象字面量简化对象的创建和定义

// 传统对象创建
const person = {
    firstName'John',
    lastName'Doe',
    age30,
  };
  
  // 对象字面量简化
  const firstName = 'John';
  const lastName = 'Doe';
  const age = 30;
  const person = { firstName, lastName, age };

10. 使用适当的命名和注释来提高代码可读性

// 不好的
const x = 10// 设置x的值为10
function a(b) {
  return b * 2// 返回b的两倍
}

// 好的
const speed = 10// 设置速度为10
function double(value) {
  return value * 2// 返回输入值的两倍

接着奏乐接着舞(实战篇)!!!

image.png

实战

1. 优雅的写条件判断代码

简单的条件判断逻辑用if else 或者 三元运算符, 一眼看过去还能知道说的啥,但是大量的if else和叠加在一起的三元运算符就是接盘侠的噩梦~~~

给大家上一个三元运算符叠加的案例,我是真实在项目中遇到过,cpu直接干爆~~~

<view>{{status===1?'成功': status===2 ? '失败' : status===3 ? '进行中' : '未开始' }}</view>

大概是这样的,具体的项目代码不好放在这里,小伙伴们意会就行。

复杂逻辑推荐使用对象Map写法,符合人脑的逻辑,可读性高,看着舒服~~~

1,普通的if else

let txt = '';
if (falg) {
    txt = "成功"
else {
    txt = "失败"
}

2,三元运算符

let txt = flag ? "成功" : "失败";

3,多个if else


// param {status} status 活动状态:1:成功 2:失败 3:进行中 4:未开始
let txt = '';
if (status == 1) {
    txt = "成功";
else if (status == 2) {
    txt = "失败";
else if (status == 3) {
    txt = "进行中";
else {
    txt = "未开始";
}

4,switch case

let txt = '';
switch (status) {
    case 1:
        txt = "成功";
        break;
    case 2:
        txt = "成功";
        break;
    case 3:
        txt = "进行中";
        break;
    default:
        txt = "未开始";
}

5,对象写法

const statusMap = {
    1"成功",
    2"失败",
    3"进行中",
    4"未开始"
}
//调用直接 statusMapp[status] 

5,Map写法

const actions = new Map([
    [1"成功"],
    [2"失败"],
    [3"进行中"],
    [4"未开始"]
])
// 调用直接 actions.get(status) 

2. 封装条件语句

同上,if里的条件越多越不利于接盘侠的维护,不利于人脑的理解,一眼看过去又是一堆逻辑。多个逻辑应该化零为整

大脑:'别来碰我,让我静静'

// 不好的
if (fsm.state === 'fetching' && isEmpty(listNode)) {
    // ...
  }
  // 好的
  shouldShowSpinner(fsm, listNode){
      return fsm.state === 'fetching' && isEmpty(listNode)
  }
  if(shouldShowSpinner(fsm, listNode)){
      //...doSomething
  }

3. 函数应该只做一件事

函数式写法推崇柯里化, 一个函数一个功能,可拆分可组装。

// 不好的
function createFile(name, temp) {
    if (temp) {
      fs.create(`./temp/${name}`);
   } else {
      fs.create(name);
   }
  }
  // 好的
  function createFile(name) {
    fs.create(name);
  }
  function createTempFile(name) {
      createFile(`./temp/${name}`)
  }

再来一个栗子

函数要做的事情如下:

  • 遍历clients数组

  • 遍历过程中,通过lookup函数得到一个新的对象clientRecord

  • 判断clientRecord对象中isActive函数返回的是不是true,

    • isActive函数返回true,执行email函数并把当前成员带过去
// 不好的
function emailClients(clients) {
    clients.forEach((client) => {
      const clientRecord = database.lookup(client);
      if (clientRecord.isActive()) {
        email(client);
     }
   });
  }
  // 好的
  function emailClients(clients) {
      clients
        .filter(isClientRecord)
        .forEach(email)
  }
  function isClientRecord(client) {
      const clientRecord = database.lookup(client);
      return clientRecord.isActive()
  }

上面不好的栗子一眼看过去是不是感觉一堆代码在那,一时半会甚至不想去看了。

好的栗子,是不是逻辑很清晰,易读。

  • 巧用filter函数,把filter的回调单开一个函数进行条件处理,返回符合条件的数据

  • 符合条件的数据再巧用forEach,执行email函数

4. Object.assign给默认对象赋默认值

// 不好的
const menuConfig = {
    titlenull,
    body'Bar',
    buttonTextnull,
    cancellabletrue
  };
  function createMenu(config) {
    config.title = config.title || 'Foo';
    config.body = config.body || 'Bar';
    config.buttonText = config.buttonText || 'Baz';
    config.cancellable = config.cancellable === undefined ?
    config.cancellable : true;
  }
  createMenu(menuConfig);
  // 好的
  const menuConfig = {
    title'Order',
    buttonText'Send',
    cancellabletrue
  };
  function createMenu(config) {
    Object.assign({
      title'Foo',
      body'Bar',
      buttonText'Baz',
      cancellabletrue 
    }, config)
  }
  createMenu(menuConfig);

5. 函数参数两个以下最好

说一千道一万,就是为了优雅,就是为了可读性好。

// 不好的
function createMenu(title, body, buttonText, cancellable) {
    // ...
  }
  // 好的
  const menuConfig = {
    title'Foo',
    body'Bar',
    buttonText'Baz',
    cancellabletrue
  };
  function createMenu(config){
      // ...
  }
  createMenu(menuConfig)

6. 使用解释性的变量

省流,用了扩展运算符,为了可读性(saveCityZipCode(city, zipCode)可读性很好,知道参数是干嘛的)

// 不好的
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2]);
// 好的
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
cosnt [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode)

7. 想对类中的属性进行更多自定义取/增/改的操作时,使用set/get

第一次见这个写法,不知道是啥意思的小伙伴,把他当成vue2中的defineProperty

Object.defineProperty(data1,'age',{
    set:function(newAge){
        console.log(this.name+'现在'+newAge+'岁')
    },
    get:function(){
     return 18;
 }
}) 

是一个意思,赋值的时候set会被触发,取值的时候get会被触发。

巧用自带属性,提升性能。

class BankAccount {
    constructor(balance = 1000) {
      this._balance = balance;
   }
    // It doesn't have to be prefixed with `get` or `set` to be a
  //getter/setter
    set balance(amount) {
      console.log('set')
      if (verifyIfAmountCanBeSetted(amount)) {
        this._balance = amount;
     }
   }
    get balance() {
      console.log('get')
      return this._balance;
   }
    verifyIfAmountCanBeSetted(val) {
      // ...
   }
  }
  const bankAccount = new BankAccount();
  // Buy shoes...
  bankAccount.balance -= 100;
  // Get balance
  let balance = bankAccount.balance;

8. 让对象拥有私有成员-通过闭包来实现

闭包天生就是做私有化的

// 不好的
const Employee = function(name) {
    this.name = name;
  };
  Employee.prototype.getName = function getName() {
    return this.name;
  };
  const employee = new Employee('John Doe');
  console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
  delete employee.name;
  console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined
  // 好的
  const Employee = function(name){
      this.name = name;
      this.getName = function(){
          return this.name
      }
  }
  const employee = new Employee('John Doe');
  console.log(`Employee name: ${employee.getName()}`); // Employee name: John Doe
  delete employee.name;
  console.log(`Employee name: ${employee.getName()}`); // Employee name: undefined

第一个示例

优点:

  • 通过原型链共享方法,节省了内存空间。所有实例对象共享同一个 getName 方法,而不是每个实例对象都创建一个独立的方法。

缺点:

  • 在构造函数中无法直接定义私有属性或方法,所有属性和方法都会被暴露在原型链上

第二个示例

优点:

  • 可以在构造函数内部定义私有属性和方法,不会暴露在对象的原型链上,提供了更好的封装性。

缺点:

  • 每次创建实例对象时,都会创建一个独立的方法,每个实例对象都有自己的 getName 方法,占用更多的内存空间。

9. 使用方法链

链式写法也是代码优雅之道的重头戏。

ps:发明这个的程序员肯定是后端出身的,这种写法在PHP的CI框架中见过。

// 不好的
class Car {
    constructor() {
      this.make = 'Honda';
      this.model = 'Accord';
      this.color = 'white';
   }
    setMake(make) {
      this.make = make;
   }
    save() {
      console.log(this.make, this.model, this.color);
   }
  }
  const car = new Car();
  car.setMake('Ford');
  car.save();
  // 好的
  class Car {
    constructor() {
      this.make = 'Honda';
      this.model = 'Accord';
      this.color = 'white';
   }
    setMake(make) {
      this.make = make;
      // NOTE: return this是为了用链式写法
      return this;
   }
    save() {
      console.log(this.make, this.model, this.color);
      // NOTE:return this是为了用链式写法
      return this;
   }
  }
  const car = new Car()
   .setMake('Ford')
   .save();

看完上面的这么多栗子,小伙伴的思路是不是清晰了很多,在你们的项目里练练手吧。

你将收获的是同事们、 接盘侠们的感恩和自己内心的舒适!!!

- EOF -


加主页君微信,不仅前端技能+1

主页君日常还会在个人微信分享前端开发学习资源技术文章精选,不定期分享一些有意思的活动岗位内推以及如何用技术做业余项目

加个微信,打开一扇窗



推荐阅读  点击标题可跳转

1、自查,你的 React Hooks 够优雅吗?

2、前端性能调优的实际案例 - 小白都看的懂

3、小白入门级!webpack 基础、分包大揭秘


觉得本文对你有帮助?请分享给更多人

推荐关注「前端大全」,提升前端技能

点赞和在看就是最大的支持❤️

继续滑动看下一个
前端大全
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存