函数是JavaScript中最基本的要素,是基础中的基础,如果你对函数仅仅是一知半解,那基于函数构建的JavaScript知识体系也不会牢固。本人的观点:基础是最重要的东西,所以一定要彻底搞清楚。
一、创建函数的方式
1.1 函数申明
单纯的使用function+函数名:
function a() {...}
两个关键点:
一、强调单纯,申明函数的代码不归属于任何表达式
二、必须指定函数名
此外函数申明可以被JavaScript解释器提前执行,所以以下代码不会报错:
// 打印:test
a();
function a() {
console.log("test");
}
1.2 函数表达式
1.2.1什么是表达式
理解函数表达式,首先要搞清楚什么是表达式。从数学角度看,表达式的定义:由数字、运算符合、分组操作符()、变量组成的以求值为目的组合。关键词:以求值为目的。在编程语言中表达式是用来计算出结果的。
1.2.2 函数表达式的定义
在表达式中定义的函数。
由定义可知,函数表达式并没有一个固定的写法,判断准则只有一条:函数是否定义在表达式中,或作为表达式存在。举例说明:
// 将代码块“function() {...}”的计算结果(实质上是匿名函数的引用)赋值给a
var a = function() {..};
// 将代码块“function() {...}”的计算结果(实质上是匿名函数的引用)作为t的参数
function t(function(){
});
// 分组操作符中的函数表达式,JavaScript解释器会自动计算()内的内容
(function() {});
// 指定函数名的函数表达式叫:命名函数表达式
var a = function b() {...};
以上示例中的function() {...}
与function b() {...}
都是函数表达式,但是单独使用的function() {...}
与function b() {...}
就不是函数表达式了,由此可知,函数表达式必须拥有一个上下文的环境,在此环境中,作为表达式的函数才是函数表达式,本质上就是一个表达式而已。
函数表达式中的函数名可以省略,没有指定函数名的函数叫匿名函数,匿名函数不能单独存在,只能作为表达式存在。
1.3 命名函数表达式
1.3.1 存在的意义
一般情况下函数表达式不需要指定函数名,只作为匿名函数存在,但是有一种场景必须指定名字才行:函数表达式中调用函数本身。
var a = function b(n) {
n--;
if (n > 5) {
b(n);
}
};
你可能觉得上面的例子没有说服力,因为完全可以通过函数申明的方式实现;但是下面的例子,只能通过命名函数表达式来实现:
function a(function b(n) {
n--;
if (n > 5) {
b(n);
}
});
该例中,函数表达式作为参数存在,如果想调用自身,只能指定函数的名字。(arguments.callee()可以实现调用函数本身,但从es5开始禁止在strict mode中使用)
1.3.2 ie中的bug
命名函数表达式在ECMAScript规范中指出,函数名对外不可见,只能在函数内部使用,例如:
var a = function b() {};
// 打印:b is not defined
console.log(b);
但是在ie8及ie8以下版本中,函数名却可以访问,如果你在ie8中使用了命名函数表达式,一定要注意该bug带来的影响,好在从ie9开始,微软修复了这个bug。
二、IIFE(立即调用的函数表达式)
在JavaScript开发中,最头疼的问题就是命名冲突,为了解决这个问题,我们首先想到的是将全部的属性与方法挂在一个全局变量上,实现单全局变量;为了追求极致,人们开始寻找0全局变量的解决方案,首先想到的就是匿名函数,为什么呢?逻辑很简单,因为它没有名字。而匿名函数只能作为函数表达式存在,所以就有了以下代码:
(function() {...})();
function() {...}
是一个匿名函数,通过分组操作符包裹起来作为表达式存在,而这个表达式的计算结果显然是一个函数,既然是函数就可以通过()来调用执行。
三、为什么说函数也是对象
在JavaScript中有一个关键字:Function,注意F大写,它是一个构造方法,我们创建的任何函数本质上都是Function的实例,所以函数是实实在在的对象。什么时候将函数视为对象呢?在使用函数的属性或方法时,虽然有点绕,但是JavaScript就是这么设计的,函数也拥有属性与方法,比如apply、call等,这里不做展开,只给大家一个初步的概念。