专栏首页前端皮小蛋写好一个函数的个人见解

写好一个函数的个人见解

前言

在 JS 中,除了变量,用的最多的应该就是函数了,函数是 Javascript 的第一公民。

要写好一个函数,个人认为,可以从以下几点来编写:

  1. 命名准确
  2. 函数注释
  3. 函数参数
  4. 函数的返回

本文对以上几点做了梳理和总结,希望能对大家有所帮助。

正文

1. 命名准确

  1. 函数名称

这应该是最基本的要求了,函数的命名需要明确,语义清晰,简单概括函数的功能。我们不要想着代码简短而缩短函数名称,这并不会提高什么性能或效率,相反,一个函数名称若不够清晰,往往其他人无法理解。

除了一些都所有人知道的名次(我们这边的 SKU )缩写外,一些相对来讲比较少人知道的专业名次就尽量不要使用缩写。

尽量使用动词,比如:getXxxxxsetXxxxx,动词在前面,语义就能更加清晰。

  1. 参数命名

参数的命名同样重要,我们都强调语义化,参数命名让调用者更清晰的知道该传入什么,对应什么参数。当然,像一些通用命名还是可接受的,像 callbackfn,即使不看注释,往往我也知道这里的参数要做什么,传什么。

2. 函数注释

当我们的命名准确后,我们也不可能让每一个看代码的人都通过名称就知道这个函数在做什么,这个参数代表什么。因此,函数一定要写注释,具体的交互代码不写,但函数的功能,参数至少是不可避免的。

/**
 * 时间格式化工具函数
 * 
 * @param { (Date | number) } date - 时间
 * @param { string } unit - 转换格式
 */
export const timeFormat = (date: Date | number | string, unit: string) => {
  if (!date) {
    return ''
  }
  if (typeof date === 'string') return date;
  if (typeof date === 'number') {
    date = new Date(date);
  }
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const hour = date.getHours();
  const minute = date.getMinutes();
  const second = date.getSeconds();
  if (unit === 'year') return `${year}`;
  if (unit === 'month') return `${year}-${month}`;
  if (unit === 'day') return `${year}-${month}-${day}`;
  if (unit === 'hour') return `${year}-${month}-${day} ${hour}`;
  if (unit === 'minute') return `${year}-${month}-${day} ${hour}:${minute}`;
  if (unit === 'second') return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}
  1. 参数注释

像上面这个例子,参数前面都加了一个 @param,表明这个是参数的注释。

一般的格式为 @param { type } 参数 - 参数解释

type 表明的是参数的类型,比如 stringnumber,当有多个参数类型的时候,可以这么来标识 { (string|string[]) },表示这个参数可以是字符串或者字符串数组。

还有其他的参数注释可以这么来写

  • 对象属性:需要解释对象的每一个属性
/**
 * Assign the project to an employee.
 * @param {Object} employee - The employee who is responsible for the project.
 * @param {string} employee.name - The name of the employee.
 * @param {string} employee.department - The employee's department.
 */
 Project.prototype.assign = function(employee) {
   // ...
 };
  • 可选参数:

有两种语法 JSDocGoogle Closure Compiler

JSDoc 语法

/**
 * 时间格式化工具函数
 * 
 * @param { (Date | number) } date - 时间
 * @param { string } [unit] - 转换格式
 */
export const timeFormat = (date: Date | number | string, unit: string) => {
  // ...
}

Google Closure Compiler 语法

/**
 * 时间格式化工具函数
 * 
 * @param { (Date | number) } date - 时间
 * @param { string= } unit - 转换格式
 */
export const timeFormat = (date: Date | number | string, unit: string) => {
  // ...
}
  • 默认值:
/**
 * 时间格式化工具函数
 * 
 * @param { (Date | number) } date - 时间
 * @param { string } [unit = 'second'] - 转换格式
 */
export const timeFormat = (date: Date | number | string, unit: string) => {
  // ...
}
  1. 函数注释

函数的功能点需要表明,一个函数一个功能,这就是我们说的 单一功能,一个函数就做一件事,因此注释也只会说明函数做了哪一件事。

  1. 其他注释

除了 @param 之外,还有其他的类型

3. 函数参数

  1. 参数默认值

有时候,当一个函数的参数不那么必要,或者某一个值相对应用较多,就应该考虑加上一个默认值,比如上面的时间转换工具,参数就应该这么写。

export const timeFormat = (date: Date, unit = 'second') => {
  // ...
}

当我们的业务中需要 YYYY-MM-DD hh:mm:ss 这样的格式较多的时候,unit 默认就是 second, 这样,当我们调用函数的时候,可以只传一个 date 参数,而不需要在每一处调用的代码都写上 timeFormat(new Date(), 'second')

  1. 对象参数

