LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

26个写出简洁优雅JavaScript代码的技巧

admin
2024年10月13日 22:47 本文热度 181

写在前面
在编程世界中,代码不仅仅是让事情正常运转。 它就像一件讲述故事的艺术品。 当代码干净时,它就像一个美丽的、精心制作的雕塑,既美观又运行良好。
但在急于按期完成任务的过程中,有时团队不会太注意保持代码的整洁。 这可能会导致项目变得混乱、复杂,变得更加难以开展。 随着情况变得更糟,生产力也会下降。 然后,公司需要引进更多的人来提供帮助,这使得一切都变得更加昂贵。
那么,干净的代码是什么样的呢? 它的代码易于理解,没有多余的部分,简单,并且可以通过测试。 换句话说,它是可读的、可重用的,并且在需要时易于更改。
为了帮助你编写出色的 JavaScript 代码,我将在今天的内容中与你分享 26 个写干净代码的技巧,这些技巧将指导你编写既优雅又高效的代码。
1. 变量
1.使用有意义且易于发音的变量名
// Badconst yyyymmdstr = moment().format("YYYY/MM/DD");
// Goodconst currentDate = moment().format("YYYY/MM/DD");

2. 同一类型的变量使用相同的词汇表

// BadgetUserInfo();getClientData();getCustomerRecord();
// GoodgetUser();

3. 使用可搜索的名称

我们将阅读比我们编写的更多的代码,我们编写的代码可读且可搜索,这一点很重要。

// Bad// What the heck is 86400000 for?setTimeout(blastOff, 86400000); 
// Good// Declare them as capitalized named constants.const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000;
setTimeout(blastOff, MILLISECONDS_PER_DAY);

4. 使用解释变量

// Badconst address = "One Infinite Loop, Cupertino 95014";const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;saveCityZipCode(  address.match(cityZipCodeRegex)[1],  address.match(cityZipCodeRegex)[2]);
// Goodconst address = "One Infinite Loop, Cupertino 95014";const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];saveCityZipCode(city, zipCode);

5. 避免心理映射

显式的比隐式的好。

// Badconst locations = ["Austin", "New York", "San Francisco"];locations.forEach(l => {  doStuff();  doSomeOtherStuff();  // ...  // ...  // ...  // Wait, what is `l` for again?  dispatch(l);});
// Goodconst locations = ["Austin", "New York", "San Francisco"];locations.forEach(location => {  doStuff();  doSomeOtherStuff();  // ...  // ...  // ...  dispatch(location);});

6. 不要添加不需要的上下文

如果您的类/对象名称告诉您一些信息,请不要在变量名称中重复该信息。

// Badconst Car = {  carMake: "Honda",  carModel: "Accord",  carColor: "Blue"};
function paintCar(car, color) {  car.carColor = color;}
// Goodconst Car = {  make: "Honda",  model: "Accord",  color: "Blue"};
function paintCar(car, color) {  car.color = color;}

7. 使用默认参数代替短路或条件

默认参数通常比短路更清晰。 请注意,如果您使用它们,您的函数将只为未定义的参数提供默认值。 其他“假”值(例如 ''、""、false、null、0 和 NaN)不会被默认值替换。

// Badfunction createMicrobrewery(name) {  const breweryName = name || "Hipster Brew Co.";  // ...}
// Goodfunction createMicrobrewery(name = "Hipster Brew Co.") {  // ...}

2. 功能

8. 函数参数(理想情况下为 2 个或更少)

限制函数参数的数量非常重要,因为它使测试函数变得更加容易。 超过三个会导致组合爆炸,您必须使用每个单独的参数来测试大量不同的情况。

// Badfunction createMenu(title, body, buttonText, cancellable) {  // ...}
createMenu("Foo", "Bar", "Baz", true);
//Goodfunction createMenu({ title, body, buttonText, cancellable }) {  // ...}
createMenu({  title: "Foo",  body: "Bar",  buttonText: "Baz",  cancellable: true});

9.函数应该做一件事

这是迄今为止软件工程中最重要的规则。 当函数做不止一件事时,它们就更难编写、测试和推理。 当您可以将一个函数隔离为一个操作时,就可以轻松重构它,并且您的代码读起来会更清晰。 如果您除了本指南之外没有任何其他内容,您将领先于许多开发人员。

