知识篇 -- JS函数详解
函数是JavaScript中最核心的概念之一,它是组织代码、实现模块化和代码复用的基本单位。通过函数,我们可以将一系列操作封装起来,并在需要时多次调用,避免重复编写相同的代码。理解函数的定义、调用、参数、返回值以及不同类型的函数,是编写高效、可维护JavaScript代码的关键。
# 一、函数的定义与调用 基础
JavaScript提供了多种定义函数的方式:
# 1. 函数声明 (Function Declaration) 可提升
function greet(name) {
return "Hello, " + name + "!";
}
特点:
- 函数提升 (Hoisting):函数声明会被提升到其所在作用域的顶部,这意味着你可以在函数声明之前调用它。
- 命名函数:函数有名称,便于调试和递归调用。
# 2. 函数表达式 (Function Expression) 灵活
const sayHello = function(name) {
return "Hello, " + name + "!";
};
特点:
- 不会提升:函数表达式不会被提升,必须在定义之后才能调用。
- 匿名函数或命名函数:可以是匿名函数(如上例),也可以是命名函数表达式(
const sayHello = function funcName(name) {...}),命名函数表达式的名称只在函数内部可见。
# 3. 箭头函数 (Arrow Function) (ES6新增) 简洁
const add = (a, b) => a + b;
const sayHi = name => console.log(`Hi, ${name}!`); // 单个参数可省略括号
const getMessage = () => "This is a message."; // 无参数需保留空括号
特点:
- 语法简洁:特别适用于简单的函数。
- 没有自己的
this:this关键字的值由外层作用域决定(词法作用域)。 - 不能作为构造函数:不能使用
new关键字调用。 - 没有
arguments对象:但可以使用剩余参数...args。 - 不会提升:与函数表达式类似。
# 2. 函数的调用
定义函数后,通过函数名后跟括号 () 来调用它。
console.log(greet("Alice")); // Hello, Alice!
console.log(sayHello("Bob")); // Hello, Bob!
console.log(add(5, 3)); // 8
sayHi("Charlie"); // Hi, Charlie!
# 二、参数与返回值 核心
# 1. 参数 (Parameters)
函数可以接受零个或多个参数,这些参数是函数内部使用的局部变量。
- 形参 (Parameters):函数定义时声明的变量。
- 实参 (Arguments):函数调用时传递给函数的值。
- 默认参数 (Default Parameters) (ES6新增):允许为函数参数设置默认值。
function multiply(a, b = 2) { return a * b; } console.log(multiply(5)); // 10 (b使用默认值2) console.log(multiply(5, 3)); // 15 - 剩余参数 (Rest Parameters) (ES6新增):允许将不定数量的参数表示为一个数组。
function sumAll(...numbers) { return numbers.reduce((total, num) => total + num, 0); } console.log(sumAll(1, 2, 3)); // 6 console.log(sumAll(10, 20, 30, 40)); // 100 arguments对象:在ES5及之前,函数内部有一个类数组对象arguments,包含了所有传递给函数的实参。但在箭头函数中没有arguments对象。function oldSum() { let total = 0; for (let i = 0; i < arguments.length; i++) { total += arguments[i]; } return total; } console.log(oldSum(1, 2, 3)); // 6
# 2. 返回值 (Return Value)
函数可以通过 return 语句返回一个值。
- 如果没有
return语句,或者return后面没有值,函数会隐式返回undefined。 return语句会立即终止函数的执行。- 示例:
function calculateArea(width, height) { if (width <= 0 || height <= 0) { return "无效的尺寸"; // 提前返回 } return width * height; } console.log(calculateArea(10, 5)); // 50 console.log(calculateArea(-2, 5)); // 无效的尺寸
# 三、函数作用域与闭包 (Scope and Closures) 进阶
# 1. 作用域 (Scope)
作用域决定了变量和函数在代码中的可访问性。
- 全局作用域:在函数外部声明的变量,在程序的任何地方都可以访问。
- 函数作用域:在函数内部声明的
var变量,只能在该函数内部访问。 - 块级作用域 (ES6新增):在
if语句、for循环或任何{}块中用let或const声明的变量,只能在该块内部访问。
# 2. 闭包 (Closures)
闭包是指函数能够记住并访问其词法作用域(即函数定义时的作用域),即使该函数在其词法作用域之外执行。
- 形成条件:当一个内部函数引用了其外部函数作用域中的变量,并且这个内部函数被返回或传递到外部时,就形成了闭包。
- 作用:
- 数据私有化:创建私有变量,保护数据不被外部直接访问。
- 状态持久化:使函数能够记住其创建时的环境,保持状态。
- 示例:
function createCounter() { let count = 0; // 外部函数作用域中的变量 return function() { // 内部函数 count++; console.log(count); }; } const counter1 = createCounter(); counter1(); // 1 counter1(); // 2 const counter2 = createCounter(); counter2(); // 1 (counter1和counter2拥有独立的count变量) - 注意:过度使用闭包可能导致内存泄漏,因为闭包会阻止其引用的外部变量被垃圾回收。
# 四、this 关键字:指向何方? 难点
this 关键字是JavaScript中最复杂但又最重要的概念之一,它的值取决于函数被调用的方式。
- 全局上下文:在全局作用域中,
this指向全局对象(浏览器中是window,Node.js中是global)。 - 函数调用:普通函数调用时,
this默认指向全局对象(严格模式下为undefined)。 - 方法调用:作为对象的方法调用时,
this指向该对象。 - 构造函数调用:使用
new关键字调用时,this指向新创建的实例对象。 - 事件处理函数:通常指向触发事件的元素。
- 箭头函数:箭头函数没有自己的
this,它会捕获其外层(词法)作用域的this值。 call(),apply(),bind():可以显式地改变this的指向。
示例:
const person = {
name: "Alice",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
},
farewell: () => {
console.log(`Goodbye from ${this.name}`); // 箭头函数this指向全局对象
}
};
person.greet(); // Hello, my name is Alice (this指向person)
person.farewell(); // Goodbye from undefined (浏览器中,this.name会是window.name,通常为空)
const anotherGreet = person.greet;
anotherGreet(); // Hello, my name is undefined (普通函数调用,this指向全局对象)
理解函数是JavaScript编程的基石。通过掌握函数的定义、参数、返回值、作用域和 this 关键字,你将能够编写出结构清晰、功能强大且易于维护的JavaScript代码。