JavaScript函数
在本章节中,我们将学习JavaScript函数的相关知识,包括函数的定义、调用、类型、属性和方法等内容。
1. 什么是函数?
函数是一段可重复使用的代码块,用于执行特定的任务。函数可以接受输入参数,并返回一个输出结果。
函数的主要作用:
- 封装代码,提高代码的可重用性
- 提高代码的可读性和可维护性
- 实现模块化编程
- 实现异步编程
2. 函数的定义
在JavaScript中,有多种定义函数的方式:
2.1 函数声明
使用function关键字声明函数。
// 函数声明语法
function functionName(parameters) {
// 函数体
return value; // 可选的返回值
}
// 函数声明示例
function add(a, b) {
return a + b;
}
// 调用函数
let result = add(5, 3); // 82.2 函数表达式
将函数赋值给一个变量。
// 匿名函数表达式
let add = function(a, b) {
return a + b;
};
// 命名函数表达式
let multiply = function multiply(a, b) {
return a * b;
};
// 调用函数
let result1 = add(5, 3); // 8
let result2 = multiply(5, 3); // 152.3 箭头函数
使用箭头=>定义函数,是ES6(2015)引入的新特性。
// 箭头函数语法
let functionName = (parameters) => {
// 函数体
return value; // 可选的返回值
};
// 简化语法:当只有一个参数时,可以省略括号
let square = x => x * x;
// 简化语法:当函数体只有一条返回语句时,可以省略大括号和return关键字
let add = (a, b) => a + b;
// 调用函数
let result1 = square(5); // 25
let result2 = add(5, 3); // 82.4 Function构造函数
使用Function构造函数创建函数,不推荐使用,因为它会影响性能和安全性。
// Function构造函数示例
let add = new Function('a', 'b', 'return a + b');
// 调用函数
let result = add(5, 3); // 83. 函数的调用
函数定义后,需要调用才能执行。函数调用的方式有多种:
3.1 直接调用
直接使用函数名调用函数。
function test() {
console.log("直接调用函数");
}
test(); // 直接调用函数3.2 作为方法调用
函数作为对象的方法调用,此时this指向调用该方法的对象。
let person = {
name: "张三",
sayHello: function() {
console.log(`你好,我叫${this.name}。`);
}
};
person.sayHello(); // 你好,我叫张三。3.3 作为构造函数调用
使用new关键字调用函数,创建一个新对象,此时this指向新创建的对象。
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`你好,我叫${this.name},今年${this.age}岁。`);
};
}
let person = new Person("张三", 18);
person.sayHello(); // 你好,我叫张三,今年18岁。3.4 使用call()和apply()调用
使用call()和apply()方法调用函数,可以显式指定this的值。
function sayHello(city) {
console.log(`你好,我叫${this.name},今年${this.age}岁,来自${city}。`);
}
let person = {
name: "张三",
age: 18
};
// 使用call()调用函数,参数逐个传递
sayHello.call(person, "北京"); // 你好,我叫张三,今年18岁,来自北京。
// 使用apply()调用函数,参数以数组形式传递
sayHello.apply(person, ["上海"]); // 你好,我叫张三,今年18岁,来自上海。3.5 使用bind()调用
使用bind()方法创建一个新函数,绑定this的值和部分参数。
function sayHello(city, country) {
console.log(`你好,我叫${this.name},今年${this.age}岁,来自${city}${country}。`);
}
let person = {
name: "张三",
age: 18
};
// 使用bind()创建一个新函数,绑定this和部分参数
let sayHelloToChina = sayHello.bind(person, "北京");
// 调用新函数
sayHelloToChina("中国"); // 你好,我叫张三,今年18岁,来自北京中国。4. 函数的参数
函数可以接受零个或多个参数。在JavaScript中,函数参数具有以下特性:
4.1 参数的默认值
可以为函数参数设置默认值,当调用函数时不传递该参数或传递undefined时,使用默认值。
// 参数默认值示例
function greet(name = "陌生人") {
console.log(`你好,${name}!`);
}
greet(); // 你好,陌生人!
greet("张三"); // 你好,张三!
greet(undefined); // 你好,陌生人!
// 多个参数的默认值
function add(a = 0, b = 0) {
return a + b;
}
console.log(add()); // 0
console.log(add(5)); // 5
console.log(add(5, 3)); // 84.2 剩余参数
使用...语法定义剩余参数,将剩余的参数收集到一个数组中。
// 剩余参数示例
function sum(...numbers) {
return numbers.reduce((total, number) => total + number, 0);
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15
// 剩余参数必须是最后一个参数
function multiply(multiplier, ...numbers) {
return numbers.map(number => number * multiplier);
}
console.log(multiply(2, 1, 2, 3)); // [2, 4, 6]4.3 展开运算符
使用...语法将数组或对象展开为多个参数。
// 展开运算符示例
let numbers = [1, 2, 3, 4, 5];
// 将数组展开为多个参数
console.log(Math.max(...numbers)); // 5
// 合并数组
let moreNumbers = [6, 7, 8];
let allNumbers = [...numbers, ...moreNumbers]; // [1, 2, 3, 4, 5, 6, 7, 8]
// 合并对象
let person1 = { name: "张三", age: 18 };
let person2 = { city: "北京", ...person1 };
console.log(person2); // { city: "北京", name: "张三", age: 18 }5. 函数的返回值
函数可以使用return语句返回一个值,return语句会终止函数的执行。
// 返回一个值
function add(a, b) {
return a + b;
}
let result = add(5, 3); // 8
// 返回多个值(通过数组或对象)
function getPerson() {
return {
name: "张三",
age: 18,
city: "北京"
};
}
let person = getPerson();
console.log(person.name); // 张三
console.log(person.age); // 18
console.log(person.city); // 北京
// 提前返回
function isEven(number) {
if (typeof number !== "number") {
return false;
}
return number % 2 === 0;
}
console.log(isEven(4)); // true
console.log(isEven(5)); // false
console.log(isEven("4")); // false6. 函数的作用域
函数内部可以访问其所在作用域中的变量,以及全局作用域中的变量。
// 全局变量
let globalVar = "全局变量";
function outer() {
// 外部函数变量
let outerVar = "外部函数变量";
function inner() {
// 内部函数变量
let innerVar = "内部函数变量";
// 可以访问所有作用域中的变量
console.log(innerVar); // 内部函数变量
console.log(outerVar); // 外部函数变量
console.log(globalVar); // 全局变量
}
inner();
// 无法访问内部函数变量
console.log(innerVar); // 报错:innerVar is not defined
}
outer();7. 函数的属性和方法
函数是对象,具有属性和方法。
7.1 name属性
name属性返回函数的名称。
// 函数名称示例
function add(a, b) {
return a + b;
}
console.log(add.name); // "add"
// 匿名函数表达式的名称
let multiply = function(a, b) {
return a * b;
};
console.log(multiply.name); // "multiply"
// 箭头函数的名称
let square = x => x * x;
console.log(square.name); // "square"7.2 length属性
length属性返回函数参数的数量。
// 函数length属性示例
function add(a, b) {
return a + b;
}
console.log(add.length); // 2
// 带默认值的参数
function greet(name = "陌生人") {
console.log(`你好,${name}!`);
}
console.log(greet.length); // 0
// 剩余参数
function sum(...numbers) {
return numbers.reduce((total, number) => total + number, 0);
}
console.log(sum.length); // 07.3 call()方法
call()方法调用函数,显式指定this的值和参数。
// call()方法示例
function sayHello(city) {
console.log(`你好,我叫${this.name},来自${city}。`);
}
let person = {
name: "张三"
};
sayHello.call(person, "北京"); // 你好,我叫张三,来自北京。7.4 apply()方法
apply()方法调用函数,显式指定this的值,参数以数组形式传递。
// apply()方法示例
function sayHello(city, country) {
console.log(`你好,我叫${this.name},来自${city}${country}。`);
}
let person = {
name: "张三"
};
sayHello.apply(person, ["北京", "中国"]); // 你好,我叫张三,来自北京中国。7.5 bind()方法
bind()方法创建一个新函数,绑定this的值和部分参数。
// bind()方法示例
function sayHello(city) {
console.log(`你好,我叫${this.name},来自${city}。`);
}
let person = {
name: "张三"
};
let sayHelloToBeijing = sayHello.bind(person, "北京");
sayHelloToBeijing(); // 你好,我叫张三,来自北京。8. 函数的类型
8.1 普通函数
使用函数声明或函数表达式定义的函数。
function add(a, b) {
return a + b;
}
let multiply = function(a, b) {
return a * b;
};8.2 箭头函数
使用箭头=>定义的函数,是ES6(2015)引入的新特性。
let add = (a, b) => a + b;
let square = x => x * x;
let greet = () => console.log("你好!");8.3 匿名函数
没有名称的函数。
// 匿名函数作为参数
setTimeout(function() {
console.log("1秒后执行");
}, 1000);
// 匿名函数作为事件处理程序
document.getElementById("btn").addEventListener("click", function() {
console.log("按钮被点击了");
});8.4 递归函数
调用自身的函数。
// 递归函数示例:计算阶乘
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
// 递归函数示例:计算斐波那契数列
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
console.log(fibonacci(6)); // 88.5 高阶函数
接受一个或多个函数作为参数,或者返回一个函数的函数。
// 高阶函数示例:接受一个函数作为参数
function map(arr, callback) {
let result = [];
for (let i = 0; i < arr.length; i++) {
result.push(callback(arr[i]));
}
return result;
}
let numbers = [1, 2, 3, 4, 5];
let squares = map(numbers, x => x * x); // [1, 4, 9, 16, 25]
// 高阶函数示例:返回一个函数
function multiplyBy(multiplier) {
return function(number) {
return number * multiplier;
};
}
let double = multiplyBy(2);
let triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 158.6 立即执行函数表达式(IIFE)
定义后立即执行的函数表达式。
// IIFE语法
(function() {
// 函数体
})();
// IIFE示例
(function() {
console.log("立即执行函数");
})();
// 带参数的IIFE
(function(name) {
console.log(`你好,${name}!`);
})("张三"); // 你好,张三!
// 箭头函数的IIFE
(() => {
console.log("箭头函数的IIFE");
})();9. 箭头函数
箭头函数是ES6(2015)引入的新特性,它提供了一种更简洁的函数定义方式。
9.1 箭头函数的特点
- 语法简洁
- 没有自己的
this,继承父级作用域的this - 不能作为构造函数使用
- 没有
arguments对象 - 没有
prototype属性
9.2 箭头函数示例
// 基本语法
let add = (a, b) => a + b;
// 只有一个参数时,可以省略括号
let square = x => x * x;
// 没有参数时,必须使用括号
let greet = () => console.log("你好!");
// 函数体有多条语句时,必须使用大括号和return关键字
let multiply = (a, b) => {
let result = a * b;
return result;
};
// 返回对象时,必须使用括号包裹对象
let createPerson = (name, age) => ({ name: name, age: age });9.3 箭头函数的this
箭头函数没有自己的this,它继承父级作用域的this。
// 普通函数的this
let person = {
name: "张三",
age: 18,
sayHello: function() {
console.log(`你好,我叫${this.name}。`); // this指向person对象
// 普通函数的this指向全局对象或undefined(严格模式)
setTimeout(function() {
console.log(`我今年${this.age}岁。`); // this指向全局对象,age为undefined
}, 1000);
}
};
person.sayHello();
// 箭头函数的this
let person2 = {
name: "李四",
age: 20,
sayHello: function() {
console.log(`你好,我叫${this.name}。`); // this指向person2对象
// 箭头函数的this继承父级作用域的this,即person2对象
setTimeout(() => {
console.log(`我今年${this.age}岁。`); // this指向person2对象,age为20
}, 1000);
}
};
person2.sayHello();10. 常见问题解答
Q: 函数声明和函数表达式有什么区别?
A: 函数声明会被提升到作用域顶部,可以在声明之前调用;函数表达式不会被提升,只能在定义之后调用。
Q: 箭头函数和普通函数有什么区别?
A: 箭头函数语法简洁,没有自己的this,不能作为构造函数使用,没有arguments对象,没有prototype属性。
Q: 什么是高阶函数?
A: 高阶函数是指接受一个或多个函数作为参数,或者返回一个函数的函数。
Q: 什么是递归函数?
A: 递归函数是指调用自身的函数,用于解决可以分解为相同子问题的问题。
Q: 什么是IIFE?
A: IIFE(立即执行函数表达式)是指定义后立即执行的函数表达式,用于创建独立的作用域,避免变量污染。
Q: 如何显式指定函数的this值?
A: 可以使用call()、apply()和bind()方法显式指定函数的this值。
11. 练习项目
创建一个HTML文件,包含以下内容:
- 一个输入框,用于输入数字
- 一个按钮,用于计算阶乘
- 一个显示结果的区域
使用JavaScript实现以下功能:
- 定义一个递归函数,计算一个数的阶乘
- 点击按钮时,获取输入框的值
- 调用阶乘函数计算结果
- 在结果区域显示计算结果
- 处理可能的错误,如输入非数字或负数
测试不同的输入值,确保计算结果正确
12. 小结
- 函数是一段可重复使用的代码块,用于执行特定的任务
- JavaScript中有多种定义函数的方式:函数声明、函数表达式、箭头函数、Function构造函数
- 函数可以接受参数,并返回一个值
- 函数是对象,具有属性和方法
- 箭头函数是ES6引入的新特性,语法简洁,没有自己的this
- 高阶函数是指接受函数作为参数或返回函数的函数
- 递归函数是指调用自身的函数
- IIFE是指定义后立即执行的函数表达式
- 函数的this值取决于调用方式
在下一章节中,我们将学习JavaScript函数参数的相关知识。