JavaScript 函数
在 JavaScript中, 函数 是头等 (f irs t-class) 对象,因为它们可以像任何其他对象一样具有 属性 和 方法 。它们与其他对象的区别在于 函数 可以被 调用 。简而言之,它们是 Function 对象。(MDN)
函数 就是一段 代码 片段, 调用 函数 就是执行 函数 中的 代码 。
1. 函数 的使用
1.1 语法
函数 使用前通常与变量一样需要先进行声明,用 function 关键字定义 函数 。
// 常见的 函数 的定义方式 function 函数 名 ( 参数 , 参数 , ... ) { 代码 片段 ; return 返回值 ; } // 调用 函数 (执行 函数 中的 代码 ) var 函数 的返回值 = 函数 名 ( 参数 , 参数 , ... ) ;调用 函数 就是执行 函数 中的 代码 参数是 调用 函数 的时候传递过去的,在 函数 执行过程中可以访问到 函数 执行完毕后可以有 一个 返回值, 调用 函数 的地方可以接收到这个返回值
1.2 调用 函数
使用 函数 名() 的方式即可 调用 一个 函数
以下是 一个 最简单的 函数 :
function say ( ) { console . log ( 'hello' ) ; } say ( ) ; // 输出 :"hello"
调用 这个 函数 就会在控制台 输出 hello 字符串。
这个 函数 没有返回值, 默 认会返回 一个 undefined 。
1.3 带有参数与返回值的 函数
在声明 函数 的时候,可以对参数也做上说明
假设有 一个 需求, 需要 一个 计算三角形周长的 函数 。
计算三角形周长则需要知道三角形三条边各自的长度,然后将他们求和。
定义 函数 的时候就可以将三条边作为参数进行声明。
function calcPerimeter ( a , b , c ) { // a, b, c 分别代表三条边 var sum = a + b + c ; return sum ; } // 调用 函数 并将返回值赋值给perimeter var perimeter = calcPerimeter ( , , ) ;
在 调用 函数 的时可以 传递值 过去,这些值可以在 函数 中被访问。
在以上 calcPerimeter 函数 被 调用 的时,传递了 3, 4, 5 三个值。
三个值对应到 函数 声明时定义的三个参数 a, b, c 。
所以 函数 执行过程中 sum 的值为 3 + 4 + 5 ,即 12 ,随后 sum 被作为返回值进行返回。
最终变量 perimeter 也会被赋值为12。
2. 怎么运用 函数
2.1 合理包装 内容
函数 可以对 代码 进行封装,让逻辑更加清晰。
比如如下 代码 块:
// 改写前 var num = ; var flag = false ; var i ; var len ; for ( i = , len = num - ; i <= len ; i ++ ) { if ( num % i === ) { flag = true ; break ; } } console . log ( flag ) ;
以上 代码 第一眼可能无法看出具体在做什么,仅需 要做 一点 修改 ,就能有所改善。
// 改写后 function isPrimeNumber ( num ) { var flag = false ; var i ; var len ; for ( i = , len = num - ; i <= len ; i ++ ) { if ( num % i === ) { flag = true ; break ; } } return flag ; } var num = ; var result = isPrimeNumber ( num ) ; console . log ( result ) ;
改写后的 代码 似乎多了几行,但是将其中核心部分包装成了 函数 。
通过 isPrimeNumber 函数 名可以很容易的了解到这一段 代码 作用是用来判断 一个 数是否为 质数 。
当然有个前提就是起 一个 可以让大部分人看得懂 的 函数 名。
2.2 优秀的 函数 名
优秀的 函数 名可以帮助他人更容易理解 代码 ,同时当自己一段时间后再回头看 代码 时,能更容易进入当时写 代码 时候的思维模式等。
这里提供几个 函数 命名的建议,具体的命名可以根据团队规范、个人成长等做调整。
2.2.1 拼写准确
准确的拼写十分重要,绝大多数情况下 函数 名都会是英文单词组成的。
当然许多时候手一快可能就少了 一个 字母,或者错将 wrap 进行乾坤大挪移拼写成了 warp 。
许多情况是无法避免的,经常需要自检。
当然可以借助一些单词的检查 插件 ,如 Visual Stu dio Code 可以借助 Code Spell Checker 插件 来检查单词的正确性。
再者碰到想起的 函数 名但是单词拼写不出来,尽可能翻词典,日积月累能有大量的词汇沉淀。
2.2.2 尽量不使用拼音或者混用拼写
尽量不要使用拼音或者是首字母缩写。
以下 函数 名或许会造成困扰:
function jslsh ( ) { } function jsNumber ( ) { }
以上是 计算两数和 函数 的命名,可能只有天和地知道这个是什么意思。
当然,如果是自己写 demo 或者测试 代码 的时候,其实不需要考虑这么多。
2.2.3 有“状态”的 函数 名
如碰到 函数 功能 是判断 是否 、 有没有 、 可以 的时候,可以带上一些前缀,比如:
// 是否登入 function isLogin ( ) { }
同时可以合理的使用动词,比如 打开 文件 就可以使用 openFile 函数 名,具体的状态可以根据语境、 函数 作用、个人习惯等做调整使用。
2.2.4 合理使用缩写
使用词语的缩写尽量使用通用的缩写
如:
pwd - password mgr - manager del - delete …
这些缩写大部分开发者是可以看的懂的缩写。
3. 函数 示例
3.1 计算圆的面积
分析:根据圆面积公式 S=π·r·r,其中 S 就是要求的值,即 函数 的返回值,π 是常量(固定的 一个 值),半径r是未知数,所以r就可以设计成参数
function circleArea ( r ) { var pi = ; return pi * r * r ; } // 计算半径为10的圆的面积 var area = circleArea ( ) ;
3.2 判断某个DOM元素是否含有某个类名
分析:
某个DOM 和 某个类名 可以说明有两个未知量,可以设计成两个参数。
根据描述也可以确定 一个 某个DOM 的类型是个 DOM 对象, 某个类名 是个字符串
只要拿到这个DOM的 class 属性 ,判断里面是不是含有这个类型即可得到结果
function hasClass ( el , className ) { // el 是 element的缩写,表示 一个 dom元素 // 如果没有元素 则返回 if ( ! el ) { return false ; } // 根据空格分割成数组 // 可以不使用 split 方法 ,使用字符串也可以用indexOf匹配 var classList = el . className . split ( ' ' ) ; // 判断是否存在 if ( classList . indexOf ( className ) >= ) { return true ; } return false ; }
4. 函数 的其他知识
以下扩展 内容 可能需要一定的知识积累,遇到不懂的地方可以停下脚步,先学习下一章节
4.1 函数 表达式
以上篇幅的 函数 其实都通过 函数 声明 的方式来定义,还有一种方式就是使用 函数 表达式定义 函数 。
// 函数 声明 function add ( a , b ) { return a + b ; } // 函数 表达式 var add = function ( a , b ) { return a + b ; } ;
通过上述例子可以看出写法上的区别就是 函数 表达式 是将 函数 赋值给了变量。
这两种方式创建的 函数 最大的区别在于, 不能提前 调用 使用 函数 表达式创建的 函数
光看句子有点抽象,举个例子?:
var num1 = add1 ( , ) ; var num2 = add2 ( , ) ; // 函数 声明 function add1 ( a , b ) { return a + b ; } // 函数 表达式 var add2 = function ( a , b ) { return a + b ; } ;
上面一段 代码 在执行的时候会报 add2 is not a function 的 错误 ,表示 add2 不是 函数 ,也就是说 add2 不能被提前使用,而 add1 可以。
具体原因可以查看 执行上下文 章节。
4.2 函数 作用域
函数 有他自己的作用域, 函数 内声明的变量等 通常情况下 不能被外部访问,但是 函数 可以访问到外部的变量或者其他 函数 等
var a = ; function fn ( ) { var b = ; console . log ( a ) ; // 输出 :1 console . log ( b ) ; // 输出 :2 } fn ( ) ; console . log ( b ) ; // ReferenceError: b is not defined
执行以上 代码 会报 b is not defined 错误 。
4.3 匿名 函数
没有名字的 函数 就是 一个 匿名 函数
var fn = function ( ) { console . log ( '我是 一个 匿名 函数 ' ) ; } ;
除了在 函数 表达式 中会出现匿名 函数 ,还有许多场景。
相对常见的 一个 就是 自执行匿名 函数 ,MDN官方翻译为 立即 调用 函数 表达式 。
自执行 就是这个 函数 声明后就会立即执行,自执行的匿名 函数 通常会被用来 形成独立的作用域 。
如:
( function ( ) { var num = ; alert ( num ) ; } ) ( ) ;
这是 一个 自执行的匿名 函数 ,这个匿名 函数 是被包裹了一段括号后才被 调用 的。
以下这段 代码 会报错:
// 报错 function ( ) { var num = ; alert ( num ) ; } ( ) ;
浏览器会告诉你必须给 函数 一个 名字。
通过括号包裹一段 函数 ,让 js引擎 识别成他是 一个 函数 表达式,再对他进行执行,就不会报错,这是加括号的原因。
同理,可以使用 + , ! 等运算符代替括号,让 一个 匿名 函数 成为 一个 函数 表达式即可。
大部分第三方框架都会通过 一个 自执行的匿名 函数 包裹 代码 ,与浏览器全局环境隔离,避免污染到全局环境。
4.4 具有 函数 名的 函数 表达式
函数 表达式进行声明的时候也可以使用具名 函数
var count = function fn ( num ) { console . log ( '我是 一个 函数 ' ) ; } ;
以上这段 代码 是不会报错的,但是不能通过 fn 访问到 函数 ,这里的 fn 只能在 函数 内部进行访问,通常在使用递归的形式做计算的时候会用到这种写法。
var count = function fn ( num ) { if ( num < ) { return num ; } return fn ( num - ) + num ; } count ( ) ;
上面这个例子,就是在 函数 内部访问 fn 调用 自己,使用递归的形式求和。
注:递归相关的知识可以参考相关文献进行学习
4.5 arguments
arguments 是 一个 对应于传递给 函数 的参数的类数组对象。(MDN)
通常情况下 函数 都具有 arguments 对象,可以在 函数 内部直接访问到。
他是 一个 类数组,即长得很像数组,成员都是用数字编号,同时具有 length 属性 。
arguments 中存放着当前 函数 被 调用 时,传递过来的所有参数,即便不声明参数,也可以通过 arguments 取到传递过来的参数。
function sum ( ) { console . log ( arguments ) ; } sum ( , , , ) ;
执行上述 代码 ,可以看到在控制台 输出 了 一个 对象,存放的就是所有传递过去的参数,利用这一特性,就可以不限制参数个数,或者让 函数 做中转站( 拦截 函数 ),利用 arguments 将参数传递给另 一个 函数 。
如, 一个 不确定 用户 输入的参数个数的 求和函数 :
function sum ( ) { var total = ; var i ; var len ; for ( i = , len = arguments . length ; i < len ; i ++ ) { total += arguments [ i ] ; } return total ; } var total = sum ( , , , , ) ; console . log ( total ) ; // 输出 :25
通过循环遍历 arguments 对象,就可以得到所有参数,然后做累加就可以达到求和的目的。
4.6 函数 和 方法
方法 在本质上是个 函数 。
通常都能听到“ 调用 一下某个 方法 ”,“取到某个 方法 的返回值”,这里的 方法 其实就是 一个 函数 。
一般 方法 是用来描述对象的某个行为的,但是平时我们会混用,口头交流的时候会经常把 函数 直接称作 方法 。
只要自己理解,不需要去纠结 函数 和 方法 到底是什么,也不用特意纠正别人的说法,大家都能听得懂就行。
4.7 JS DOC 注释
使用 JS DOC 描述 函数 是非常良好的习惯,良好的 JS DOC 书写还可以使用工具 快速 生成 文档。
JS DOC 对 函数 的描述大体如下:
/** * 这是这个求幂 函数 计算 x 的 y 次方 * @p ara m {Number} x - 底数 * @p ara m {String} y - 指数 */ function pow ( x , y ) { // ... }
除此之外还可以描述返回值等。
4.8 纯 函数 与副作用
所谓纯 函数 ,就是没有副作用的 函数
一个 函数 从执行开始到结束,没有对外部环境做任何操作,即对外部环境没有任何影响(没有副作用),这样的 函数 就是纯 函数 。
纯 函数 只负责输入 输出 ,对于一种输入只有一种 函数 返回值。
如果 函数 中存在 Math.random 这种影响返回值的 函数 ,也不能算是纯 函数 。
// 纯 函数 function add ( a , b ) { return a + b ; } // 非纯 函数 var person = { name : '小明' } ; function changeName { person . name = '小红' ; // 影响了 函数 外的 内容 ,产生了副作用 }
4.9 构造 函数
当 一个 函数 与 new 关键字一起被 调用 的时候,就会作为 一个 构造 函数 。
function Person ( name , age ) { this . name = name ; this . age = age ; } Person . prototype . say = function ( ) { console . log ( '我是' + this . name ) ; } ; var person = new Person ( '阿梅' , ) ; person . say ( ) ; console . log ( person ) ;
可以看到当 函数 作为构造 函数 调用 的时候, 默 认返回的是 一个 对象。
细心的读者仔细观察就能发现,构造 函数 的 默 认返回值是 函数 体内的 this。
事实上构造 函数 的执行有一定流程:
创建 一个 空对象,将 函数 的this指向这个空对象 执行 函数 如果 函数 没有指定返回值,则直接返回 this(一开始创建的空对象),否则返回指定返回值
理解这个流程,就能理解构造 函数 的返回值。
具体的 函数 的 prototype 属性 等可以参阅 原型 章节。
5. 小结
函数 特性相对较多,也是 JavaScript 的核心之一。
函数 可以用于 封装代码 ,提供 代码 的复用率和可读性,在大部分情况下,当两段 代码 具有超高相似度时,应当设计成 函数 ,不同的部分使用参数进行区分。
JavaScript 对象 ? ?JavaScript 表达式声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did92398