在一段打印代码中,参数多达十几个。像下面这个 打印函数

async function printer_proxy_print(
  html_str: string,
  file_path: string,
  device: string | undefined,
  orientation: number,
  printer_mode: string,
  width: number,
  height: number,
  scale: number,
  from: number,
  to: number,
  left_offset: number,
  top_offset: number,
  pdf_tools: string | undefined,
  begin_page = 1,
  end_page = 1,
  repeat_times = 1,
  print_type: string
) {
    // ...
}

这个时候,我们可以给参数默认值,这样可以只传前面几个必要的参数,像这样调用。

async function printer_proxy_print(
  html_str: string,
  file_path: string,
  device = 'pc',
  orientation = 'xxx',
  printer_mode = 'xxx',
  width = 123,
  height = 123,
  scale = 123,
  from = 123,
  to = 123,
  left_offset = 123,
  top_offset = 123,
  pdf_tools = 123,
  begin_page = 1,
  end_page = 1,
  repeat_times = 1,
  print_type = 'base64'
) {
    // ...
}

await printer_proxy_print(html_str, file_path);

上面的方法看似可行,实际上,当我中间某个参数不一样的时候,我就需要把这个参数前面的参数都传一遍。这样显然不可行。所以当参数多的时候,我们需要用对象解构的方式传参。

async function printer_proxy_print({
  html_str,
  file_path,
  device = 'pc',
  orientation = 'xxx',
  printer_mode = 'xxx',
  width = 123,
  height = 123,
  scale = 123,
  from = 123,
  to = 123,
  left_offset = 123,
  top_offset = 123,
  pdf_tools = 123,
  begin_page = 1,
  end_page = 1,
  repeat_times = 1,
  print_type = 'base64'
}) {
    // ...
}

await printer_proxy_print({html_str, file_path});

解构的好处便是我可以随便传我想要的某几个参数,而不用在意顺序问题。不过像这么多参数的函数往往存在问题(具体问题具体分析)。也就是下面提到的参数数量问题。

  1. 参数数量

一个函数的参数越少越好,最多不应该超过3个,参数多往往意味着关系多,逻辑交叉相对也就多了起来。在进行测试的时候,往往也就很难覆盖到所有条件,出问题概率也就加大了。

参数多的时候,有时候也意味着功能多,就违背了 单一功能 的原则。

  1. 参数类型防御

在 TS 开发前,我们不知道用户会传什么东西进来,这时候往往容易产生类型错误,又或者,我们想实现兼容,像前面的 timeFormat 函数,我们希望用户调用的时候,可以是想对 时间对象 格式化,也可以是对 时间戳 格式化,那我们就需要做一个防御处理。

  if (!date) {
    return ''
  }
  if (typeof date === 'string') return date;
  if (typeof date === 'number') {
    date = new Date(date);
  }

不过值得注意的是,即使我们用上了 TS,在大多数情况下,我们确实可以避免参数类型问题,但是这并不绝对,因为我们有时候会直接接受 接口 返回的数据。

我们常说,永远不要相信用户的输入,同样,接口返回的数据我也不信,我们不能保证,后端不会出错,约定好的参数是数组类型,怎么空的时候,你给我个 null 呢?

当然这些情况有时候需要去试错,有时候我们能想到的可能,不要偷懒,给写上类型判断吧。

4. 函数的返回

  1. 幂等

什么叫幂等,简单来说,输入什么输出什么是固定的,入参决定了出参,不管调用多少次,只要输入一样,结果应该保持一样。

简单的例子:

  function sum(a: number, b: number) {
    return a + b;
  }

幂等函数具有可维护性,相对容易进行单元测试。

  1. 纯函数

纯函数在幂等的条件下,还要求没有副作用。

举个例子:

  const dog = {
    name: 'puppy',
    age: 2,
    weight: 30,
  }

  if (!dog.color) {
    console.log('has no color');
  }

  function addColor(dog) {
    dog.color = 'white';
  }

  addColor(dog);
  console.log(dog); // {name: "puppy", age: 2, weight: 30, color: "white"}

可以看到,addColor 函数修改了 dog 对象的属性,也就是产生了副作用,我们应该怎么做修改会合理点呢?可以像下面这样:

  function addColor(dog) {
    let copyDog = Object.assign({}, dog);
    copyDog.color = 'white';
    return copyDog;
  }

这样一来,dog 对象的属性就不会修改,addColor 函数是纯函数。

  1. return null

null 在进行处理的时候相对麻烦,需要进行判断,导致了额外的代码,应当返回空对象,或者是空数组,或者抛出异常。

