JavaScript 中的函数
函数简史
在编程诞生之初,我们使用例程和子例程来创建系统。后来,这种模式演变为使用程序、子程序和函数来创建系统。而如今,我们只剩下函数了,至少这是大多数编程社区成员所了解和日常使用的。
什么是函数?为什么需要它们?
函数是一组语句,被打包成一个单一的实体,用于执行特定任务。它们可以被多次调用。它们可以接收零个或多个输入,并返回零个或多个输出。
例如,假设我们要计算几个数字的平方。
不使用函数的实现如下所示:
var number2 = 2;
var square_of_number2 = number2 * number2;
console.log(square_of_number2);
var number3 = 3;
var square_of_number3 = number3 * number3;
console.log(square_of_number3);
现在,让我们看一个使用函数创建的实现:
function square(number){
return number*number;
}
var square_of_number2 = square(2);
var square_of_number3 = square(3);
仅通过比较上述两种方法,代码看起来就干净多了。
创建函数的优点
- 代码看起来干净且有条理。
- 遵循DRY原则。
- 允许程序中的代码重用。
在 JS 中创建函数的方法
JavaScript 中有 4 种创建函数的方法,每种方法都有其独特的精妙之处。下面的顺序代表了我使用这些方法的偏好,从最常用到从未使用过。
- 函数声明(始终命名)
- 函数表达式(可以是命名的或匿名的)
- 箭头函数(始终匿名)
- 使用
Function
构造函数(永远不要这样做;出于安全原因和性能问题)
函数声明
引用MDN 的话,
函数定义(也称为函数声明或函数语句)由 function 关键字组成,后跟:
- 函数的名称。
- 函数的参数列表,括在括号中并用逗号分隔。
- 定义函数的 JavaScript 语句,括在花括号 {...} 中。
换句话说,如果任何 JS 语句以function
关键字开头,那么它就是函数声明。
为什么更喜欢使用函数声明?
- 标准语法(跨语言)
- 定义提升函数的唯一方法
- 有名称;更易读的错误堆栈跟踪
console.log(calculateSquare(2)); // hoisting
// OUTPUT: 4
function calculateSquare(number) {
return number * number;
}
参数和实参
在函数中接受值作为输入的占位符称为参数。
声明函数时,我们指定它的参数。
在前面的示例代码中,number
是一个参数。
在调用函数时传递给函数的值称为参数。
在前面的示例代码中,该值2
是一个参数。
默认参数
参数可以分配默认值。当函数中没有传递参数值或undefined
传递了参数值时,将使用此默认值。
function defaultParamFunction(param1 = 'some value') {
console.log(param1);
}
defaultParamFunction(); // no argument is passed
// OUTPUT: 'some value'
defaultParam('overridden'); // argument is passed
// OUTPUT: 'overridden'
函数表达式
创建函数的另一种方法是使用函数表达式语法。
这些可以有两种类型:命名函数表达式和匿名函数表达式。
命名函数表达式非常少见。虽然很少遇到,但应该最常使用。
// named function expression
const variable_name = function function_name() {
console.log("Hello World");
}
// anonymous function expression
const another_variable_name = function () {
console.log("Hello World");
}
我的建议是,所有函数都应使用表达式命名。这里的理由与为什么应该始终使用函数声明的理由相同。
为什么要使用命名函数表达式?
- 更加自文档化的代码
- 更多可调试的堆栈跟踪
IIFE(立即调用函数表达式)
顾名思义,这些是函数表达式,它们在定义的地方立即被调用。
语法涉及将函数包装到一组括号 () 中,然后用另一组括号 () 调用它。
// function definition is wrapped in parentheses
( function immediately_invoked() {
console.log("Hello World");
} )(); // notice the invoking set of parentheses
参数也可以传递给 IIFE。
(
function iife(param1) {
console.log(param1);
}
)(1); // argument 1 passed here
箭头函数
使用箭头函数时,function
不再使用关键字来定义函数,而是=>
使用粗箭头来定义函数。
根据定义,箭头函数是匿名的。
// syntax for defining an arrow function
const function_name = () => { // more characters
// function body
}
// syntax for a normal function declaration
function function_name() { // less characters
// function body
}
不要仅仅因为箭头函数看起来更短就用它作为函数声明的通用替代品。因为如果我们比较一下上面例子中的两个定义函数,就会发现箭头函数比用函数声明的方式编写相同的函数需要更多的字符(比较声明第一行的字符)。
不要使用箭头函数。为什么?
- 它们在语法上是匿名的,这意味着在调试时,堆栈跟踪将这些函数中的错误显示为“匿名函数”
- 它没有
this
关键字。它this
通过词法绑定。(更多内容请见后续文章) - 它们乍一看似乎更短,但占用的字符几乎与函数声明一样多或更多。
箭头函数 IIFE
IIFE 也可以使用箭头函数语法来定义。
// notice the first wrapping parenthesis
(
() => {
console.log("Hello World");
}
)();
函数构造函数
函数也可以使用函数构造器语法来创建。但是,除非绝对必要,否则建议不要使用此语法。这会在作用域内动态创建函数,并且这些函数只能在全局作用域中调用。
// syntax
const variable_name = new Function('arg1', 'arg2', ..., 'argN', 'functionBody');
例如:
const square_number = new Function('number', 'return number*number;');
console.log(square_number(2));
// OUTPUT: 4
有趣的事实:函数构造函数可以使用或不使用new
关键字来调用。
概括
声明、表达式和箭头
// 01 Function Declaration
function function_name() {
// body
}
// 02-a Function Expression (Named)
var variable_name = function function_name() {
// body
}
// 02-b Function Expression (Anonymous)
var variable_name = function() {
// body
}
// 03 Anonymous Arrow Function
var variable_name = () => {
// body
}
// 04 Function Constructor
var variable_name = new Function('param_name', 'function body');
总而言之,在 JavaScript 中创建函数有 4 种不同的方法。
但是,(在我看来)相比其他类型的函数设计,我更倾向于使用函数声明。也可以使用命名函数表达式。除非绝对必要,否则不要使用匿名函数表达式和箭头函数,并且绝对不要将其作为函数声明的通用替代品。切勿使用函数构造函数来创建函数。
鏂囩珷鏉ユ簮锛�https://dev.to/gokukun/functions-in-javascript-3mp8