基本概念
JavaScript中的函数使用function关键字来声明,后跟一组参数以及函数体。函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。
例子:
function demo(n) {
return n + 1;
}
demo(1);
//输出:2
没有重载
如果函数中定义了两个名字相同的函数,则该名字只属于后定义的函数。
例子:
function demo(n) {
return n + 1;
}
function demo(n) {
return n + 2;
}
demo(1);
//输出:3
函数声明与函数表达式
创建函数最常用的两个方法是函数表达式和函数声明,两者在语法形式上的区别只有一点:函数声明必须带有标示符(即函数名),而函数表达式可以省略。
例子:
函数表达式
let fn1 = function(n1, n2) {
console.log(n1 + n2)
};
函数声明
function fn2(n1, n2) {
console.log(n1 + n2)
}
如果函数是作为赋值表达式的一部分,那它就是一个函数表达式:
例子:
let num = function(n1, n2) {
return n1 + n2;
}(1, 1);
let obj = new function(name) {
this.name = name;
}("Tom");
另外,立即调用函数也属于函数表达式,因为括号运算符里面只能包含表达式:
例子:
//两种立即调用函数方式
(function(n1, n2) {
console.log(n1 + n2);
}(1, 1)); //输出:2
(function(n1, n2) {
console.log(n1 + n2);
})(1, 1); //输出:2
如果函数被包含在一个函数体内,或者位于程序的最外部的话,那它就是一个函数声明:
例子:
//程序最外部
function fn1() {
console.log("ok");
};
//函数内部
(function() {
function fn2() {
console.log("ok");
}
})();
*区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位置:如果 function 是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
命名函数表达式
函数表达式可以省略函数名,但也可以拥有函数名,如果拥有函数名,这个名字只在当前函数作用域内有效。
例子:
let fn = function num() {
console.log(typeof(num)); //输出:function
}
fn();
console.log(typeof(num)); //输出:undefined
console.log(typeof(fn)); //输出:function
在 ES5 的严格模式下,出于安全方面的考虑,arguments.callee(函数内部可用,指向当前函数)属性被废弃,因此可以使用命名函数表达式来实现递归。
例子:
//arguments.callee 实现递归
let fn1 = function(num) {
if (num <= 0) {
return 0;
} else {
return num + arguments.callee(num - 1);
}
}
fn1(5); //输出:15
//命名函数表达式实现递归
let fn2 = function numFn(num) {
if (num <= 0) {
return 0;
} else {
return num + numFn(num - 1);
}
}
fn2(5); //输出:15
函数声明优先
JavaScript 代码在执行前都要进行编译,编译阶段中的一部分工作就是找到所有的声明( var 和 function 声明),提升到当前作用域的最上面。提升的只是声明本身,而赋值或其他运行逻辑会留在原地等待执行。
例子:
fn1();
fn2();
function fn1() {
console.log(1);
}
var fn2 = function() {
console.log(2);
}
由于声明提升的存在,上面例子中的代码在编译阶段会被编译成下面的形式去执行:
function fn1() {
console.log(1);
}
//默认 fn2 赋值为 undefined
var fn2;
fn1(); //输出:1
fn2(); //输出:TypeError
fn2 = function() {
console.log(2);
}
*函数 fn2 由于在执行前没有赋值,而导致抛出 TypeError 异常。
命名函数表达式也无法在赋值之前使用
例子:
foo();
var fn = function foo() {
console.log(1);
}
编译为:
var fn;
foo(); //输出:ReferenceError
fn = function foo() {
console.log(1);
}
函数声明和变量声明的优先级
函数声明和变量声明都会被提升,但如果标识符一样,函数声明的优先级高于变量声明。
例子1:
fn();
var fn = function() {
console.log(2);
}
function fn() {
console.log(1);
}
编译为:
function fn() {
console.log(1);
}
//由于函数声明优先级高,因此这个变量声明会被忽略
var fn;
fn(); //输出:1
fn = function() {
console.log(2);
}
例子2:
var fn = function() {
console.log(2);
}
function fn() {
console.log(1);
}
fn();
编译为:
function fn() {
console.log(1);
}
//由于函数声明优先级高,因此这个变量声明会被忽略
var fn;
//但到了这里,函数声明的标识符 fn 被重新赋值
fn = function() {
console.log(2);
}
fn(); //输出:2
ES6中的箭头函数
除了函数声明与函数表达式,ES6 语法中可以使用“箭头”(=>)定义函数。
例子:
普通函数
var demo = function(n1, n2) {
console.log(n1 + n2);
}
demo(1, 1);
//输出:2
箭头函数
var demo = (n1, n2) => {
console.log(n1 + n2);
}
demo(1, 1);
//输出:2
注意
1、如果箭头函数只有一个参数,可以省略圆括号;如果没有参数或有多个参数,就需要使用一个圆括号代表参数部分。
例子:
没有参数
var demo = () => {
console.log(2);
}
demo();
//输出:2
一个参数
var demo = n => {
console.log(n);
}
demo(2);
//输出:2
多个参数
var demo = (n1, n2) => {
console.log(n1 + n2);
}
demo(1, 1);
//输出:2
2、如果箭头函数的代码只有一条语句,可以省略大括号,默认是返回该语句的计算值;如果多于一条语句,就要使用大括号将它们括起来。
例子:
一条语句
var demo = (n1, n2) => n1 + n2;
demo(1, 1);
//输出:2
多条语句
var demo = (n1, n2) => {
if (typeof n1 == "number" && typeof n1 == "number") {
return n1 + n2;
}
}
//输出:2
使用箭头函数简化回调函数
箭头函数最常用于回调函数。
例子:
普通匿名回调函数
[1, 2, 3].map(function(v) {
return v + 1;
});
//输出:[2, 3, 4]
箭头函数简化匿名回调函数
[1, 2, 3].map(v => v + 1);
//输出:[2, 3, 4]
如有错误,欢迎指出。