文档章节

javascript代码规范化的一些方法

筱飞
 筱飞
发布于 2016/06/27 10:23
字数 3048
阅读 37
收藏 1
点赞 0
评论 0

在我们的编程开发中,如果能在没有一行注释的代码中找到注释,是不是很意思呢?

  我们经常容易犯一个错误:我们修改了一段代码,但是忘记修改更新注释。混乱的注释并不会打断你代码的执行,但是想象一下debug的时候会发生什么事情。你认真地阅读了注释,它说的是一件事,但是代码干的是另一件事。结果是,你浪费了很多时间发现注释是错误的,甚至最糟糕的是,你可能完全被误导了。

一些编程者将注释归纳到自文档化代码的范畴。在这篇文章中,我们只关注代码。注释很重要,但是却单独覆盖了太多东西。我们可以把自文档化代码归为三大类:

  • 结构类自文档化。使用代码的结构和目录来让代码变得清晰
  • 命名自文档化。例如函数或变量命名让代码更易理解
  • 语句相关自文档化。我们利用语言的特性来让代码变得清晰

一、结构类自文档化

  首先我们看下结构类自文档化。这里指的是通过移动部分代码来让代码变得清晰。

  • 将代码移动到函数里面。

  这和提取函数重构一样,意思是我们将已经写好的代码移动到一个新的函数里:即提取代码成为一个新函数。例如:

var width = (value - 0.5) * 16;

  不是很清晰,这里添加一个注释会很有帮助,或者,我们将它提取到一个函数里面进行自文档描述:

var width = emToPixels(value);

function emToPixels(ems) {
    return (ems - 0.5) * 16;
}

  这里唯一的变化是我们把计算放到函数里面。函数的名称描述了代码的作用,所以代码的意思就不言而喻了。另一个好处是,我们现在有一个到处可以调用的辅助函数,所以这种方法也增强了代码复用性。

  • 用函数代替条件表达式

  如果没有注释,含有多个操作运算的语句很难理解。我们可以使用一个简单的方法来描述。

if(!el.offsetWidth || !el.offsetHeight) {
}

  这段代码的目的是啥?

function isVisible(el) {
    return el.offsetWidth && el.offsetHeight;
}

if(!isVisible(el)) {
}

  同样,我们把代码移动到一个函数里面,代码就立即变得容易理解了。

  • 使用变量代替表达式

  使用变量代替表达式和将代码移入函数里面类似,但是相对于函数,我们是用一个变量。让我们再来看下这个语句:

if(!el.offsetWidth || !el.offsetHeight) {
}

  这次我们用变量来代替表达式,而不是函数:

var isVisible = el.offsetWidth && el.offsetHeight;
if(!isVisible) {
}

  这样比使用函数更好,例如,如果使用的逻辑非常明确,而且这段逻辑只在某一个地方使用一次,就适合用变量了。这种方法最常用的场景是在数学表达式中:

return a * b + (c / d);

  我们可以这样让代码变得清晰:

var multiplier = a * b;
var divisor = c / d;
return multiplier + divisor;

  因为我数学很差,想象一下上面的例子是一个有含义的运算,在任何场景下,你可以将复杂的表达式换成变量来给表达式代码添加本身的含义。

  • 类和模块接口

  接口不仅是类或模块的公用方法和属性,而且在使用中能起到自文档化的作用。来看下这个例子:

class Box {
    setState(state) {
        this.state = state;
    }

    getState() {
        return this.state;
    }
}

  这个类当然可以含有其他的代码部分,我这里是让例子简单些来演示公共接口是怎样自文档化的。你能理解这个类是怎样使用的吗?可能需要通过一段时间的理解,但不是特别明显。

  这两个函数都有合理的命名:他们做的事情从它们的名称来看非常清晰。但是尽管如此,你仍然不知道如何使用它们。很可能你需要阅读更多的代码或文档来理解怎么用。所以我们可以这样改下:

class Box {
    open() {
        this.state = 'open';
    }

    close() {
        this.state = 'closed';
    }