总结

良好的代码,自带注释。

希望大家都能养成好的习惯。

谢谢。

本文分享自微信公众号 - 前端皮小蛋(gh_e69260c16440),作者:zelin.xu

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-04-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 怎样才能写好一个 Python 函数

    链接:https://hackernoon.com/write-better-python-functions-c3a9a36382a6

    咸鱼学Python
  • 干货 | 如何写一个更好的Python函数?

    《Writing Idiomatic Python》一书的作者在Medium上发表了一篇文章,给出了6个建议。

    磐创AI
  • 干货 | 如何写一个更好的Python函数?

    《Writing Idiomatic Python》一书的作者在Medium上发表了一篇文章,给出了6个建议。

    量子位
  • 写一个resample的函数

    因为传统实现resample的方法好像没有做迭代,只会重抽一次。这就导致了每次重抽会有一些差别。于是我加入了迭代。 懒得写成独立的函数了,就这样放出来,可以看到...

    Listenlii-生物信息知识分享
  • 给函数取一个“好”的名字

    早在2013年,国外有个程序员做了一个有意思的投票统计,该投票是让程序员从以下几个选项中选出平时在工作中自己认为最难做的事情:

    Java团长
  • 构造函数和使用一个个 setter 的效率哪个好??

    在对 Java 代码进行优化的时候,想方设法的要提高整体的效率,使用 JProfiler 看代码的时间占比,然后,看看哪些部分是可以优化的,减少运行时间的。下面...

    好好学java
  • 写一个限制执行次数的函数

    前言:当监听窗口的下拉事件或者输入框的输入事件时,会频繁的触发所监听的函数,我们并不想如此频繁的执行那些我们定义的函数,这样会造成一些不好的用户体验,接下来我们...

    Theone67
  • 5个好用的 CSS 函数

    CSS 包含了许多函数,而且它能够完成许多早期需要用 JavaScript才能完成的事情。每年都有新的特性被添加进来,这让我们的开发更加轻松,也减少了对Java...

    前端小智@大迁世界
  • SAS-函数(二),几个很好用的函数~

    第二部分的函数也是很常用的基本函数,学会了某些函数,在编程上回很省事,效率也会大大的提高...

    Setup
  • 【面试宝典】写一个函数将两个数交换

    没有参加过面试的同学可能会很忐忑,面试都会出些什么题呢?其实一般情况下,大部分的面试题都是比较基础的。关于如何交换两个数字,应该是非常简单的问题了。看下面几个函...

    程序员互动联盟
  • 个人建议-怎样写出一篇好文章

    写文章有几个月了,之前跟几个作者交流写作的方式,有几个作者说自己不知道怎样写好文章,有两个还说,自己写的文章,即使是自己读也是感觉不通顺,但是不知道怎么改!见这...

    守候i
  • 请编写一个C函数,将一个字符串逆序

    目前有两种思路,一个是申请一片辅助空间,然后将原字符串逆向拷贝到辅助空间,然后输出;另一种是原地逆序,不需要额外的辅助空间,方法就是字符串首尾交换。

    Daotin
  • 调参的一些个人拙见

    最近的事。。浓缩成下面的一张图。 ? 调参有哪些方法呢? 语料处理。这个是之后一切操作的基础。有人或许认为算法是最重要的,其实不然,语料处理真的真的是最重要的。...

    zenRRan
  • 怎么写一个好的 Git commit message

    网络上很多关于 commit message 的想法都来源于 tpope,在他看来,一个好的 Git commit messge 应该是这样的:

    用户1558438
  • 如何写出你的第一个递归函数?

    但编程里面有一些术语或者思想或者理论,在现实中不容易找到类比的东西,此时初学者就很难理解了。

    青南
  • 2021-05-02:给定一个文件目录的路径,写一个函数

    2021-05-02:给定一个文件目录的路径,写一个函数统计这个目录下所有的文件数量并返回。隐藏文件也算,但是文件夹不算 。

    福大大架构师每日一题
  • tensorflow 中dataset.padded_batch函数的个人理解过程

    今天继续啃Tensorflow实战Google深度学习框架这本书,在250P的Seq2Seq模型代码理解时候有点困难,其中padded_batch(batch_...

    用户7886150
  • 写一函数,将一个3X3的整型矩阵转置

    py3study
  • 私藏的5个好用的Pandas函数!

    explode用于将一行数据展开成多行。比如说dataframe中某一行其中一个元素包含多个同类型的数据,若想要展开成多行进行分析,这时候explode就派上用...

    统计学家

扫码关注云+社区

领取腾讯云代金券