为什么我坚持使用 JavaScript 函数声明

时光溯回到上世纪 90 年代晚期,在初次接触 JavaScript 时,老师教我们使用函数声明写下Hello World,它看上去是这样的······

function helloWorld() {
  return ‘Hello World!’;
}

那时候,再没有比写出如下Hello World函数更酷的事儿了······

const helloWorld = () => 'Hello World!';

这个函数表达式体现了 ES 2015 的最大亮点——箭头函数(Arrow function),精简可爱,简直迷死人。第一次看到它时,我的内心戏是:“憋说话,吻我”!

即使是免费的 Babel(JavaScript编译器),也无法阻挡我对函数声明的喜爱。

一晃 20 年过去了,用 ES 2015 做了这么多项目,如今再写Hello World函数,就是这样的了:

function helloWord() {
  return ‘Hello World!’;
}

一睹了新世界的风采,想必你们眼中的旧世界已经不忍直视了吧?——那么简单的函数竟然要 3 行!那些多余的字符怎么看都扎眼!

如今你们内心戏大概是:

我对箭头函数绝对是真爱,但要声明一个顶级函数时,我仍用“土气”的函数声明。

为什么呢?“Uncle Bob” Martin 是这么说的:

花1小时写代码,就要花10小时检查代码。查找漏洞都成了写代码的一部分了,这个差距太悬殊。就算写代码多费点时间,代码也要简单易懂。—— Robert C. Martin《Clean Code: A Handbook of Agile Software Craftsmanship》

相比函数表达式,函数声明有两大优势:

1

目的明了

每天看上千行代码,快速了解程序员的目的非常重要。

看看这行代码:

const maxNumberOfItemsInCart = ...;

读完了还是不知道这省略符号是函数还是其他什么值。它可能是:

const maxNumberOfItemsInCart = 100;

也可能是:

const maxNumberOfItemsInCart = (statusPoints) => statusPoints * 10;

如果使用函数声明就没有这个麻烦了。

请看:

const maxNumberOfItemsInCart = 100;

对比:

function maxNumberOfItemsInCart(statusPoints) {
  return statusPoints * 10;
}

从一开始目的就很明了。

不过,如果你的代码编辑器可以用颜色区分代码,或者你在用一个 Speed Reader,就当我什么都没说。

你们肯定在暗自赞叹函数声明既简洁又迷人吧。

实际上,仅这一个原因还不足以服人,还有另外一个原因。

2

Order of declaration == order of execution

执行顺序和声明顺序一致时,是最理想的。如果能做到执行时才声明使用 const(常数)关键字的值就很厉害了。

都退后,我要开始装逼了

Fair warning:接下来我要放大招了——下文“行话”连篇,但你只要明白在声明之前不能使用常数就好。

以下的代码会抛出一个错误:

sayHelloTo(‘Bill’);
const sayHelloTo = (name) => `Hello ${name}`;

这是因为,当 JavaScript 引擎阅读代码时,会绑定(bind)而非初始化sayHelloTo。

JavaScript 中所有声明都是提升(hoist)和绑定的,但初始化的方式各自不同。换言之,JavaScript 提升了sayHelloTo的声明——先阅读,再置其于顶层,然后创建空间来储存其值——但在执行之前不会声明sayHelloTo。

sayHelloTo绑定和初始化的时间差叫作“暂存死区(TDZ)”。

如果在浏览器里直接使用ES2015(而不是借助Babel等使用ES5),以下的代码也会抛出错误:

if(thing) { 
  console.log(thing);
}
const thing = 'awesome thing';

以上的代码是用var而非const写的,不会抛出错误,因为变量被绑定时会初始化为undefined,而常数被绑定时完全不会被初始化。有点偏题了……

函数声明不会有此类 TDZ 问题。如下文,代码可以是:

sayHelloTo(‘Bill’);
function sayHelloTo(name) {
  return `Hello ${name}`;
}

这是因为函数声明在被绑定时就被初始化了——这是在执行代码之前。

所以不论何时进行函数声明,代码开始执行时,语法都是符合其作用域的。

Professor JavaScript,out!

刚说的问题让代码不得不从低级开始,一步一步往上走。

但我根本适应不了这样的思维方式啊。框架在前,细节在后才对。

大多数代码都是人写的,人的思维往往是跟着代码执行的顺序走的。

实际上,在代码顶端上面加一个 API 的小总结不是很好吗?用函数声明完全可以做到。

