函数表达式 与 函数声明 别搞混了

简介: 函数表达式 与 函数声明 别搞混了

在JavaScript中,function关键字做一个简单的工作:创建一个函数。但是,使用关键字定义函数的方式可以创建具有不同属性的函数

在这篇文章中,你将了解如何使用function关键字来编写函数声明和函数表达式,以及这两种类型的函数之间有什么区别。


1. 函数表达式 VS 函数声明

函数声明和函数表达式是使用Function关键字创建函数的两种方法。

让我们举个例子来说明两者的区别——我们先创建两个版本的求和函数:

function sumA(a, b) {
  return a + b;
}
(function sumB(a, b) {
  return a + b;
});
sumA(1, 2); // ???
sumB(1, 2); // ???

在一种情况下,可以像往常一样定义函数(如:sumA() )。在另一种情况下,函数被放置在一对括号中(如:sumB())。

如果你调用sumA(1,2)sumB(1,2)会发生什么?

正如预期的那样,sumA(1, 2)只是返回12个数字的和3。然而,调用sumB(1, 2)抛出一个未捕获的ReferenceError: sumB未定义。

原因是,sumA是使用函数声明创建的,该函数声明在当前作用域中创建了一个函数变量(与函数名同名)。但是sumB是使用函数表达式创建的(它被括在圆括号中),它不会在当前作用域内创建函数变量

如果你想访问使用函数表达式创建的函数,那么将函数对象保存到一个变量中:

// Works!
const sum = (function sumB(a, b) {
  return a + b;
});
sum(1, 2); // => 3

下面是一个关于如何区分函数声明和函数表达式的简单提示:

如果语句以function关键字开头,那么它就是一个函数声明,否则就是一个函数表达式。

// 函数声明: 以 `function` 关键字开头
function sumA(a, b) {
  return a + b;
}
// 函数表达式: 不以 `function` 关键字开头
const mySum = (function sumB(a, b) {
  return a + b;
});
// 函数表达式: 不以 `function` 关键字开头
[1, 2, 3].reduce(function sum3(acc, number) { 
  return acc + number 
});

从更高的角度来看,函数声明对于创建独立的函数很有用,但是函数表达式作为回调很好

现在,让我们深入了解函数声明和函数表达式的行为。

2. 函数声明

正如你在前面的例子中已经看到的,sumA是一个函数声明:

// 函数声明
function sumA(a, b) {
  return a + b;
}
sumA(4, 5); // => 9

当一个语句包含function关键字,后面跟着函数名、一对带有参数(param1, param2, paramN)的圆括号以及用一对**大括号{}**括起来的函数体时,就会发生函数声明。

函数声明创建了一个函数变量——**一个与函数名相同的变量(**例如前面例子中的sumA)。函数变量可以在当前作用域(在函数声明之前和之后),甚至在函数作用域本身内访问。

函数变量通常用于调用函数或将函数对象传递给其他函数(高阶函数)

例如,我们写一个函数sumArray(array),它对数组(数组可以包含数字或其他数组)中的项进行递归求和:

sumArray([10, [1, [5]]]); // => 16
function sumArray(array) {
  let sum = 0;
  for (const item of array) {
    sum += Array.isArray(item) ? sumArray(item) : item;
  }
  return sum;
}
sumArray([1, [4, 6]]); // => 11

函数sumArray(array){…}是一个函数声明。

包含函数对象的函数变量sumArray在当前范围内可用:在 sumArray([10,[1,[5]]])之前和在sumArray([1,[4, 6]])之后的函数声明,以及在函数本身的范围内sumArray(item)(允许递归调用)。

由于变量提升,函数变量在函数声明之前可用。

2.1 函数声明的注意事项

函数声明语法的作用是创建独立的函数。函数声明应该在全局作用域内或直接在其他函数的作用域内:

// Good!
function myFunc1(param1, param2) {
  return param1 + param2;
}
function bigFunction(param) {
  // Good!
  function myFunc2(param1, param2) {
    return param1 + param2;
  }
  const result = myFunc2(1, 3);
  return result + param;
}

出于同样的原因,不建议在条件语句(if)和循环语句(while, for)中使用函数声明:

// Bad!
if (myCondition) {
  function myFunction(a, b) {
    return a * b;
  }
} else {
  function myFunction(a, b) {
    return a + b;
  }
}
myFunction(2, 3);

而使用函数表达式可以更好地在条件语句中创建函数。

3. 函数表达式

function关键字在表达式中创建一个函数(有或没有名称)时,才是一个函数表达式。

以下是使用表达式创建的函数示例:

// 函数表达式
const sum = (function sumB(a, b) {
  return a + b;
});
const myObject = {
  myMethod: function() {
    return 42;
  }
};
const numbers = [4, 1, 6];
numbers.forEach(function callback(number) {
  console.log(number);
  // logs 4
  // logs 1
  // logs 1
});

在函数表达式中创建的函数有两种:

  1. 如果表达式内的函数没有名称,例如function() {return 42},则这是一个匿名函数表达式;
  2. 如果函数有名字,例如前面例子中的sumBcallback,那么这就是一个命名函数表达式

3.1 函数表达式的注意事项

函数表达式适合作为回调函数或由条件创建的函数:

// 由条件创建的函数
let callback;
if (true) {
  callback = function() { return 42 };
} else {
  callback = function() { return 3.14 };
}
// 作为回调函数使用
[1, 2, 3].map(function increment(number) {
  return number + 1;
}); // => [2, 3, 4]

如果你已经创建了一个命名函数表达式,请注意函数变量只在已创建函数范围内可用:

const numbers = [4];
numbers.forEach(function callback(number) {
  console.log(callback); // logs function() { ... }
});
console.log(callback); // ReferenceError: callback is not defined
  • Callback是一个命名函数表达式,因此Callback函数变量只能在Callback()函数范围内使用,而不能在Callback()函数范围外使用。

但是,如果你将函数对象存储到一个常规变量中,那么你可以在函数作用域内外从该变量访问函数对象:

const callback = function(number) {
  console.log(callback); // logs function() { ... }
};
const numbers = [4];
numbers.forEach(callback);
console.log(callback); // logs function() { ... }

4. 总结

根据如何使用function关键字创建函数,可以以两种方式来创建函数:函数声明和函数表达式。

函数声明发生在以function关键字开始的语句中:

// 函数声明
function sumA(a, b) {
  return a + b;
}

函数声明对于创建独立的、通用的函数非常有用。

但是,如果一个语句不以function关键字开头,那么就有一个函数表达式:

// 函数表达式
(function sumB(a, b) {
  return a + b;
});

使用函数表达式创建的函数对于根据条件创建回调函数或函数很有用。

最后,考考大家:

function sum(a, b) { return a + b } + 1;

是函数声明还是函数表达式,留言写下你的答案!



相关文章
|
存储 Swift
24 闭包和闭包表达式
闭包和闭包表达式
57 0
|
1月前
练习函数调用
练习函数调用。
27 13
|
3月前
|
JavaScript 前端开发 开发者
调用函数
【8月更文挑战第26天】
18 2
|
3月前
|
JavaScript 前端开发
IIFE(立即执行函数表达式)
IIFE(立即执行函数表达式)
34 1
C4.
|
6月前
|
Python
Phython调用函数
Phython调用函数
C4.
37 0
|
5月前
|
安全 C++ 开发者
C++一分钟之-函数参数传递:值传递与引用传递
【6月更文挑战第19天】C++中函数参数传递涉及值传递和引用传递。值传递传递实参副本,安全但可能效率低,适合不变对象;引用传递传递实参引用,允许修改,用于高效修改或返回多值。值传递示例显示交换不生效,而引用传递示例实现交换。常量引用则防止意外修改。选择传递方式需考虑效率与安全性。
45 2
|
6月前
|
XML 存储 JavaScript
loadXMLString() 函数
`loadXMLString()` 是一个JavaScript函数,用于在不同浏览器环境下解析XML字符串。它使用DOMParser在支持的浏览器中解析,而在IE中则使用ActiveXObject。函数接受XML文本作为参数,返回解析后的XML文档。此函数适用于HTML页面的<script>标签内,方便在页面中重用,尤其在处理XML实例时。
|
C++ Python
Python函数参数传递:传值还是传引用
Python函数参数传递:传值还是传引用
59 0
|
6月前
|
算法 前端开发
2666. 只允许一次函数调用
2666. 只允许一次函数调用
46 0
|
11月前
函数调用
输出十行十列的星号(要求用函数调用方式解决)。
90 1