JavaScript 函数(手把手讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在 JavaScript 开发中,函数是构建程序逻辑的核心工具。无论是简单的数据计算,还是复杂的异步操作,函数都如同程序中的“积木块”,通过灵活组合实现多样化功能。对于编程初学者而言,理解函数的定义、参数传递、作用域等基础概念至关重要;而中级开发者则需要掌握闭包、高阶函数等进阶特性,以提升代码的复用性和可维护性。本文将从零开始,逐步解析 JavaScript 函数的原理与实践,帮助读者建立系统化的知识框架。


函数的基础定义与调用

函数是什么?

函数可以类比为一个“自动化流程”。例如,厨房中的厨师按照菜谱(函数)制作菜品:输入食材(参数),经过加工(函数体),最终产出成品(返回值)。在 JavaScript 中,函数是封装特定任务的代码块,通过名称调用即可执行。

定义函数的两种方式

  1. 函数声明(Function Declaration)

    function calculateArea(radius) {  
      return Math.PI * radius * radius;  
    }  
    

    这种方式通过 function 关键字声明,函数名 calculateArea 可以在声明前的任何位置调用(得益于 JavaScript 的“函数提升”特性)。

  2. 函数表达式(Function Expression)

    const add = function(a, b) {  
      return a + b;  
    };  
    

    此时函数被赋值给变量 add,调用方式为 add(2, 3)。函数表达式不被提升,需先定义后使用。

函数调用与参数传递

函数调用时,参数的传递遵循“按值传递”原则。例如:

function greet(name) {  
  console.log(`Hello, ${name}!`);  
}  
greet("Alice"); // 输出 "Hello, Alice!"  

当参数是对象或数组时,传递的是引用地址而非原始值。例如:

function modifyObject(obj) {  
  obj.name = "Bob";  
}  
const person = { name: "Alice" };  
modifyObject(person);  
console.log(person.name); // 输出 "Bob"  

参数与返回值的深度解析

默认参数与可选参数

在 ES6 之前,开发者常通过 if 判断或 arguments 对象处理可选参数。ES6 引入了默认参数,使代码更简洁:

function makeCoffee(beans = 10, water = 200) {  
  return `使用 ${beans}g 咖啡豆和 ${water}ml 水冲泡`;  
}  
console.log(makeCoffee()); // 使用 10g 咖啡豆和 200ml 水冲泡  

灵活传递参数:argumentsrest 参数

  1. arguments 对象
    在函数内部,arguments 是一个类数组对象,可访问所有传入参数:

    function sum() {  
      return Array.from(arguments).reduce((acc, num) => acc + num, 0);  
    }  
    console.log(sum(1, 2, 3)); // 输出 6  
    

    arguments 不支持数组方法(如 map),需转换为真实数组。

  2. rest 参数(ES6)
    使用 ... 语法替代 arguments,直接获取参数的数组形式:

    function multiply(...numbers) {  
      return numbers.reduce((acc, num) => acc * num, 1);  
    }  
    console.log(multiply(2, 3, 4)); // 输出 24  
    

返回值的多态性

函数可以返回任意类型,甚至函数本身。例如:

function createMultiplier(factor) {  
  return function(number) {  
    return number * factor;  
  };  
}  
const double = createMultiplier(2);  
console.log(double(5)); // 输出 10  

此例中,函数 createMultiplier 返回了一个匿名函数,实现了“函数工厂”的功能。


函数的作用域与闭包

作用域的层级结构

JavaScript 的作用域分为全局作用域、函数作用域和块级作用域(ES6 引入)。函数内部可以直接访问外部变量,但外部无法直接访问函数内部变量。例如:

let globalVar = "全局变量";  
function outerFunction() {  
  let outerVar = "外部函数变量";  
  function innerFunction() {  
    let innerVar = "内部函数变量";  
    console.log(globalVar, outerVar); // 可访问  
  }  
  // console.log(innerVar); // 报错,无法访问内部变量  
}  

闭包(Closure)的定义与应用

闭包是函数与对其周围状态(即词法环境)的引用捆绑。例如:

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

此处,increment 函数引用了外部 countercount 变量,形成了闭包。闭包常用于封装私有变量或实现计数器、回调函数等场景。


高阶函数与函数式编程

高阶函数的定义

高阶函数是指满足以下任一条件的函数:

  • 接收其他函数作为参数
  • 返回一个函数作为结果

例如,Array.prototype.map 是一个典型高阶函数:

const numbers = [1, 2, 3];  
const squares = numbers.map(num => num * num); // [1, 4, 9]  

函数柯里化(Currying)

柯里化是将多参数函数转换为单参数函数链的过程。例如:

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

此方法可预设参数,提升代码灵活性。


箭头函数与 this 绑定

箭头函数的语法与特性

ES6 引入的箭头函数简化了函数书写:

// 传统函数  
function square(x) { return x * x; }  
// 箭头函数  
const square = x => x * x;  

箭头函数有以下特点:

  • 没有 arguments 对象,但可通过 ...args 获取参数
  • 没有独立的 this 绑定,继承外层函数的 this
  • 不能用作构造函数(无 new 关键字支持)

this 绑定的对比

传统函数的 this 取决于调用方式,而箭头函数的 this 始终指向定义时的外层作用域。例如:

const obj = {  
  value: 10,  
 传统函数:  
  method: function() {  
    setTimeout(function() {  
      console.log(this.value); // undefined,因函数内部 this 指向全局  
    }, 100);  
  },  
  箭头函数:  
  method2: function() {  
    setTimeout(() => {  
      console.log(this.value); // 10,继承外层 this  
    }, 100);  
  }  
};  

函数防抖与节流

场景需求

在处理高频事件(如窗口调整、输入框输入)时,频繁触发函数可能导致性能问题。此时需要使用防抖(Debounce)和节流(Throttle)。

函数防抖

防抖的核心是“在事件停止触发后,延迟执行函数”。例如:

function debounce(func, delay) {  
  let timer;  
  return function(...args) {  
    clearTimeout(timer);  
    timer = setTimeout(() => func.apply(this, args), delay);  
  };  
}  
// 应用:  
window.addEventListener("resize", debounce(handleResize, 300));  

函数节流

节流则是“在指定时间间隔内,确保函数最多执行一次”。例如:

function throttle(func, delay) {  
  let canRun = true;  
  return function(...args) {  
    if (!canRun) return;  
    canRun = false;  
    setTimeout(() => {  
      func.apply(this, args);  
      canRun = true;  
    }, delay);  
  };  
}  

实战案例:构建模块化计算器

以下示例通过函数组合实现一个简单计算器:

// 基础操作函数  
const add = (a, b) => a + b;  
const subtract = (a, b) => a - b;  
const multiply = (a, b) => a * b;  

// 组合函数  
function calculate(a, operator, b) {  
  const ops = { add, subtract, multiply };  
  return ops[operator](a, b);  
}  

// 使用防抖处理输入  
const handleInput = debounce((input) => {  
  const [a, op, b] = input.split(" ");  
  console.log(calculate(Number(a), op, Number(b)));  
}, 500);  

// 模拟用户输入  
handleInput("5 add 3"); // 输出 8  

结论

JavaScript 函数不仅是代码逻辑的容器,更是实现模块化、复用性和高性能的关键工具。从基础定义到闭包、高阶函数,再到防抖节流等进阶技巧,掌握这些知识点能显著提升开发效率与代码质量。对于初学者,建议从函数的参数传递、作用域入手,逐步理解闭包等抽象概念;中级开发者则可深入探索函数式编程、设计模式等场景,将函数的威力发挥到极致。

JavaScript 函数的灵活性与强大功能,正驱动着现代 Web 开发的无限可能。希望本文能成为你探索这一领域的坚实起点。

最新发布