您现在的位置是:网站首页 > 函数声明与函数表达式文章详情

函数声明与函数表达式

在JavaScript中,函数可以通过声明或表达式的方式定义,两者在语法、作用域和调用时机上存在差异。理解这些差异对编写高效、可维护的代码至关重要。

函数声明的基本语法

函数声明以function关键字开头,后跟函数名和一对圆括号。函数体包裹在花括号中。例如:

function greet(name) {
  return `Hello, ${name}!`;
}

函数声明会被提升(hoisting),这意味着可以在声明之前调用函数:

console.log(greet('Alice')); // 输出: Hello, Alice!

function greet(name) {
  return `Hello, ${name}!`;
}

函数表达式的基本语法

函数表达式将函数赋值给变量或属性。它可以是匿名的,也可以有名称:

const greet = function(name) {
  return `Hello, ${name}!`;
};

命名函数表达式:

const greet = function sayHello(name) {
  return `Hello, ${name}!`;
};

函数表达式不会被提升,必须在定义后才能调用:

console.log(greet('Alice')); // 报错: greet is not defined

const greet = function(name) {
  return `Hello, ${name}!`;
};

作用域差异

函数声明的作用域是它所在的函数或全局作用域。函数表达式的作用域取决于它被赋值的位置:

function outer() {
  innerDeclared(); // 正常执行
  innerExpressed(); // 报错

  function innerDeclared() {
    console.log('I am hoisted');
  }

  const innerExpressed = function() {
    console.log('I am not hoisted');
  };
}

立即调用函数表达式(IIFE)

函数表达式可以立即调用,形成独立作用域:

(function() {
  const secret = 'hidden';
  console.log(secret); // 输出: hidden
})();

console.log(secret); // 报错: secret is not defined

带参数的IIFE:

(function(message) {
  console.log(message); // 输出: Immediately invoked
})('Immediately invoked');

箭头函数表达式

ES6引入的箭头函数是函数表达式的简写形式:

const greet = (name) => {
  return `Hello, ${name}!`;
};

// 单行时可省略return和花括号
const square = x => x * x;

箭头函数没有自己的this绑定,适合用作回调:

const timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++;
      console.log(this.seconds);
    }, 1000);
  }
};

作为参数传递

函数表达式常用于高阶函数参数:

const numbers = [1, 2, 3];
const doubled = numbers.map(function(n) {
  return n * 2;
});

// 箭头函数更简洁
const tripled = numbers.map(n => n * 3);

构造函数中的差异

函数声明不能直接用作构造函数,除非通过new调用:

function Person(name) {
  this.name = name;
}
const alice = new Person('Alice');

函数表达式赋值给变量后同样可用作构造函数:

const Animal = function(species) {
  this.species = species;
};
const cat = new Animal('Felis catus');

递归调用时的区别

命名函数表达式在递归调用时更可靠:

const factorial = function calc(n) {
  return n <= 1 ? 1 : n * calc(n - 1);
};

// 即使变量被重新赋值,函数仍能正常工作
const oldFactorial = factorial;
factorial = null;
console.log(oldFactorial(5)); // 输出120

调试时的可读性

命名函数表达式在调试时显示函数名,提高可读性:

const anonymous = function() { /* 调试显示anonymous */ };
const named = function myFunc() { /* 调试显示myFunc */ };

模块模式中的应用

函数表达式常用于创建私有作用域:

const counter = (function() {
  let count = 0;

  return {
    increment() {
      count++;
    },
    get value() {
      return count;
    }
  };
})();

动态函数生成

函数表达式允许运行时动态创建函数:

function createMultiplier(factor) {
  return function(x) {
    return x * factor;
  };
}

const double = createMultiplier(2);
console.log(double(5)); // 输出10

性能考量

现代JavaScript引擎对两种形式的优化差异不大,但函数声明由于提升特性可能略微影响脚本解析速度。实践中应优先考虑代码可读性和维护性。

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步