1.2 函数与作用域
函数是 JavaScript 的核心概念,它是可重用的代码块。作用域决定了变量的可访问范围。
一、函数定义
1. 函数声明(命名函数)
javascript
function greet(name) {
return "Hello, " + name + "!";
}
// 函数声明会被提升(可在声明前调用)
console.log(greet("张三")); // "Hello, 张三!"2. 函数表达式(匿名函数)
javascript
const greet = function(name) {
return "Hello, " + name + "!";
};
// 不会被提升
// console.log(greet("张三")); // 报错3. 箭头函数(ES6)
javascript
const greet = (name) => {
return "Hello, " + name + "!";
};
// 简化形式(单行返回)
const greet = name => "Hello, " + name + "!";
// 无参数
const sayHello = () => "Hello!";
// 多参数
const add = (a, b) => a + b;4. 立即执行函数(IIFE)
javascript
(function() {
console.log("立即执行");
})();
// 带参数
(function(name) {
console.log("Hello, " + name);
})("张三");
// 箭头函数写法
(() => {
console.log("立即执行");
})();二、参数
1. 默认参数
javascript
function greet(name = "张三") {
return "Hello, " + name + "!";
}
console.log(greet()); // "Hello, 张三!"
console.log(greet("李四")); // "Hello, 李四!"2. 剩余参数
javascript
function sum(...nums) {
return nums.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4)); // 103. 解构参数
javascript
function createUser({ name, age, city = "北京" }) {
return { name, age, city };
}
const user = createUser({ name: "张三", age: 25 });
console.log(user); // { name: "张三", age: 25, city: "北京" }4. 参数验证
javascript
function divide(a, b) {
if (typeof a !== "number" || typeof b !== "number") {
throw new Error("参数必须是数字");
}
if (b === 0) {
throw new Error("除数不能为零");
}
return a / b;
}三、返回值
javascript
// 显式返回
function add(a, b) {
return a + b;
}
// 隐式返回(undefined)
function noReturn() {
console.log("没有返回值");
}
// 返回对象(注意括号)
const getUser = () => ({ name: "张三", age: 25 });
// 提前返回
function login(username, password) {
if (!username || !password) {
return false;
}
// 验证逻辑...
return true;
}四、作用域
1. 作用域链
javascript
let global = "全局变量";
function outer() {
let outer = "外层变量";
function inner() {
let inner = "内层变量";
console.log(global); // 可访问全局
console.log(outer); // 可访问外层
console.log(inner); // 可访问内层
}
inner();
// console.log(inner); // 报错:无法访问内层变量
}
outer();2. 块级作用域
javascript
// var:函数作用域
function testVar() {
if (true) {
var x = 10;
}
console.log(x); // 10(var 会泄露到函数作用域)
}
// let/const:块级作用域
function testLet() {
if (true) {
let y = 10;
}
console.log(y); // 报错:y 在块外不可访问
}3. 作用域示例
javascript
let globalVar = "全局";
function testScope() {
let functionVar = "函数局部";
if (true) {
let blockVar = "块级局部";
console.log(globalVar); // ✅
console.log(functionVar); // ✅
console.log(blockVar); // ✅
}
console.log(globalVar); // ✅
console.log(functionVar); // ✅
// console.log(blockVar); // ❌ 报错
}五、闭包
定义
闭包是指函数能够访问其外部作用域的变量,即使外部函数已经执行完毕。
示例
javascript
function outer() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3
// 每次调用 counter,都可以访问 outer 中的 count 变量应用场景
javascript
// 1. 数据私有化
function createCounter() {
let count = 0;
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count,
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 1
// counter.count 无法直接访问
// 2. 函数工厂
function makeGreeter(greeting) {
return function(name) {
console.log(greeting + ", " + name + "!");
};
}
const sayHello = makeGreeter("Hello");
const sayHi = makeGreeter("Hi");
sayHello("张三"); // "Hello, 张三!"
sayHi("李四"); // "Hi, 李四!"六、this 关键字
1. 普通函数
javascript
// 严格模式下 this 为 undefined
function test() {
"use strict";
console.log(this); // undefined
}
test();
// 非严格模式
function test2() {
console.log(this); // window(浏览器)
}
test2();2. 对象方法
javascript
const person = {
name: "张三",
greet: function() {
console.log(this.name); // "张三"(指向 person)
}
};
person.greet();3. 箭头函数
javascript
const person = {
name: "张三",
greet: () => {
console.log(this.name); // undefined(箭头函数没有绑定 this)
},
greet2: function() {
const arrow = () => {
console.log(this.name); // "张三"(继承外层 this)
};
arrow();
}
};
person.greet(); // undefined
person.greet2(); // "张三"4. call/apply/bind
javascript
function greet(greeting = "你好") {
console.log(greeting + ", " + this.name + "!");
}
const person = { name: "张三" };
// call
greet.call(person, "你好"); // "你好, 张三!"
// apply(参数以数组形式传递)
greet.apply(person, ["您好"]); // "您好, 张三!"
// bind(返回新函数)
const boundGreet = greet.bind(person);
boundGreet("哈喽"); // "哈喽, 张三!"七、高阶函数
函数作为参数或返回值的函数。
1. 函数作为参数
javascript
function operate(a, b, operation) {
return operation(a, b);
}
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
console.log(operate(5, 3, add)); // 8
console.log(operate(5, 3, multiply)); // 152. 函数作为返回值
javascript
function multiplyBy(n) {
return function(x) {
return x * n;
};
}
const double = multiplyBy(2);
const triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 153. 常用高阶函数
javascript
const nums = [1, 2, 3, 4, 5];
// map:映射
const doubled = nums.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// filter:过滤
const evens = nums.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]
// reduce:归约
const sum = nums.reduce((total, n) => total + n, 0);
console.log(sum); // 15
// forEach:遍历
nums.forEach(n => console.log(n));
// find:查找
const found = nums.find(n => n > 3);
console.log(found); // 4
// some:是否存在满足条件的
const hasEven = nums.some(n => n % 2 === 0);
console.log(hasEven); // true
// every:是否都满足条件
const allPositive = nums.every(n => n > 0);
console.log(allPositive); // true八、递归
函数调用自身。
javascript
// 阶乘
function factorial(n) {
if (n === 0 || n === 1) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
// 斐波那契数列
function fibonacci(n) {
if (n === 0) return 0;
if (n === 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(10)); // 55九、总结
- 函数声明会被提升,表达式不会
- 箭头函数没有自己的 this,继承外层
- 闭包让函数能访问外部作用域变量
- 高阶函数让代码更简洁、可复用
- 递归要设置终止条件,避免栈溢出