    isOpen() {
        return this.state === 'open';
    }
}

  很容易理解了用法,对吧?需要注意的是,我们只改变了公用接口,内部的声明仍然使用的是this.state来用的。现在你可以一眼就知道Box类是怎样来使用的了。这就演示了尽管使用了很好命名,但是整个模块使用仍然不好理解的情况,通过这些简单的决策,你会有一个更好的认知。

  • 代码分块。

  对不同部分的代码进行分块也能达到自文档化的目的。例如,你应该养成将变量的声明和它们使用的语句放到一起的习惯,并尽可能将变量的使用放到一起。这样可以显示不同部分代码之间的联系,所以后面任何人修改都能花更少的时间来找到它被使用到的所有位置。看下这个例子:

var foo = 1;

blah()
xyz();

bar(foo);
baz(1337);
quux(foo);

  你能一眼看出foo被调用了几次吗?再来看下这个例子:

var foo = 1;
bar(foo);
quux(foo);

blah()
xyz();

baz(1337);

  通过把所有的foo调用的地方放到一起,我们可以很容易的看到那几个部分的方法函数式依赖它的。

  • 使用纯函数。

  纯函数比普通的函数更容易理解,什么是纯函数?当使用同样的参数调用,如果输出的内容相同,这就是所谓的”纯函数”。这就是说,这个函数不会对状态有任何依赖或影响,例如时间、对象属性、ajax方法等等。这类函数很容易理解,因为很多影响他们输出结果的值都被明确指定了,你不用研究某个东西到底是从哪里来的,或者什么东西会影响输出,而是很直观能理解到的。

  这类函数能够进行自文档化描述的另一个原因是你可以信任它们的输出。无论怎样,这种函数会根据你的输入返回特定的结果。它不会影响任何额外的东西,所以你能确定它不会带来任何副影响。

  一个这类函数出错的例子是document.write(),有经验的js开发者知道我们不应该使用它,但是很多新手还在那折腾。有时它没问题,但是有时,在某些特定情况,它会清除页面所有内容。这就是副影响。

  为了了解什么是纯函数,可以看下这篇文章《Functional Programming: Pure Functions》。

  • 目录和文件结构

  在文件或目录命名时,根据项目中的命名规范进行命名。如果没有明确的规范,那就根据你语言选择合适的标准。例如:如果你添加一个与UI相关的代码,发现项目中有相似功能的内容,如果UI相关的内容放在src/ui/的文件夹下,你应该也这样做。这样可以更容易的找到代码,并且根据项目中其它的代码,你可以很容易知道它的作用。所有的UI代码都在相同的地方,然后,它必须是与UI相关的。

二、命名自文档化

  计算机科学领域有句名言:

在计算机凌云有量大难题:缓存失效和命名。–Phil Karlton

  所以让我们来看下我们怎样使用命名来使我们的代码自文档化。

  • 重命名函数。

  函数命名常常比较复杂,但是有一些简单的规则我们可以参考:

  1、避免使用想handle或manage这类单词:handleObject,manageObject。轻微这些什么意思。   2、使用主动词:cutGruss(),sendFile。根据函数的具体功能来决定。   3、使用返回值:getMagicBullet(),readFile。这种情况并不经常使用,但是在语义化方面很有作用   4、使用强类型语言编码能明确知道返回值是什么类型

  • 重命名变量

  对于变量,有两个比较好的方法。

  1、指明单位:如果你含有数字参数,你可以带上单位。例如widthPx就比 width要好。   2、不要使用简写:例如a和b,这些是不能理解的变量,除非是在循环计数里面。

  • 遵循已有的命名规范

  代码中尽量使用原有的规范。例如,如果你有一个具体类型的对象,那么使用相同的名字:

var element = getElement();

  千万不要傻傻的这样写:

var node = getElement();

  如果你遵循其它地方代码的规范,任何一个读代码的人都能正确的认为某个东西在任何地方出现的含义都是相同的。

  • 使用有含义的错误

  Undefined is not an object。很多人都喜欢这样提示到页面上,所以让我们提示一些具有提示意义的内容给页面使用者。怎样让错误变得有含义:

  1、应该描述具体问题所在。   2、如果可能,应该包含是哪个变量或数据导致的问题。   3、关键一点:错误应该能帮助我们找到哪里出了问题。例如错误上报的方式收集解决问题。

三、 声明类自文档化

  让我们看下一个JavaScript的例子:

imTricky && doMagic();

尽量这样写:

