@Chiang
2020-05-31T08:49:31.000000Z
字数 3370
阅读 503
JavaScript
2020-05
函数是一段可以反复调用的代码块。函数还能接受输入的参数,不同的参数会返回不同的值。
JavaScript 有三种声明函数的方法。
function 命令
function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。
function print(s) {
console.log(s);
}
上面的代码命名了一个print函数,以后使用print()这种形式,就可以调用相应的代码。这叫做函数的声明(Function Declaration)。
函数表达式
除了用function命令声明函数,还可以采用变量赋值的写法。
var print = function(s) {
console.log(s);
};
这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式。
采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效。
var print = function x(){
console.log(typeof x);
};
x
// ReferenceError: x is not defined
print()
// function
上面代码在函数表达式中,加入了函数名x。这个x只在函数体内部可用,指代函数表达式本身,其他地方都不可用。这种写法的用处有两个,一是可以在函数体内部调用自身,二是方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)。因此,下面的形式声明函数也非常常见。
var f = function f() {};
需要注意的是,函数的表达式需要在语句的结尾加上分号,表示语句结束。而函数的声明在结尾的大括号后面不用加分号。总的来说,这两种声明函数的方式,差别很细微,可以近似认为是等价的。
Function 构造函数
第三种声明函数的方式是Function
构造函数。
var add = new Function(
'x',
'y',
'return x + y'
);
//等同于
function add(x, y) {
return x + y;
}
上面代码中,Function
构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数。
你可以传递任意数量的参数给Function构造函数,只有最后一个参数会被当做函数体,如果只有一个参数,该参数就是函数体。
var foo = new Function(
'return "hello world";'
);
// 等同于
function foo() {
return 'hello world';
}
Function构造函数可以不使用new命令,返回结果完全一样。
总的来说,这种声明函数的方式非常不直观,几乎无人使用。
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明。
function f() {
console.log(1);
}
f() // 2
function f() {
console.log(2);
}
f() // 2
上面代码中,后一次的函数声明覆盖了前面一次。而且,由于函数名的提升(参见下文),前一次声明在任何时候都是无效的,这一点要特别注意。
调用函数时,要使用圆括号运算符。圆括号之中,可以加入函数的参数。
function add(x, y) {
return x + y;
}
add(1, 1); // 2
上面代码中,函数名后面紧跟一对圆括号,就会调用这个函数。
函数体内部的return
语句,表示返回。JavaScript 引擎遇到return
语句,就直接返回return
后面的那个表达式的值,后面即使还有语句,也不会得到执行。也就是说,return
语句所带的那个表达式,就是函数的返回值。return
语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回undefined
。
函数可以调用自身,这就是递归(recursion)。下面就是通过递归,计算斐波那契数列的代码。
function fib(num) {
if (num === 0) return 0;
if (num === 1) return 1;
return fib(num - 2) + fib(num - 1);
}
fib(6) // 8
上面代码中,fib函数内部又调用了fib,计算得到斐波那契数列的第6个元素是8。
JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。
由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。
function add(x, y) {
return x + y;
}
// 将函数赋值给一个变量
var operator = add;
// 将函数作为参数和返回值
function a(op){
return op;
}
a(add)(1, 1)
// 2
JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。所以,下面的代码不会报错。
f();
function f() {}
表面上,上面代码好像在声明之前就调用了函数f。但是实际上,由于“变量提升”,函数f被提升到了代码头部,也就是在调用之前已经声明了。但是,如果采用赋值语句定义函数,JavaScript 就会报错。
f();
var f = function (){};
// TypeError: undefined is not a function
上面的代码等同于下面的形式。
var f;
f();
f = function () {};
上面代码第二行,调用f的时候,f只是被声明了,还没有被赋值,等于undefined,所以会报错。
注意,如果像下面例子那样,采用function命令和var赋值语句声明同一个函数,由于存在函数提升,最后会采用var赋值语句的定义。
var f = function () {
console.log('1');
}
function f() {
console.log('2');
}
f() // 1
上面例子中,表面上后面声明的函数f
,应该覆盖前面的var
赋值语句,但是由于存在函数提升,实际上正好反过来。
函数的name
属性返回函数的名字。
function f1() {}
f1.name // "f1"
如果是通过变量赋值定义的函数,那么name属性返回变量名。
var f2 = function () {};
f2.name // "f2"
但是,上面这种情况,只有在变量的值是一个匿名函数时才是如此。如果变量的值是一个具名函数,那么name属性返回function关键字之后的那个函数名。
var f3 = function myName() {};
f3.name // 'myName'
上面代码中,f3.name
返回函数表达式的名字。注意,真正的函数名还是f3
,而myName
这个名字只在函数体内部可用。
name
属性的一个用处,就是获取参数函数的名字。
var myFunc = function () {};
function test(f) {
console.log(f.name);
}
test(myFunc) // myFunc
上面代码中,函数test内部通过name属性,就可以知道传入的参数是什么函数。
参考资料:
函数