看看如下颇为生硬的 shopping cart 模块:

export {
          createCart,
       addItemToCart,
  removeItemFromCart,
        cartSubTotal,
           cartTotal,
            saveCart,
           clearCart,
}
function createCart(customerId) {...}
function isValidCustomer(customerId) {...}
function addItemToCart(item, cart) {...}
function isValidCart(cart) {...}
function isValidItem(item) {...}
...

用函数表达式,它就是这样的:

...
const _isValidCustomer = (customerId) => ...
const _isValidCart = (cart) => ...
const _isValidItem = (item) => ...
const createCart = (customerId) => ...
const addItemToCart = (item, cart) => ...
...
export {
          createCart,
       addItemToCart,
  removeItemFromCart,
        cartSubTotal,
           cartTotal,
            saveCart,
           clearCart,
}

把它想象成一个包含着小函数,稍大点儿的模块呢?看个人喜好。

很多人,包括很多聪明人都会觉得在声明之前使用不合常理,而且会产生不良后果。但什么好,什么不好,只是个人意见而已,并非真理。

但我的意见是:代码就是用来交流的。好的代码就是一则精彩的故事。

我会用编译器、转译器、压缩工具等来优化机器的代码。但是优化代码对我来说就是让其更简单易懂。

3

关于箭头函数

是的,箭头函数是真爱啊。

我一般会用箭头函数来通过一个小函数,将其作为更高阶函数的值。使用箭头函数时,还会使用地图、过滤器等,它们都是我的好朋友。

举个例子:

const goodSingers = singers.filter((singer) => singer.name !== 'Justin Bieber');
function tonyMontana() {
  return getTheMoney().then((money) => power)
                      .then((power) => women);
}

此文还提及了一些 JavaScript 的新功能。若想了解 JavaScript 最新标准 ES 2015 及其各种新功能新亮点,我可以提供免费的入门指南。

原文发布于微信公众号 - CSDN技术头条(CSDN_Tech)

原文发表时间:2016-08-22

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小樱的经验随笔

BZOJ 3670: [Noi2014]动物园【KMP变形 】

3670: [Noi2014]动物园 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 2738  Solve...

3427
来自专栏小詹同学

【记录帖】(No.001)从零打卡刷Leetcode

小詹一直觉得自己编程能力不强,想在网上刷题,又怕不能坚持。不知道有木有和小伙伴和小詹一样想找个人一起刷题呢?欢迎和小詹一起定期刷leetcode,每周一周五更新...

1173
来自专栏葬爱家族

Android高德之旅(14)行政区划搜索废话简介总结

前后两千万,拍照更清晰。大家好,这里是OPPO R11独家冠名赞助播出的大型情感类电视连续剧《Android高德之旅》,我是主持人大公爵。(开篇占位)

1551
来自专栏take time, save time

你所能用到的数据结构(八)

十一、不能被应用的理论不是好研究 前面介绍了堆栈的一些小小的理论模型,那么这样一个东西有什么作用呢?实际中不可能有那么一辆停在站台前方堵死的火车的,即使有,也...

2694
来自专栏zaking's

js算法初窥03(搜索及去重算法)

1272
来自专栏Python数据科学

如何用Python递归地思考问题?

递归是一个很经典的算法,在实际中应用广泛,也是面试中常常会提到的问题。本文就递归算法介绍如何在Python中实现递归的思想,以及递归在Python中使用时的一些...

5275
来自专栏Python入门

十五道Python小案例,学会这些,Python基础已过关!

分析:可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。

4654
来自专栏技术墨客

Java函数式开发——优雅的Optional空指针处理

    在Java江湖流传着这样一个传说:直到真正了解了空指针异常,才能算一名合格的Java开发人员。在我们逼格闪闪的java码字符生涯中,每天都会遇到各种nu...

1112
来自专栏我的小碗汤

一文带你读懂:最小栈问题

设一个变量int min = -1; 当一个元素进入栈时,把最小值的下标记录成0,后面进来的数和stack[min]做比较,如果大于等于当前的最小值,那就不做变...

1083
来自专栏AI科技大本营的专栏

送书 | 跟我一起学《流畅的Python》

本文引自图灵新书《流畅的Python》的第一章——Python数据模型。本书由奋战在Python开发一线近20年的Luciano Ramalho执笔,Victo...

3824

扫码关注云+社区