if(imTricky) {
    doMagic();
}

  声明技巧并不适合每个人。

  • 使用命名常量。例如const MEANING_OF_LIFE = 42;
  • 避免bool值。例如 myThing.setData({ x: 1 }, true);
  • 使用编程语言的优势。

  我们甚至可以使用编程语言某些特有的特性来描述一段代码的作用:

var ids = [];
for(var i = 0; i < things.length; i++) {
  ids.push(things[i].id);
}

  上面代码收集了一个数组元素里面的id。但是我么可以这样写。

var ids = things.map(function(thing) {
  return thing.id;
});

  在这个例子中。我们立即知道了这个过程的作用,因为这就是map方法的目的。另一个JavaScript的例子是const关键字,常常,我们通过它来声明不会更变的变量。一个非常通用的例子是加载CommonJS模块的时候:

var async = require('async');

  我们可以描述得更清晰:

const async = require('async');

  另一个好处是,如果有人修改,会报错。

四、反模式

  当这些你都熟悉了之后,你可以做更多的事情,然而有些事情你必须要小心:

  • 提取更短的函数。

  一些人主张使用很小的函数,将所有的东西函数化,你可以这样做,但是,这会增加代码理解的难度。例如,试想一下,你debug的时候,你看了A函数,然后A调用了B函数,然后B调用了C函数…短的函数比较容易理解,但是如果你只是在一个地方使用,推荐使用变量来代替。

  • 不要强迫去使用

  通常,没有绝对正确的方式,所以,如果听起来某件事情很不错,但是不要强制自己去使用。

结论

  让自己的代码自文档化是一件是你的代码更容易维护的事情,也是一件很难的事情。每一行添加的代码注释不一定会被维护起来,所以消除代码注释也是一件很好的事情。

  然而,自文档化代码并不能代替文档或注释。例如,代码在表达方面是有限的,所以你也需要很好的注释来补充。API文档对于代码库来说是很重要的,除非你的代码很小,否则自文档化的方式并不可行。

原文作者:Jani Hartikainen

原译:ouven

原文地址: https://www.sitepoint.com/self-documenting-javascript/

© 著作权归作者所有

共有 人打赏支持
筱飞
粉丝 13
博文 108
码字总数 22146
作品 0
虹口
前端工程师
关于js的bind牌胶水,了解一下?

前言 今天聊一聊js中的bind方法,主要从三个维度来阐述:why——>what——>how。文章虽经个人多次校验,对语言表述、代码书写等进行了认真审核,但仍免不了有疏漏之处,如若发现,还望指出,...

hanmin ⋅ 05/14 ⋅ 0

爬虫获取 js 动态数据 (1)

爬虫遇到 js 动态数据时,主要解决方法有两种: 使用一些库,例如 Selenium,来模拟浏览器环境抓取数据。但这样做对内存和 CPU 的消耗都比较大,爬虫效率低,应尽量避免。 手动分析 js 请求,...

anye137 ⋅ 06/05 ⋅ 0

iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge 转载:原地址 https://www.jianshu.com/p/e951af9e5e74 上一篇文章介绍了UIWebView 如何通过WebViewJavascriptBridge 来实现......

法斗斗 ⋅ 05/11 ⋅ 0

React Native 与原生模块数据通信(一)(iOS)

(一)iOS日历模块封装演示 下面开始演示如何封装一个iOS日历原生模块,让JavaScript可以进行访问到iOS平台日历的功能。 在React Native中,原生模块就是一个Objective-C类,该实现了RCTBridge...

manofit ⋅ 05/24 ⋅ 0

JavaScript 编程精解 中文第三版 零、前言

零、前言 原文:Introduction 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了《JavaScript 编程精解(第 2 版)》 We think we are creating the system for our own purp...

ApacheCN_飞龙 ⋅ 06/01 ⋅ 0

React Native通信原理源码分析一

小伙伴们都知道在Android开发中实现Java和JS的通信可以通过WebView来实现,包括注册JSBridge或者在接口中拦截都可以。然而React Native中并没有用WebView控件的方式,而是基于WebKit内核的方...

juexingzhe ⋅ 04/08 ⋅ 0

code-rhythm:写了个vscode扩展,让代码更有快感

项目地址 Github - onvno/code-rhythm 原因 写代码本身是件快乐的事情,但开发中总有各种烦恼。 有时候一个很简单的方法,因为不确定传参的形式,不确定返回形式,不确定具体用法,就得翻墙,...

