JS —— 函数和方法
本文按照 Mozilla 贡献者基于 CC-BY-SA 2.5 协议发布的以下文章改编:
- https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Functions
- https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Build_your_own_function
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions
在 JS 中另一个基本概念是函数, 它允许你在一个代码块中存储一段用于处理单任务的代码,然后在任何你需要的时候用一个简短的命令来调用,而不是把相同的代码写很多次。在本文中,我们将探索函数的基本概念,如基本语法、如何定义和调用、范围和参数。
函数的引入
现有有一个大于等于 2 的正整数 a ,现在我们需要判断这个数字是不是质数。质数是只有 1 和它自己两个因数的数。对于所有的大于 2 的数,我们需要枚举那些大于 2 小于等于根号 a 的所有数字,一一确定它们是不是 a 的因数,如果都找不到那么它肯定是质数。
1 | let a = 114514; // 很明显这是个合数 |
看起来很合理,但是如果有三个数,分别是 a, b, c。或者有三十个数,你是不是要把上面那个 if else 和循环都复制一下?这么做的话太麻烦了。所以我们有更好的方法:函数 (function) 。这就是上面写成函数的例子:
1 | function judgePrime(a) { |
我们可以看出函数的结构:
1 | function 函数名(参数) { |
一个函数定义(也称为函数声明,或函数语句)由一系列的 function 关键字组成,依次为:
- function 关键字。
- 函数的名称。
- 函数参数列表,包围在括号中并由逗号分隔。
- 定义函数的 JavaScript 语句,用大括号{}括起来。
比如我们从定义一个简单的乘方运算函数开始,这个函数可以得到给定数字的平方:
1 | function square(number) { |
函数 square 使用了一个参数,叫作 number 。这个函数只有一个语句,它说明该函数将函数的参数 number 自乘(就是平方)后返回。函数的 return 语句确定了函数的返回值。返回值就像普通的数值一样。比如下面这个代码:
1 | function square(number) { |
它执行了这样的操作:
- 声明一个 square 函数。
- 调用函数, 将 3 作为参数传给函数,此时函数的 number 变量的值变成了 3 。
- 程序继续向下运行,计算 number * number 的值。
- 将计算出来的值返回回来。
- 然后
let x = square(3)
就相当于let x = 9
,x 就变成了 9 了。
可以非常清楚的非常清楚地看到,调用函数的方式是在函数名后加上一对括号,括号中包含参数。如果有多个参数就可以用逗号分隔:
1 | function add(a, b) { |
有些函数可以没有返回值,比如我有一个输出的函数:
1 | function print(x) { |
这种情况下面,函数将会返回默认值 undefined
。
函数的特性
赋值
好像有点跑题,不过这个也是有关联的。
我们都知道那些基本数据是在赋值的时候,是可以被复制的:
1 | > let val1 = 23; |
但是,不同于之前的那些基本数据,比如字符串和数字,数组和对象在使用 =
进行赋值的时候,其实没有进行复制,而是让两者指向同一个数组或者对象,就像是同一个数组或者对象有了两个名字,例如:
1 | > let arr1 = [12, 22]; |
类似的,对象也是一样。如果要复制的话,可以使用 Array.from()
方法。
1 | > let arr1 = [12, 22]; |
参数
函数的参数传入之后,如果我们修改了参数,会发生什么?
直接跑一遍代码,清楚明了:
1 | function test(x) { |
输出:
1 | Before called the function, a = 1 |
是不是非常清楚,函数 test
改了一个寂寞,最后 a 的值还是没变,其实函数的参数传递,就类似对每个参数进行了一次赋值,函数里面的变量和外面的不是同一个变量。类似的,如果传入的是数组和对象,情况就不太一样了:
1 | function test(x) { |
输出:
1 | Before called the function, a = 1,3,5 |
定义域
函数里面的定义的值不会占用掉外面的名称,更不会占用掉其他函数里面定义的变量名。比如说这个:
1 | let x = "x global"; |
输出:
1 | x in test1() |
函数和对象
我们之前谈过了对象,现在是进一步扩充它的时候了。
函数作为值
函数可以被作为一个值赋值给一个变量:
1 | function add(a, b) { |
更加常用的是这么写:
1 | let myadd = function (a, b) { |
这种情况下面会创造一个匿名函数,并且将这个函数赋值给 myadd 变量,使它的值成为一个函数,并且可以使用函数的方式调用。
方法
那么,既然函数可以赋值给普通变量,那么也可以赋值给一个对象成员了。一个函数成员叫做方法。,比如我们举个例子:
1 | let cat = { |
你也许在我们的方法里注意到了一些奇怪的地方, this
到底是什么?
this
指的是正在运行这个函数的对象。你可能会很奇怪为什么不能直接使用 cat
来代指而是用这个奇怪的关键字。这是因为对象是可以被复制 (更准确地说是继承) 的,这可以构造一个新的对象。如果直接使用 cat
就会导致这个代码不能指向被复制后的对象。我们以后会在进阶的 JS 里面提到。下面举个简单的例子, Object.create()
方法:
1 | let cat1 = { |
输出:
1 | My name is Ket |
可以看到最后那个函数并没有指向正确的对象,导致输出了错误的值。对象之间的继承是一件很复杂的事情。事实上最常用的是构造器函数。推荐阅读 https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects 当中列出的文章。
大家都是函数
那么,我们可以了解了,之前那些不明不白的什么 console.log()
什么 join()
什么 split()
什么 Math.floor()
,通通都是函数或者方法,就和我们写出来的一样。
下一步
到了现在 JS 的基本语法你都已经掌握了,下面到了要拿这个新的工具去做点什么的时候了。
下一章: DOM!我们将会正式用 JS 去对网页做点事情。
练习
没啥好些的,接下来就直接是 DOM 了。我也累了,改天再更点啥吧。