// Badfunction emailClients(clients) {  clients.forEach(client => {    const clientRecord = database.lookup(client);    if (clientRecord.isActive()) {      email(client);    }  });}
// Goodfunction emailActiveClients(clients) {  clients.filter(isActiveClient).forEach(email);}
function isActiveClient(client) {  const clientRecord = database.lookup(client);  return clientRecord.isActive();}

10.函数名称应该说明它们的作用

// Badfunction addToDate(date, month) {  // ...}
const date = new Date();
// It's hard to tell from the function name what is addedaddToDate(date, 1);
// Goodfunction addMonthToDate(month, date) {  // ...}
const date = new Date();addMonthToDate(1, date);

11.函数应该只是一层抽象

当你有多个抽象级别时,你的函数通常会做太多事情。 拆分功能可以实现可重用性和更容易的测试。

// Badfunction parseBetterJSAlternative(code) {  const REGEXES = [    // ...  ];
 const statements = code.split(" ");  const tokens = [];  REGEXES.forEach(REGEX => {    statements.forEach(statement => {      // ...    });  });
 const ast = [];  tokens.forEach(token => {    // lex...  });
 ast.forEach(node => {    // parse...  });}
// Goodfunction parseBetterJSAlternative(code) {  const tokens = tokenize(code);  const syntaxTree = parse(tokens);  syntaxTree.forEach(node => {    // parse...  });}
function tokenize(code) {  const REGEXES = [    // ...  ];
 const statements = code.split(" ");  const tokens = [];  REGEXES.forEach(REGEX => {    statements.forEach(statement => {      tokens.push(/* ... */);    });  });
 return tokens;}
function parse(tokens) {  const syntaxTree = [];  tokens.forEach(token => {    syntaxTree.push(/* ... */);  });
 return syntaxTree;}

12. 删除重复代码

尽最大努力避免重复代码。 重复的代码是不好的,因为这意味着如果您需要更改某些逻辑,则需要在多个地方进行更改。

// Badfunction showDeveloperList(developers) {  developers.forEach(developer => {    const expectedSalary = developer.calculateExpectedSalary();    const experience = developer.getExperience();    const githubLink = developer.getGithubLink();    const data = {      expectedSalary,      experience,      githubLink    };
   render(data);  });}
function showManagerList(managers) {  managers.forEach(manager => {    const expectedSalary = manager.calculateExpectedSalary();    const experience = manager.getExperience();    const portfolio = manager.getMBAProjects();    const data = {      expectedSalary,      experience,      portfolio    };
   render(data);  });}
// Goodfunction showEmployeeList(employees) {  employees.forEach(employee => {    const expectedSalary = employee.calculateExpectedSalary();    const experience = employee.getExperience();
   const data = {      expectedSalary,      experience    };
   switch (employee.type) {      case "manager":        data.portfolio = employee.getMBAProjects();        break;      case "developer":        data.githubLink = employee.getGithubLink();        break;    }
   render(data);  });}

13. 使用Object.assign设置默认对象

// Badconst menuConfig = {  title: null,  body: "Bar",  buttonText: null,  cancellable: true};
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);

// Goodconst menuConfig = {  title: "Order",  // User did not include 'body' key  buttonText: "Send",  cancellable: true};
function createMenu(config) {  let finalConfig = Object.assign(    {      title: "Foo",      body: "Bar",      buttonText: "Baz",      cancellable: true    },    config  );  return finalConfig  // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}  // ...}
createMenu(menuConfig);

14. 不要使用标志作为函数参数

标志告诉你的用户这个函数不止做一件事。 函数应该做一件事。 如果函数遵循基于布尔值的不同代码路径,则拆分它们。

// 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}`);}

15.不要写入全局函数

在 JavaScript 中污染全局变量是一种不好的做法,因为你可能会与另一个库发生冲突,并且 API 的用户在生产中遇到异常之前不会意识到这一点。

// BadArray.prototype.diff = function diff(comparisonArray) {  const hash = new Set(comparisonArray);  return this.filter(elem => !hash.has(elem));};
// Goodclass SuperArray extends Array {  diff(comparisonArray) {    const hash = new Set(comparisonArray);    return this.filter(elem => !hash.has(elem));  }}

16. 优先使用函数式编程而不是命令式编程

JavaScript 不像 Haskell 那样是一种函数式语言,但它具有函数式风格。 函数式语言可以更简洁、更容易测试。 尽可能喜欢这种编程风格。

// Badconst programmerOutput = [  {    name: "Uncle Bobby",    linesOfCode: 500  },  {    name: "Suzie Q",    linesOfCode: 1500  },  {    name: "Jimmy Gosling",    linesOfCode: 150  },  {    name: "Gracie Hopper",    linesOfCode: 1000  }];
let totalOutput = 0;
for (let i = 0; i < programmerOutput.length; i++) {  totalOutput += programmerOutput[i].linesOfCode;}
// Goodconst programmerOutput = [  {    name: "Uncle Bobby",    linesOfCode: 500  },  {    name: "Suzie Q",    linesOfCode: 1500  },  {    name: "Jimmy Gosling",    linesOfCode: 150  },  {    name: "Gracie Hopper",    linesOfCode: 1000  }];
const totalOutput = programmerOutput.reduce(  (totalLines, output) => totalLines + output.linesOfCode,  0);

17.封装条件语句

// Badif (fsm.state === "fetching" && isEmpty(listNode)) {  // ...}
// Goodfunction shouldShowSpinner(fsm, listNode) {  return fsm.state === "fetching" && isEmpty(listNode);}
if (shouldShowSpinner(fsmInstance, listNodeInstance)) {  // ...}

18.避免否定条件

// Badfunction isDOMNodeNotPresent(node) {  // ...}
if (!isDOMNodeNotPresent(node)) {  // ...}
// Goodfunction isDOMNodePresent(node) {  // ...}
if (isDOMNodePresent(node)) {  // ...}

3、并发性

19.使用 Promise,而不是回调

回调不干净,并且会导致过多的嵌套。 在 ES2015/ES6 中,Promise 是内置的全局类型。 使用它们!

// Badimport { get } from "request";import { writeFile } from "fs";
get(  "https://en.wikipedia.org/wiki/Robert_Cecil_Martin",  (requestErr, response, body) => {    if (requestErr) {      console.error(requestErr);    } else {      writeFile("article.html", body, writeErr => {        if (writeErr) {          console.error(writeErr);        } else {          console.log("File written");        }      });    }  });
// Goodimport { get } from "request-promise";import { writeFile } from "fs-extra";
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")  .then(body => {    return writeFile("article.html", body);  })  .then(() => {    console.log("File written");  })  .catch(err => {    console.error(err);  });

20. Async/Await 比 Promise 更简洁

Promise 是回调的一个非常干净的替代方案,但 ES2017/ES8 带来了 async 和 wait,它提供了更干净的解决方案。 

您所需要的只是一个以 async 关键字为前缀的函数,然后您可以命令式地编写逻辑,而无需 then 函数链。

// Badimport { get } from "request-promise";import { writeFile } from "fs-extra";
get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")  .then(body => {    return writeFile("article.html", body);  })  .then(() => {    console.log("File written");  })  .catch(err => {    console.error(err);  });
// Goodimport { get } from "request-promise";import { writeFile } from "fs-extra";
async function getCleanCodeArticle() {  try {    const body = await get(      "https://en.wikipedia.org/wiki/Robert_Cecil_Martin"    );    await writeFile("article.html", body);    console.log("File written");  } catch (err) {    console.error(err);  }}
getCleanCodeArticle()

4. 错误处理

抛出错误是一件好事! 它们意味着运行时已成功识别出程序中的某些问题,并且它会通过停止当前堆栈上的函数执行、终止进程(在 Node 中)并通过堆栈跟踪在控制台中通知您来通知您。

21. 不要忽略捕获的错误

对捕获的错误不采取任何措施并不能让您有能力修复或对所述错误做出反应。 将错误记录到控制台 (console.log) 也好不了多少,因为它常常会迷失在打印到控制台的大量内容中。 

如果您将任何代码包装在 try/catch 中,则意味着您认为那里可能会发生错误,因此您应该为错误发生时制定计划或创建代码路径。

// Badtry {  functionThatMightThrow();} catch (error) {  console.log(error);}
// Goodtry {  functionThatMightThrow();} catch (error) {  // One option (more noisy than console.log):  console.error(error);  // Another option:  notifyUserOfError(error);  // Another option:  reportErrorToService(error);  // OR do all three!}

22. 不要忽视被拒绝的承诺

出于同样的原因,您不应该忽略 try/catch 中捕获的错误。

// Badgetdata()  .then(data => {    functionThatMightThrow(data);  })  .catch(error => {    console.log(error);  });
// Goodgetdata()  .then(data => {    functionThatMightThrow(data);  })  .catch(error => {    // One option (more noisy than console.log):    console.error(error);    // Another option:    notifyUserOfError(error);    // Another option:    reportErrorToService(error);    // OR do all three!  });

5. 评论

23. 只评论具有业务逻辑复杂性的事物

评论是道歉,而不是要求。 好的代码主要是文档本身。

// Badfunction hashIt(data) {  // The hash  let hash = 0;
 // Length of string  const length = data.length;
 // Loop through every character in data  for (let i = 0; i < length; i++) {    // Get character code.    const char = data.charCodeAt(i);    // Make the hash    hash = (hash << 5) - hash + char;    // Convert to 32-bit integer    hash &= hash;  }}
// Goodfunction hashIt(data) {  let hash = 0;  const length = data.length;
 for (let i = 0; i < length; i++) {    const char = data.charCodeAt(i);    hash = (hash << 5) - hash + char;
   // Convert to 32-bit integer    hash &= hash;  }}

24. 不要在代码库中留下注释掉的代码

版本控制的存在是有原因的。 将旧代码留在您的历史记录中。

// BaddoStuff();// doOtherStuff();// doSomeMoreStuff();// doSoMuchStuff();
// GooddoStuff();

25.没有期刊评论

请记住,使用版本控制! 不需要死代码、注释代码,尤其是期刊注释。 使用 git log 获取历史记录!

// Bad/** * 2016-12-20: Removed monads, didn't understand them (RM) * 2016-10-01: Improved using special monads (JP) * 2016-02-03: Removed type-checking (LI) * 2015-03-14: Added combine with type-checking (JR) */function combine(a, b) {  return a + b;}
// Goodfunction combine(a, b) {  return a + b;}

26. 避免位置标记

它们通常只是增加噪音。 让函数和变量名称以及正确的缩进和格式为代码提供视觉结构。

// Bad////////////////////////////////////////////////////////////////////////////////// Scope Model Instantiation////////////////////////////////////////////////////////////////////////////////$scope.model = {  menu: "foo",  nav: "bar"};
////////////////////////////////////////////////////////////////////////////////// Action setup////////////////////////////////////////////////////////////////////////////////const actions = function() {  // ...};
// Good$scope.model = {  menu: "foo",  nav: "bar"};
const actions = function() {  // ...};

结论

从一开始就让代码干净并不总是那么容易。 重要的是要记住,没有必要执着于使每条线路都完美,尤其是当您的日程安排很紧时。 只是没有足够的时间来一遍又一遍地重写代码。 

相反,专注于在有限的时间内编写最好的代码。 当下一轮更新到来并且您注意到可以改进的内容时,这是进行这些更改的好时机。

每个公司和每个项目都有自己的编码风格,它可能与我分享的技巧不同。 如果您加入一个已经启动并运行的项目,通常最好坚持使用已经存在的编码风格(除非您要重构它)。 这是因为在整个代码中保持一致的风格也是保持代码整洁的一种形式。

请记住,干净的代码是一个旅程,而不是目的地。 这是关于随着时间的推移做出小的改进,而不是陷入完美。 通过应用您所学到的技巧,您将能够编写出更简洁、更高效的 JavaScript 代码,未来的您和其他人都会感谢您。 

最后,感谢您的阅读,也期待你的关注,祝编程快乐!


该文章在 2024/10/14 10:35:55 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved