6

JS 中的函数表达式和函数声明你混淆了吗?

 3 years ago
source link: https://www.techug.com/post/are-you-confused-between-function-expression-and-function-declaration-in-js.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

在本文中,我们来看一下,如何使用function关键字来定义函数声明和函数表达式,以及这两种函数之间的区别又是什么。

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

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

举个例子来说明差异,我们创建两个版本的 sums 函数:

function sumA(a, b) {
  return a + b;
}

(function sumB(a, b) {
  return a + b;
});

sumA(1, 2); // ???
sumB(1, 2); // ???

动手试试:https://jsfiddle.net/dmitri_pavlutin/8b46yokr/2/

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

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

如预期的那样,sumA(1, 2) 返回 3。但是,调用sumB(1, 2)会引发异常:Uncaught ReferenceError: sumB is not defined。

其原因是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 declaration
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

动手试试:https://jsfiddle.net/dmitri_pavlutin/n7wcryuo/

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

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

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

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关键字在表达式内部创建函数(带有或不带有名称)时,将出现函数表达式。

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

// Function expressions

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
});

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

  • 如果表达式中的函数没有名称,例如 function(){return 42},那是一个匿名函数表达式
  • 如果函数具有名称,例如 上一个示例中的sumB和回调,那么这是一个命名函数表达式

3.1 函数表达式的注意事项

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

// Functions created conditionally
let callback;
if (true) {
  callback = function() { return 42 };
} else {
  callback = function() { return 3.14 };
}

// Functions used as callbacks
[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

试一试:https://jsfiddle.net/dmitri_pavlutin/sujwmp10/2/

callback是一个命名的函数表达式,因此callback函数变量仅在callback()函数使用域可用,而在外部则不可用。

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

const callback = function(number) {
  console.log(callback); // logs function() { ... }
};

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

试一试:https://jsfiddle.net/dmitri_pavlutin/1btmrcu2/1/

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

留个问题:function sum(a, b) { return a + b } + 1
是函数声明还是函数表达式,可以在留言中说出你的答案。


作者:Dmitri Pavlutin 译者:前端小智 来源:dmitripavlutin

原文:https://dmitripavlutin.com/javascript-function-expressions-and-declarations/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK