深入理解 JavaScript 匿名函数

188 阅读4分钟

函数声明与函数表达式区别

函数声明

函数声明式直接使用function关键字接一个函数名,函数名后是接收函数的形参,比如我们要定义求两个数的和的一个函数,示例如下。

function sum(num1, num2) {
    alert(num1 + num2);
}

sum(1,2); // 弹窗显示: 3

关于函数声明,它最重要的一个特征就是函数声明提升,意思就是执行代码之前先读取函数声明。不管函数声明写在前面,还是后面,都会出现函数声明的提升。

如下的代码可以正常执行:

sum(1,2); // 弹窗显示: 3

function sum(num1, num2) {
    alert(num1 + num2);
}

函数表达式

声明一个匿名函数,直接赋值给某一个事件

window.onload = function() {
    alert('Hello');
}

使用匿名函数表达式,将匿名函数赋值给一个变量。

// 声明
var func = function(){}
// 调用
func();

var show = function() {
    alert('Hello');
};

show(); // 弹窗 Hello

这种形式看起来好像是常规的变量赋值语句。但是函数表达式和函数声明的区别在于,函数表达式在使用前必须先赋值。所以这段代码执行的时候就会出错:

show(); // 无弹窗,报错: show is not a function  
var show = function() {
    alert('Hello');
};

【注意】:使用匿名函数表达式时,函数的调用语句,必须放在函数声明语句之后!!! 原因:检查装载时,会先对show变量及这个匿名函数声明,此时,还未将匿名函数赋值给show变量,如果在表达式之前调用,会报错 show is not a function

造成这种现象是因为解析器在向执行环境中加载数据时,解析器会率先读取函数声明,并使其在执行任何代码前可用;至于函数表达式,则必须等到解析器执行到它的所在的的代码行,才会真正的被解析。函数表达式中,创建的函数叫做匿名函数,因为function关键字后面没有标识符。

二者的区别

// 函数表达式(function expression) 
var f = function() {
      // f
}

// 函数声明(function declaration) 
function f() {
      // f
}

先说两者的显著区别:

  • 第一种声明方式也就是var声明方式, 函数只有在var语句声明之后才能被调用

  • 第二种声明方式也就是function声明方式, 函数可以在function声明之前被调用

这是因为,

  • 对第一种情况, 函数表达式是在函数运行阶段才赋值给变量f

  • 对第二种情况, 函数表达式是在代码运行阶段之前, 也就是代码解析阶段才赋值给标识符f 为了证明这种说法可以看下面两个例子:

对应第一种情况:

var fn = function () {
     // F1
}

console.log(fn) 
    
fn = function () {
     // F2
}

console的结果是

ƒ fn() {
  // F1
}

因为赋值发生在代码运行阶段, 代码自上而下运行console.log(fn)所在位置只能获取它之前的赋值

对应第二种情况:

function fn() {
      // F1
}

console.log(fn)
    
function fn() {
      // H2
}

console的结果是

ƒ fn() {
  // F2
}

因为赋值发生在代码解析阶段, 代码运行到console.log(fn)时解析早已完成, 而解析的结果是后面那个函数fn, 故会打印此结果

js代码的执行顺序问题 js代码在运行时,会分为两大部分———检查装载 和 执行阶段。

检查装载阶段:会先检测代码的语法错误,进行变量、函数的声明

执行阶段:变量的赋值、函数的调用等,都属于执行阶段。

匿名函数的调用方式

匿名函数,就是没有名字的函数。

表达式的调用

const add = function (x, y){
    return x + y;
}
const sum = add(1, 2)

console.log(sum)

匿名函数调用

//方式1
//这种方式尽量少用
const sum1 = function(x, y){
    return x + y;
}(1, 2);

//方式2
//推荐
const sum = (function(x, y){
    return x + y;
})(1, 2);
console.log(sum1);

//方式3
(new Function("x","y","return x+y"))(1,2)

自执行匿名函数

自执行函数,即定义和调用合为一体。我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。

下面我们来看下一些比较有趣的自执行函数表达方式:

// 下面2个括弧()都会立即执行  
(function () { /* code */ } ()) // 推荐使用这个  
(function () { /* code */ })() // 但是这个也是可以用的  
  
// 由于括弧()和JS的&&,异或,逗号等操作符是在函数表达式和函数声明上消除歧义的  
// 所以一旦解析器知道其中一个已经是表达式了,其它的也都默认为表达式了  
var i = function () { return 10; } ();  
true && function () { /* code */ } ();  
0, function () { /* code */ } ();  
  
// 如果你不在意返回值,或者不怕难以阅读
// 你甚至可以在function前面加一元操作符号  
!function () { /* code */ } ();  
~function () { /* code */ } ();  
-function () { /* code */ } ();  
+function () { /* code */ } ();  
  
// 还有一个情况,使用new关键字,也可以用,但我不确定它的效率  
// http://twitter.com/kuvos/status/18209252090847232  
new function () { /* code */ }  
new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()