函数
本章将详细介绍ECMAScript中的函数,包括函数的定义、调用、参数传递、作用域等核心概念,以及函数作为一等公民的重要特性。
8.1 函数概述
函数是一段可重复使用的代码块,用于执行特定任务。函数可以接收输入参数并返回结果,是ECMAScript中最重要的概念之一。
8.2 函数的定义
ECMAScript提供了多种定义函数的方式。
函数声明
// 函数声明
function greet(name) {
return `你好, ${name}!`;
}
console.log(greet("张三")); // "你好, 张三!"
函数表达式
// 函数表达式
let greet = function(name) {
return `你好, ${name}!`;
};
// 命名函数表达式
let greet = function sayHello(name) {
return `你好, ${name}!`;
};
箭头函数 (ES6)
// 箭头函数
let greet = (name) => {
return `你好, ${name}!`;
};
// 简化箭头函数
let greet = name => `你好, ${name}!`;
// 无参数箭头函数
let sayHello = () => "你好!";
// 单表达式箭头函数
let add = (a, b) => a + b;
8.3 函数的调用
函数可以通过多种方式调用。
普通调用
function add(a, b) {
return a + b;
}
let result = add(5, 3); // 8
作为方法调用
let calculator = {
value: 0,
add: function(num) {
this.value += num;
return this;
},
getValue: function() {
return this.value;
}
};
calculator.add(5).add(3); // 链式调用
console.log(calculator.getValue()); // 8
构造函数调用
// 构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
let person = new Person("张三", 25);
console.log(person.name); // "张三"
间接调用
function greet() {
return `你好, ${this.name}!`;
}
let user = { name: "李四" };
let greeting = greet.call(user); // "你好, 李四!"
let greeting2 = greet.apply(user); // "你好, 李四!"
// bind方法
let boundGreet = greet.bind(user);
let greeting3 = boundGreet(); // "你好, 李四!"
8.4 参数处理
ECMAScript提供了灵活的参数处理机制。
参数默认值 (ES6)
// 参数默认值
function greet(name = "匿名用户", greeting = "你好") {
return `${greeting}, ${name}!`;
}
console.log(greet()); // "你好, 匿名用户!"
console.log(greet("张三")); // "你好, 张三!"
console.log(greet("李四", "欢迎")); // "欢迎, 李四!"
剩余参数 (ES6)
// 剩余参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 结合普通参数和剩余参数
function multiply(multiplier, ...numbers) {
return numbers.map(num => num * multiplier);
}
console.log(multiply(2, 1, 2, 3)); // [2, 4, 6]
参数解构
// 对象参数解构
function createUser({name, age, city = "未知"}) {
return {
id: Math.random(),
name,
age,
city
};
}
let user = createUser({name: "王五", age: 30});
console.log(user);
// 数组参数解构
function add([x, y]) {
return x + y;
}
console.log(add([5, 3])); // 8
8.5 作用域和闭包
作用域决定了变量的可访问范围,闭包是函数和其词法环境的组合。
作用域
// 全局作用域
let globalVar = "全局变量";
function outer() {
// 函数作用域
let outerVar = "外部变量";
function inner() {
// 嵌套作用域
let innerVar = "内部变量";
console.log(globalVar); // 可访问
console.log(outerVar); // 可访问
console.log(innerVar); // 可访问
}
inner();
// console.log(innerVar); // 错误:无法访问
}
outer();
console.log(globalVar); // 可访问
// console.log(outerVar); // 错误:无法访问
块级作用域 (ES6)
// let和const创建块级作用域
if (true) {
let blockVar = "块级变量";
const BLOCK_CONST = "块级常量";
console.log(blockVar); // 可访问
}
// console.log(blockVar); // 错误:无法访问
闭包
// 闭包示例
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
let counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// 私有变量模拟
function createPerson(name) {
let _name = name; // 私有变量
return {
getName: function() {
return _name;
},
setName: function(newName) {
_name = newName;
}
};
}
let person = createPerson("张三");
console.log(person.getName()); // "张三"
person.setName("李四");
console.log(person.getName()); // "李四"
8.6 高阶函数
高阶函数是接收函数作为参数或返回函数的函数。
函数作为参数
// 数组方法中的高阶函数
let numbers = [1, 2, 3, 4, 5];
// map
let doubled = numbers.map(x => x * 2); // [2, 4, 6, 8, 10]
// filter
let evens = numbers.filter(x => x % 2 === 0); // [2, 4]
// reduce
let sum = numbers.reduce((acc, x) => acc + x, 0); // 15
// 自定义高阶函数
function repeat(func, times) {
return function(...args) {
for (let i = 0; i < times; i++) {
func(...args);
}
};
}
let sayHello = () => console.log("你好!");
let sayHelloThreeTimes = repeat(sayHello, 3);
sayHelloThreeTimes(); // 输出三次"你好!"
函数作为返回值
// 返回函数的函数
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
let double = createMultiplier(2);
let triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
8.7 立即执行函数表达式 (IIFE)
IIFE是一种定义后立即执行的函数。
// IIFE基本形式
(function() {
console.log("IIFE执行了");
})();
// 带参数的IIFE
(function(name) {
console.log(`你好, ${name}!`);
})("张三");
// 返回值的IIFE
let result = (function(a, b) {
return a + b;
})(5, 3);
console.log(result); // 8
// 创建私有作用域
(function() {
let privateVar = "私有变量";
// 在这里定义私有函数和变量
function privateFunction() {
console.log(privateVar);
}
// 暴露公共接口
window.MyModule = {
publicMethod: function() {
privateFunction();
}
};
})();
// MyModule.publicMethod(); // "私有变量"
8.8 函数的属性和方法
函数在ECMAScript中是对象,具有属性和方法。
函数属性
function myFunction(a, b, c) {
return a + b + c;
}
console.log(myFunction.length); // 3 (形参个数)
console.log(myFunction.name); // "myFunction" (函数名)
// 自定义属性
myFunction.description = "这是一个示例函数";
console.log(myFunction.description); // "这是一个示例函数"
call和apply方法
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
let user = { name: "张三" };
// call方法
let result1 = greet.call(user, "你好", "!");
console.log(result1); // "你好, 张三!"
// apply方法
let result2 = greet.apply(user, ["你好", "!"]);
console.log(result2); // "你好, 张三!"
bind方法
function multiply(a, b) {
return a * b;
}
// 绑定this值
let multiplyByTwo = multiply.bind(null, 2);
console.log(multiplyByTwo(5)); // 10
// 绑定this和部分参数
let obj = {
multiplier: 3,
multiply: function(a, b) {
return a * b * this.multiplier;
}
};
let boundMultiply = obj.multiply.bind(obj, 2);
console.log(boundMultiply(4)); // 24 (2 * 4 * 3)
提示:函数是ECMAScript中的一等公民,理解函数的各种特性和用法对于编写高质量的JavaScript代码至关重要。掌握闭包、高阶函数等概念可以帮助您写出更优雅和强大的代码。