函数(Function)

JavaScript 中的函数:声明、表达式、箭头函数的使用和底层原理


📖 入门:基础使用

1. 函数声明(Function Declaration)

基本语法

function functionName(parameters) {
  // 函数体
  return value;
}

示例

function add(a, b) {
  return a + b;
}
console.log(add(5, 3));  // 8

特点

  • ✅ 函数提升(hoisting)
  • ✅ 可以在声明前调用
  • ✅ 属于函数作用域

2. 函数表达式(Function Expression)

基本语法

const functionName = function(parameters) {
  // 函数体
  return value;
};

示例

const subtract = function(a, b) {
  return a - b;
};
console.log(subtract(10, 3));  // 7

特点

  • ❌ 不提升(必须先声明后使用)
  • ✅ 可以作为值传递
  • ✅ 可以匿名

3. 箭头函数(Arrow Function)

基本语法

const functionName = (parameters) => {
  // 函数体
  return value;
};

简写形式

// 单个参数,可以省略括号
const square = x => x * x;
 
// 单个表达式,可以省略 return 和花括号
const double = x => x * 2;
 
// 多个参数
const add = (a, b) => a + b;
 
// 无参数
const greet = () => console.log("Hello");

示例

const multiply = (a, b) => a * b;
console.log(multiply(4, 5));  // 20
 
// 数组方法中使用
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

特点

  • ✅ 语法简洁
  • ✅ 不绑定 this(词法 this
  • ❌ 不能作为构造函数
  • ❌ 没有 arguments 对象

4. 函数参数

4.1 默认参数(Default Parameters)

基本语法

function greet(name = "Guest") {
  console.log(`Hello, ${name}!`);
}

示例

function greet(name = "Guest", age = 0) {
  console.log(`Hello, ${name}, you are ${age} years old.`);
}
greet("Alice", 25);  // Hello, Alice, you are 25 years old.
greet("Alice");      // Hello, Alice, you are 0 years old.
greet();             // Hello, Guest, you are 0 years old.

4.2 Rest 参数(Rest Parameters)

基本语法

function functionName(...args) {
  // args 是一个数组
}

示例

function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3, 4));  // 10
console.log(sum(1, 2));        // 3

4.3 参数解构(Destructuring)

基本语法

function functionName({ prop1, prop2 }) {
  // 使用 prop1, prop2
}

示例

function greet({ name, age }) {
  console.log(`Hello, ${name}, you are ${age} years old.`);
}
greet({ name: "Alice", age: 25 });  // Hello, Alice, you are 25 years old.

5. 立即执行函数(IIFE)

基本语法

(function() {
  // 代码块
})();

示例

(function() {
  let message = "Hello";
  console.log(message);  // Hello
})();
// message 在这里不可访问

用途

  • 创建私有作用域
  • 避免变量污染全局

🚀 提高:底层原理

1. 函数执行上下文(Execution Context)

1.1 执行上下文的创建

函数调用时的执行过程

1. 创建执行上下文(Execution Context)
2. 创建变量环境(Variable Environment)
3. 创建词法环境(Lexical Environment)
4. 绑定 this
5. 初始化参数
6. 执行函数体
7. 返回结果

示例分析

function add(a, b) {
  let sum = a + b;
  return sum;
}
 
let result = add(5, 3);

执行上下文结构

FunctionExecutionContext {
  VariableEnvironment: {
    a: 5,
    b: 3,
    sum: 8
  },
  LexicalEnvironment: {
    // 词法环境
  },
  ThisBinding: undefined (严格模式) 或 global (非严格模式)
}

1.2 变量提升和函数提升

函数声明的提升

// 代码
console.log(foo);  // [Function: foo]
foo();             // "Hello"
 
function foo() {
  console.log("Hello");
}
 
// 实际执行顺序(提升后)
function foo() {    // 函数提升
  console.log("Hello");
}
console.log(foo);  // [Function: foo]
foo();             // "Hello"

函数表达式的提升

// 代码
console.log(bar);  // undefined
// bar();          // 报错:bar is not a function
 
var bar = function() {
  console.log("Hello");
};
 
// 实际执行顺序(提升后)
var bar;           // 变量提升
console.log(bar);  // undefined
bar = function() { // 赋值留在原地
  console.log("Hello");
};

2. 作用域链(Scope Chain)

