函数
函数
函数(或者脚方法)是 JavaScript 应用程序的基础,它帮助你实现逻辑复用,模拟类,信息隐藏和模块。在 JavaScript 程序中,函数被当作第一等公民(first-class)使用。这意味着函数可以像任何其他变量一样被使用、传递和返回
创建函数
创建函数一般有两种方式:函数声明和函数表达式。
函数声明
函数声明使用 function
关键字来声明一个函数,后面跟函数名和函数体。函数名和函数体之间用小括号括起来
function functionName() {
// 函数体
}
函数表达式
函数表达式使用一个变量来接收一个函数。变量名既是函数名
var functionName = function() {
// 函数体
};
函数参数
我们可以通过函数的参数来将任意值传递给函数,以供函数内的逻辑使用
参数传递
JavaScript 中的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。
function add(num1, num2) {
return num1 + num2;
}
var result = add(5, 10);
console.log(result); // 输出 15
function setAge(person){
person.age = 25
}
const Tom = { name: 'Tom', age: 20 }
setAge(Tom)
console.log(Tom) // 输出 { name: 'Tom', age: 25 }
参数默认值
我们也可以在设置参数时给参数设置默认值,当调用函数时没有传递该参数时,该参数会使用默认值
function greet(name = 'World') {
console.log(`Hello, ${name}!`);
}
greet(); // 输出:Hello, World!
greet('Alice'); // 输出:Hello, Alice!
参数对象
在函数中,我们可以通过 arguments
对象来访问所有传入的参数。arguments
是一个类数组对象,它包含传入函数的所有参数。
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(1, 2, 3, 4, 5)); // 输出 15
length属性
length
属性表示函数希望接收的命名参数的个数,它不包括设置了默认值的参数
function greet(name, message) {
console.log(`Hello, ${name}! ${message}`);
}
greet.length; // 输出 2
function greet(name, message = 'Hello') {
console.log(`Hello, ${name}! ${message}`);
}
greet.length; // 输出 1
函数上下文
在创建函数时,就会创建该函数的函数上下文(或者叫作用域),函数上下文包含函数的参数和变量等相关信息,以供函数中编写的各种运算和处理逻辑使用。函数上下文会在函数调用时被激活,并在函数执行完毕后被销毁(函数执行上下文请看 函数执行上下文 章节)
function outer() {
var outerVar = '外部变量';
function inner() {
var innerVar = '内部变量';
console.log(outerVar);
console.log(innerVar);
}
inner();
}
outer(); // 输出:外部变量 内部变量
函数返回值
函数可以将内部运算处理逻辑的结果作为返回值提供给外部,返回值可以是任意类型,通过 return
关键字来返回
function add(num1, num2) {
return num1 + num2;
}
var result = add(5, 10);
console.log(result); // 输出 15
当函数设置返回值之后,即代表函数内部逻辑执行完成,函数内部返回值之后的代码便不再运行,当函数没有设置返回值时,函数内部返回 undefined
function add(num1, num2) {
return num1 + num2;
console.log('Hello World'); // 不会执行
}
var result = add(5, 10);
console.log(result); // 输出 15
函数调用
函数可以通过以下几种方式进行调用:
- 作为函数调用
- 作为方法调用
- 作为构造函数调用
- 通过函数引用调用
- 通过 apply 或 call 调用
作为函数调用
当函数作为函数调用时,函数内部的 this
指向全局对象(在浏览器中是 window
对象)
function sayHello() {
console.log('Hello, ' + this.name);
}
var person = {
name: 'John'
};
sayHello.call(person); // 输出:Hello, John
作为方法调用
当函数作为方法调用时,函数内部的 this
指向调用该函数的对象
var person = {
name: 'John',
sayHello: function() {
console.log('Hello, ' + this.name);
}
}
person.sayHello(); // 输出:Hello, John
作为构造函数调用
当函数作为构造函数调用时,函数内部的 this
指向新创建的对象
function Person(name) {
this.name = name;
}
var person = new Person('John');
console.log(person.name); // 输出:John
通过函数引用调用
当函数作为函数引用调用时,函数内部的 this
指向全局对象(在浏览器中是 window
对象)
var name = '全局变量';
function sayHello() {
console.log('Hello, ' + this.name);
}
sayHello(); // 输出:Hello, 全局变量
通过 apply 或 call 调用
通过 apply
或 call
方法,我们可以指定函数内部的 this
指向
function sayHello() {
console.log('Hello, ' + this.name);
}
var person = {
name: 'John'
};
sayHello.call(person); // 输出:Hello, John
函数用法
函数在 JavaScript 中有很多用法,可以帮助我们解决各种复杂问题
普通函数
将函数按照普通函数使用时,就像声明函数那样就可以了,在需要使用的地方进行调用
function greet(name) {
console.log('Hello, ' + name + '!');
}
greet('John'); // 输出:Hello, John!
回调函数
将函数作为参数传递给另一个函数,并在另一个函数内部调用该函数,这种函数称为回调函数
function greet(name) {
console.log('Hello, ' + name + '!');
}
function processUser(user, callback) {
// 处理用户信息
// ...
// 调用回调函数
callback && callback(user.name);
}
processUser({ name: 'John' }, greet); // 输出:Hello, John!
立即执行函数
将函数作为立即执行函数使用时,可以在函数内部定义局部变量,避免污染全局变量
(function() {
var name = 'John';
console.log('Hello, ' + name + '!');
})();
高阶函数
将函数作为参数传递给另一个函数,或者将函数作为返回值返回,这种函数称为高阶函数
function processUser() {
// 处理用户信息
// ...
return function (user, callback){
const fullName = `${user.firstName} ${user.lastName}`
callback && callback(fullName);
}
}
function greet(name) {
console.log('Hello, ' + name + '!');
}
processUser({ firstName: 'John',lastName: 'Doe' }, greet); // 输出:Hello, John Doe!
递归
将函数自身作为参数传递给自身,这种函数称为递归函数
function factorial(n) {
if (n === 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
console.log(factorial(5)); // 输出 120
在编写函数时,应当尽量保持函数功能的单一性,即一个函数只处理一件事情,如果遇到了复杂的逻辑功能,应当尽量将逻辑拆分成不同的函数来实现,这样可以提高代码的可读性和可维护性
函数的二义性
函数的二义性是指使用了 function
关键字创建的函数既可以作为普通函数使用,也可以作为构造函数使用,这样虽然在某些情况下很方便,但是也会导致函数的意义不明确。
function sayHello() {
console.log('Hello!');
}
const person = new sayHello();
console.log(person); // 输出:Hello!
sayHello(); // 输出:Hello!
而避免这种情况的方法便是使用箭头函数或者 Class
关键字。
箭头函数
箭头函数是 ES6 引入的一种新的函数语法,它是一种简洁的函数定义方式,可以用于替代传统的函数定义方式。箭头函数的语法比普通函数更加简洁,并且可以避免一些常见的问题,例如 this 绑定和变量提升。
基本语法
箭头函数的基本语法是 (参数) => 表达式
,其中参数是可选的,表达式是必需的。箭头函数的返回值是通过表达式来决定的。
const add = (a, b) => a + b;
const result = add(1, 2);
console.log(result); // 输出 3
没有参数
如果箭头函数没有参数,则可以省略小括号。
const sayHello = () => console.log('Hello!');
sayHello(); // 输出:Hello!
单个参数
如果箭头函数只有一个参数,则可以省略小括号和箭头。
const sayHello = name => console.log(`Hello, ${name}!`);
sayHello('John'); // 输出:Hello, John!
多个参数
如果箭头函数有多个参数,则必须使用小括号将参数括起来。
const add = (a, b) => a + b;
const result = add(1, 2);
console.log(result); // 输出 3
没有表达式
如果箭头函数没有表达式,则可以省略大括号和 return 关键字。
const sayHello = () => console.log('Hello!');
sayHello(); // 输出:Hello!
返回对象
如果箭头函数的返回值是一个对象,则必须使用括号将对象括起来。
const getUser = () => ({ name: 'John', age: 25 });
const user = getUser();
console.log(user.name); // 输出 John
console.log(user.age); // 输出 25
箭头函数的 this
箭头函数的 this 绑定是固定的,它不会随着调用方式的不同而改变。在箭头函数中,this 指向的是定义时所在的对象,而不是调用时所在的对象。
const person = {
name: 'John',
sayHello: function() {
setTimeout(() => console.log('Hello, ' + this.name + '!'), 1000);
}
};
person.sayHello(); // 输出:Hello, John!