image.png

为什么我们使用JavaScript、Dart和Python等语言,而不是古老的汇编语言?

这是因为它们与自然语言更接近。

或者说,它们有可能更接近自然语言。

因为有时我们编写代码只是为了让它能工作,而不关心向其他人展示我们在做什么。

而这种做法往往会在日后造成痛苦的反噬。特别是当其中一个"其他人"是未来的自己时。

1. 使用词性命名

当你的代码尽可能地像英语时,你就知道它是自然的。就像一个有趣、描述性的故事。

这意味着你已经智能地创造了故事中的实体动作,以强有力地表达从开始到完成的代码流程。

名词

我们在谈论哪些实体?

  • 变量
  • 属性(getter和setter)
  • 类和对象
  • 模块
  • 每个角色都有一个名字,所以我们用表达力强的名词名词短语来描述它们。

不要这样:

// ❌ do-examples.ts
// ❌ 难以理解
const f = 'Coding';
const l = 'Beauty';

// ❌ Verb
// ❌ 动词
const makeFullName = `${f} ${l}`;
class Book {
  // ❌ Adjectival phrase
  // ❌ 形容词短语
  createdAt: Date;
}

而要这样:

// ✅ examples.ts
// ✅ 可读性高
const firstName = 'Coding';
const lastName = 'Beauty';

// ✅ Noun
// ✅ 名词
const fullName = `${firstName} ${lastName}`;

class Book {
  // ✅ Noun phrase
  // ✅ 名词短语
  dateCreated: Date;
}
动词

你的代码库中有哪些动作?

  • 函数
  • 对象方法

动作意味着实体在做某事;命名它们的自然方式是使用描述性的动词动词短语

不要这样:

class Product {
  constructor(name, price, quantity) {
    this.name = name;
    this.price = price;
    this.quantity = quantity;
  }

  // ❌ Noun
  // ❌ 名词
  total() {
    return this.price * this.quantity;
  }
}

const product = new Product('Pineapple🍍', 5, 8);
console.log(product.total()); // 40

而要这样:

class Product {
  constructor(name, price, quantity) {
    this.name = name;
    this.price = price;
    this.quantity = quantity;
  }

  // ✅ Verb
  // ✅ 动词
  getTotal() {
    return this.price * this.quantity;
  }
}

const product = new Product('Banana🍌', 7, 7);
console.log(product.getTotal()); // 49

方法是用来某事的。属性是用来拥有某物的。

所以更好的做法是:

class Product {
  constructor(name, price, quantity) {
    this.name = name;
    this.price = price;
    this.quantity = quantity;
  }

  get total() {
    return this.price * this.quantity;
  }
}

const product = new Product('Orange🍊', 7, 10);
console.log(product.total); // 70

副词

记住,副词用来告诉你更多关于名词、动词或另一个副词的信息。

在JavaScript中,这是指任何接受函数并返回另一个函数的函数:一个高阶函数。它们升级了函数。

所以与其这样:

// ❌ 动词
function silence(fn) {
  try {
    return fn();
  } catch (error) {
    return null;
  }
}

const getFileContents = (filePath) =>
  silence(() => fs.readFileSync(filePath, 'utf8'));

不如这样更自然:

// ✅ 副词
function silently({ fn }) { // or "withSilence"
  try {
    return fn();
  } catch (error) {
    return null;
  }
}

const getFileContents = (filePath) =>
  silently({ fn: () => fs.readFileSync(filePath, 'utf8') });

这就像在说"悄悄地获取文件内容"。

2. 编写自然的输入

编码和计算都是关于处理某些输入以产生输出。

在自然代码中,处理是动作,而输入+输出是实体。

假设我们有一个计算矩形面积并将其乘以某个数量的函数。

你能看出这里的问题吗?

const calculateArea = (length, width, height) => length * width * height;

const area = calculateArea(2, 5, 10); // 100

哪个参数是宽度和高度?哪个是乘数?

这段代码读起来不自然;在英语中,我们总是指定动作的对象。

如何修复这个问题?命名参数

// 输入是实体 - 名词 ✅
const area = calculateArea({ multiplier: 2, height: 5, width: 10 });

function calculateArea({ multiplier, height, width }) {
  return multiplier * height * width;
}

这样更容易阅读;我们立即就能理解我们在处理什么输入。

即使只有1个参数,我也建议使用这种方式。

3. 编写自然的输出

我们在输出上也可以同样明确:

const { area } = calculateArea({
  multiplier: 2,
  height: 5,
  width: 10,
});

function calculateArea({ multiplier, height, width }) {
  return { area: multiplier * height * width };
}

这也允许我们之后轻松升级函数:

const { area, perimeter } = calculateArea({
  multiplier: 2,
  height: 5,
  width: 10,
});

console.log(area, perimeter); // 100 60

function calculateArea({ multiplier, height, width }) {
  return {
    area: multiplier * height * width,
    perimeter: (height + width) * 2 * multiplier,
  };
}

4. 避免魔法数字

编码不是悬疑惊悚片!它是一篇描述性文章;尽可能地描述清楚。

而不是这种令人费解的混乱:

function c(a) {
  return a / 13490190;
}

const b = c(40075);

console.log(b); // 0.002970677210624906

在整个代码库的大背景下,所有这些数字和变量意味着什么?它们告诉我们——人类——什么?

什么也没告诉我们。实体动作的名称要么不存在,要么质量很差。

这就像告诉你的朋友:

是的,我去了这个地方,然后做了这件事,之后我做了某事去了另一个地方,并做了某事120

这毫无意义。

自然代码描述一切。为实体使用优雅的名词,为动作使用出色的动词。

const speedstersSpeedKmPerHr = 13490190;
const earthCircumferenceKm = 40075;

function calculateSpeedstersTime(distance) {
  return distance / speedstersSpeedKmPerHr;
}

const time = calculateSpeedstersTime(earthCircumferenceKm);

console.log(time); // 0.002970677210624906 ~ 11s

现在你说了些有意义的话。

是的,我去了餐厅吃了一个鸡肉三明治,然后我开车去了健身房做了二头肌卷举120磅

5. 创建"无用"变量

在自然代码中,变量不再仅仅用于在这里那里存储值。

它们也是解释你在做什么的工具:

这就是为什么我们不这样做:

if (
  !user.isBanned &&
  user.pricing === 'premium' &&
  user.isSubscribedTo(channel)
) {
  console.log('Playing video...');
}

而是这样做:

const canUserWatchVideo = 
  !user.isBanned &&
  user.pricing === 'premium' &&
  user.isSubscribedTo(channel);

if (canUserWatchVideo) {
  console.log('Playing video...');
}

我们只会使用这个变量一次,但这并不重要。它不是一个功能性变量,而是一个装饰性变量;一个自然变量。

最后的思考

代码是为你的同伴人类编写的,而不仅仅是为编译器。

一个不懂编程的人能理解你的代码中发生了什么吗?

毫无疑问,这是一个强有力的指导性问题,可以让你的代码尽可能地易读和自然。

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68.1k 声望105k 粉丝