2.1 作用域链的形成

原理

  • 每个函数都有作用域链
  • 作用域链指向外层作用域
  • 形成链式查找机制

示例

let globalVar = "global";
 
function outer() {
  let outerVar = "outer";
  
  function inner() {
    let innerVar = "inner";
    console.log(innerVar);   // "inner"(当前作用域)
    console.log(outerVar);   // "outer"(外层作用域)
    console.log(globalVar);  // "global"(全局作用域)
  }
  
  inner();
}
 
outer();

作用域链结构

inner 函数的作用域链:
inner LexicalEnvironment -> outer LexicalEnvironment -> Global LexicalEnvironment

2.2 闭包(Closure)

闭包的定义

  • 函数可以访问其外部作用域的变量
  • 即使外部函数已经执行完毕

示例

function outer() {
  let count = 0;
  
  function inner() {
    count++;
    console.log(count);
  }
  
  return inner;
}
 
const counter = outer();
counter();  // 1
counter();  // 2
counter();  // 3

闭包原理

outer 函数执行完毕后:
- outer 的执行上下文被销毁
- 但 inner 函数引用了 outer 的变量 count
- count 被保留在内存中(闭包)
- inner 函数可以继续访问 count

3. this 绑定

3.1 this 的绑定规则

默认绑定

function foo() {
  console.log(this);  // global (非严格模式) 或 undefined (严格模式)
}
foo();

隐式绑定

const obj = {
  name: "Alice",
  greet: function() {
    console.log(this.name);  // "Alice"
  }
};
obj.greet();

显式绑定

function greet() {
  console.log(this.name);
}
 
const obj = { name: "Alice" };
greet.call(obj);    // "Alice"
greet.apply(obj);   // "Alice"
const bound = greet.bind(obj);
bound();            // "Alice"

new 绑定

function Person(name) {
  this.name = name;
}
 
const person = new Person("Alice");
console.log(person.name);  // "Alice"

3.2 箭头函数的 this

箭头函数的 this 规则

  • 箭头函数没有自己的 this
  • this 继承自外层作用域(词法 this

示例对比

const obj = {
  name: "Alice",
  
  // 普通函数
  greet1: function() {
    setTimeout(function() {
      console.log(this.name);  // undefined(this 指向 global)
    }, 100);
  },
  
  // 箭头函数
  greet2: function() {
    setTimeout(() => {
      console.log(this.name);  // "Alice"(this 继承外层)
    }, 100);
  }
};
 
obj.greet1();  // undefined
obj.greet2();  // "Alice"

4. 函数作为一等公民(First-Class Citizen)

函数的特性

  • ✅ 可以作为值赋值给变量
  • ✅ 可以作为参数传递
  • ✅ 可以作为返回值
  • ✅ 可以动态创建

示例

// 作为值
const add = function(a, b) { return a + b; };
 
// 作为参数
function calculate(a, b, operation) {
  return operation(a, b);
}
console.log(calculate(5, 3, add));  // 8
 
// 作为返回值
function createMultiplier(multiplier) {
  return function(x) {
    return x * multiplier;
  };
}
const double = createMultiplier(2);
console.log(double(5));  // 10

5. 函数式编程基础

5.1 高阶函数(Higher-Order Function)

定义:接收函数作为参数或返回函数的函数

示例

// map:数组映射
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]
 
// filter:数组过滤
const evens = numbers.filter(x => x % 2 === 0);
console.log(evens);  // [2, 4]
 
// reduce:数组归约
const sum = numbers.reduce((a, b) => a + b, 0);
console.log(sum);  // 15

5.2 纯函数(Pure Function)

定义

  • 相同输入总是产生相同输出
  • 没有副作用(不修改外部状态)

示例

// 纯函数
function add(a, b) {
  return a + b;
}
 
// 非纯函数
let count = 0;
function increment() {
  count++;  // 副作用:修改外部状态
  return count;
}

📝 最佳实践

  1. 优先使用函数声明或箭头函数:避免 var + 函数表达式
  2. 合理使用默认参数:提高函数易用性
  3. 理解 this 绑定:避免 this 指向错误
  4. 利用闭包:创建私有变量和模块化
  5. 编写纯函数:提高代码可测试性和可维护性

🔗 相关链接


javascript 函数 闭包 this 作用域链