一千个读者,有一千个哈姆雷特。
此系列文章将会从函数的执行机制、鲁棒性、函数式编程、设计模式等方面,全面阐述如何通过 JavaScript 编写高质量的函数。
一、引言
如何通过 JavaScript 编写高质量的函数,这是一个很难回答的问题,不同人心中对高质量有自己的看法,这里我将全面的阐述我个人对如何编写高质量函数的一些看法。看法可能不够全面,也可能会有一些错误的见解,欢迎一起讨论,就像过日子的人,小吵小闹总会不经意的出现,一颗包容的心莫过于是最好的 best practice 。
我打算用几篇文章来完成《如何编写高质量的 JS 函数》 这个系列。
主要从以下几个方面进行阐述:
- 函数(一切皆有可能)
- 函数的命名
- 函数的注释
- 函数的复杂度
- 函数的鲁棒性(防御性编程)
- 函数的入参和出参(返回)
- 如何用函数式编程打通函数的任督二脉
- 如何用设计模式让函数如虎添翼
- 编写对 V8 友好的函数是一种什么 style
- 前端工程师的函数狂想录
本篇只说第一节 函数 ,擒贼先擒王,下面我们来盘一盘函数的七七八八。
二、函数(一切皆有可能)
函数二字,代表着一切皆有可能。
我们想一下:我们用的函数究竟离我们有多远。就像打麻将一样,你觉得你能像雀神那样,想摸啥就来啥么(夸张修辞手法)。
天天和函数打交道,函数出现的目的是什么?再深入想,函数的执行机制是什么?下面我们就来简单的分析一下。
1、函数出现的目的
函数是迄今为止发明出来的用于节约空间和提高性能的最重要的手段。
PS: 注意,没有之一。
2、函数的执行机制
有句话说的好,知己知彼,百战不殆。想要胜利,一定要非常的了解敌人。JS 肯定不是敌人啦,但是要想掌握 JS 的函数,要更轻松的编写高质量的函数,那就要掌握在 JS 中函数的执行机制。
怎么去解释函数的执行机制呢?
先来模仿一道前端面试题:输入一个 url 后,会发生什么?
执行一个函数,会发生什么?
参考下面代码:
![c9f1e281ed31e290cb792ce00bf129ff.png](https://img-blog.csdnimg.cn/img_convert/c9f1e281ed31e290cb792ce00bf129ff.png)
这道面试题要是交给你,你能答出多少呢?
如果让我来答,我大致会这样说:
首先我会创建一个函数。如果你学过 C++ ,可能会说我要先开辟一个堆内存。
所以,我会从创建函数到执行函数以及其底层实现,这三个层次进行分析。
(1)创建函数
函数不是平白无故产生的,需要创建。创建函数时会发生什么呢?
第一步:开辟一个新的堆内存
每个字母都是要存储空间的,只要有数据,就一定得有存储数据的地方。而计算机组成原理中,堆允许程序在运行时动态地申请某个大小的内存空间,所以你可以在程序运行的时候,为函数申请内存。
第二步:创建一个函数 say ,把这个函数体中的代码放在这个堆内存中。
函数体是以字符串的形式放在堆内存中的。
为什么呢?我们来看一下 say 函数体的代码:
![98ed9e6d01e0abdf358f83fe412f8099.png](https://img-blog.csdnimg.cn/img_convert/98ed9e6d01e0abdf358f83fe412f8099.png)
这些语句以字符串的形式放在堆内存中比较好,因为没有规律。如果是对象,由于有规律,可以按照键值对的形式存储在堆内存中。而没规律的通常都是变成字符串的形式。
第三步:在当前上下文中声明 say 函数(变量),函数声明和定义会提升到最前面
注意,当前上下文,我们可以理解为上下文堆栈(栈),say 是放在堆栈(栈)中的,同时它的右边还有一个堆内存地址,用来指向堆中的函数体的。
PS: 建议去学习一下数据结构,栈中的一块一块的,我们称为帧。你可以把栈理解中 DOM 树,帧理解为节点,每一帧( 节点 )都有自己的名字和内容。
第四步:把开辟的堆内存地址赋值给函数名 say
这里关键是把堆内存地址赋值给函数名 say 。
下面我画了一个简单的示意图:
![626c3cf6aadf20c1d1f135624dcc2f0f.png](https://img-blog.csdnimg.cn/img_convert/626c3cf6aadf20c1d1f135624dcc2f0f.png)
结合上图 say 右边的存