onvno_ ⋅ 06/07 ⋅ 0

[译] JavaScript 是如何工作的:对比 WebAssembly + 为什么在某些场景下它比 JavaScript 更合适

原文地址:How JavaScript works: A comparison with WebAssembly + why in certain cases it’s better to use it over JavaScript 原文作者:Alexander Zlatkov 译文出自:掘金翻译计划 本......

stormluke ⋅ 05/23 ⋅ 0

以变制变——前端动态化代码保护方案探索

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文分享了腾讯防水墙团队关于机器对抗的动态化思路,希望能抛砖引玉,给现在正在做人机对抗的团队一些启发,帮助更多中小型公司...

腾讯云加社区 ⋅ 06/07 ⋅ 0

[Node.js源码解读(1)]Node.js的启动过程

本文原载于https://github.com/DavidCai1993/my-blog/issues/26 经原作者授权连载于alinode官方博客,未经原作者允许,不得转载。 大家可能会好奇,在 Node.js 启动后,第一个执行的 JavaSc...

_朴灵_ ⋅ 05/14 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

大数据,必须掌握的10项顶级安全技术

我们看到越来越多的数据泄漏事故、勒索软件和其他类型的网络攻击,这使得安全成为一个热门话题。 去年,企业IT面临的威胁仍然处于非常高的水平,每天都会看到媒体报道大量数据泄漏事故和攻击...

p柯西 ⋅ 21分钟前 ⋅ 0

Linux下安装配置Hadoop2.7.6

前提 安装jdk 下载 wget http://mirrors.hust.edu.cn/apache/hadoop/common/hadoop-2.7.6/hadoop-2.7.6.tar.gz 解压 配置 vim /etc/profile # 配置java环境变量 export JAVA_HOME=/opt/jdk1......

晨猫 ⋅ 27分钟前 ⋅ 0

crontab工具介绍

crontab crontab 是一个用于设置周期性被执行的任务工具。 周期性执行的任务列表称为Cron Table crontab(选项)(参数) -e:编辑该用户的计时器设置; -l:列出该用户的计时器设置; -r:删除该...

Linux学习笔记 ⋅ 52分钟前 ⋅ 0

深入Java多线程——Java内存模型深入(2)

5. final域的内存语义 5.1 final域的重排序规则 1.对于final域,编译器和处理器要遵守两个重排序规则: (1)在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用...

江左煤郎 ⋅ 58分钟前 ⋅ 0

面试-正向代理和反向代理

面试-正向代理和反向代理 Nginx 是一个高性能的反向代理服务器,但同时也支持正向代理方式的配置。

秋日芒草 ⋅ 今天 ⋅ 0

Spring 依赖注入(DI)

1、Setter方法注入: 通过设置方法注入依赖。这种方法既简单又常用。 类中定义set()方法: public class HelloWorldOutput{ HelloWorld helloWorld; public void setHelloWorld...

霍淇滨 ⋅ 昨天 ⋅ 0

马氏距离与欧氏距离

马氏距离 马氏距离也可以定义为两个服从同一分布并且其协方差矩阵为Σ的随机变量之间的差异程度。 如果协方差矩阵为单位矩阵,那么马氏距离就简化为欧氏距离,如果协方差矩阵为对角阵,则其也...

漫步当下 ⋅ 昨天 ⋅ 0

聊聊spring cloud的RequestRateLimiterGatewayFilter

序 本文主要研究一下spring cloud的RequestRateLimiterGatewayFilter GatewayAutoConfiguration @Configuration@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMi......

go4it ⋅ 昨天 ⋅ 0

Spring clound 组件

Spring Cloud技术应用从场景上可以分为两大类:润物无声类和独挑大梁类。 润物无声,融合在每个微服务中、依赖其它组件并为其提供服务。 Ribbon,客户端负载均衡,特性有区域亲和、重试机制。...

英雄有梦没死就别停 ⋅ 昨天 ⋅ 0

Confluence 6 重新获得站点备份文件

Confluence 将会创建备份,同时压缩 XML 文件后存储熬你的 <home-directory>/backups> 目录中。你需要自己访问你安装的 Confluence 服务器,并且从服务器上获得这个文件。 运行从 Confluence...

honeymose ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部