跳至主要內容

函数

wzCoding大约 7 分钟JavaScript函数

函数

函数(或者脚方法)是 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 调用

通过 applycall 方法,我们可以指定函数内部的 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!