[关闭]
@Chiang 2020-05-31T08:49:31.000000Z 字数 3370 阅读 503

函数

JavaScript 2020-05


函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。

概述

函数的声明

JavaScript 有三种声明函数的方法。

function 命令

function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

  1. function print(s) {
  2. console.log(s);
  3. }

上面的代码命名了一个print函数,以后使用print()这种形式,就可以调用相应的代码。这叫做函数的声明(Function Declaration)。

函数表达式

除了用function命令声明函数,还可以采用变量赋值的写法。

  1. var print = function(s) {
  2. console.log(s);
  3. };

这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式。

采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

  1. var print = function x(){
  2. console.log(typeof x);
  3. };
  4. x
  5. // ReferenceError: x is not defined
  6. print()
  7. // function

上面代码在函数表达式中,加入了函数名x。这个x只在函数体内部可用,指代函数表达式本身,其他地方都不可用。这种写法的用处有两个,一是可以在函数体内部调用自身,二是方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)。因此,下面的形式声明函数也非常常见。

  1. var f = function f() {};

需要注意的是,函数的表达式需要在语句的结尾加上分号,表示语句结束。而函数的声明在结尾的大括号后面不用加分号。总的来说,这两种声明函数的方式,差别很细微,可以近似认为是等价的。

Function 构造函数

第三种声明函数的方式是Function构造函数。

  1. var add = new Function(
  2. 'x',
  3. 'y',
  4. 'return x + y'
  5. );
  6. //等同于
  7. function add(x, y) {
  8. return x + y;
  9. }

上面代码中,Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数。

你可以传递任意数量的参数给Function构造函数,只有最后一个参数会被当做函数体,如果只有一个参数,该参数就是函数体。

  1. var foo = new Function(
  2. 'return "hello world";'
  3. );
  4. // 等同于
  5. function foo() {
  6. return 'hello world';
  7. }

Function构造函数可以不使用new命令,返回结果完全一样。

总的来说,这种声明函数的方式非常不直观,几乎无人使用。

函数的重复声明

如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。

  1. function f() {
  2. console.log(1);
  3. }
  4. f() // 2
  5. function f() {
  6. console.log(2);
  7. }
  8. f() // 2

上面代码中,后一次的函数声明覆盖了前面一次。而且,由于函数名的提升(参见下文),前一次声明在任何时候都是无效的,这一点要特别注意。

圆括号运算符,return语句和递归

调用函数时,要使用圆括号运算符。圆括号之中,可以加入函数的参数。

  1. function add(x, y) {
  2. return x + y;
  3. }
  4. add(1, 1); // 2

上面代码中,函数名后面紧跟一对圆括号,就会调用这个函数。

函数体内部的return语句,表示返回。JavaScript 引擎遇到return语句,就直接返回return后面的那个表达式的值,后面即使还有语句,也不会得到执行。也就是说,return语句所带的那个表达式,就是函数的返回值。return语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回undefined

函数可以调用自身,这就是递归(recursion)。下面就是通过递归,计算斐波那契数列的代码。

  1. function fib(num) {
  2. if (num === 0) return 0;
  3. if (num === 1) return 1;
  4. return fib(num - 2) + fib(num - 1);
  5. }
  6. fib(6) // 8

上面代码中,fib函数内部又调用了fib,计算得到斐波那契数列的第6个元素是8。

第一等公民

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。

  1. function add(x, y) {
  2. return x + y;
  3. }
  4. // 将函数赋值给一个变量
  5. var operator = add;
  6. // 将函数作为参数和返回值
  7. function a(op){
  8. return op;
  9. }
  10. a(add)(1, 1)
  11. // 2

函数名的提升

JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。

  1. f();
  2. function f() {}

表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript 就会报错。

  1. f();
  2. var f = function (){};
  3. // TypeError: undefined is not a function

上面的代码等同于下面的形式。

  1. var f;
  2. f();
  3. f = function () {};

上面代码第二行,调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。

注意,如果像下面例子那样,采用function命令和var赋值语句声明同一个函数,由于存在函数提升,最后会采用var赋值语句的定义。

  1. var f = function () {
  2. console.log('1');
  3. }
  4. function f() {
  5. console.log('2');
  6. }
  7. f() // 1

上面例子中,表面上后面声明的函数f,应该覆盖前面的var赋值语句,但是由于存在函数提升,实际上正好反过来。

函数的属性和方法

name 属性

函数的name属性返回函数的名字。

  1. function f1() {}
  2. f1.name // "f1"

如果是通过变量赋值定义的函数,那么name属性返回变量名。

  1. var f2 = function () {};
  2. f2.name // "f2"

但是,上面这种情况,只有在变量的值是一个匿名函数时才是如此。如果变量的值是一个具名函数,那么name属性返回function关键字之后的那个函数名。

  1. var f3 = function myName() {};
  2. f3.name // 'myName'

上面代码中,f3.name返回函数表达式的名字。注意,真正的函数名还是f3,而myName这个名字只在函数体内部可用。

name属性的一个用处,就是获取参数函数的名字。

  1. var myFunc = function () {};
  2. function test(f) {
  3. console.log(f.name);
  4. }
  5. test(myFunc) // myFunc

上面代码中,函数test内部通过name属性,就可以知道传入的参数是什么函数。

length 属性

toString()

函数作用域

定义

函数内部的变量提升

函数本身的作用域

参数

概述

参数的省略

传递方式

同名参数

arguments 对象

函数的其他知识点

闭包

立即调用的函数表达式(IIFE)

eval命令

基本用法

eval的别名调用


参考资料:
函数

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注