Javascript的回调函数是什么,以及如何使用它

什么是回调函数

回调函数的讲解

回调函数是作为参数传递给另外一个函数,在主函数执行后或在该函数确定的点执行。

回调是JavaScript异步编程模型的支柱,也是Javascript在Web开发中如此强大的原因。

特点:可以支持非阻塞代码的执行,允许应用程序在执行等待API调用、用户交互或定时器操作完成时继续执行下去。

示例代码:

js 复制代码
// 基础的回调案例
function greet(name, callback) {
    console.log('hello ' + name)
    callback()
 }
 
function callbackFunction() {
  console.log('Callback function executed!');
}

greet('Tom', callbackFunction);

// 输出
// hello Tom
// Callback function executed!

示例中greet是主函数,callbackFunction作为参数传入greet中,主函数执行回调函数同时跟着执行。

特别提示:回调函数是JavaScript中异步编程的基础,可以处理耗时操作而不阻塞主线程。这对于创建响应式Web应用程序至关重要,这些应用程序在执行获取数据或处理大型数据集等任务不会卡顿。

语法和用法:

js 复制代码
// 回调函数
function doSomething(callback) {
    callback()
}

// 普通函数作为回调函数
doSomething(function() {
    console.log('callback executed')
})

// 这是一个箭头函数作为回调函数
doSomething(() => {
    console.log('callback executed')
})

关于回调的要点:

  1. 它们是可以命名函数或匿名函数
  2. 它们可以接受参数
  3. 它们可以是箭头函数
  4. 它们通常用于异步函数
  5. 它们可以嵌套使用(可能导致回调地狱)
  6. 它们支持JavaScript函数式编程模式
  7. 它们允许控制反转,接受回调的函数决定何时以及如何执行

下面结合几个示例讲解说明

示例1 - 浏览器中的事件处理

js 复制代码
const button = document.getElementById('button');

button.addEventListener('click', function(event) {
    console.log('按钮被点击了')
    console.log('坐标:', event.clientX, event.clientY)
    console.log('目标元素:', event.tatget)
    
    event.preventDefault();
    
    event.stopPropagation();
})

button.addEventListener('click', () => {
     console.log('按钮被点击了')
     console.log('事件类型', event.type)
})

方法addEventListener就是作为主函数,回调函数则是第二个参数,同时回调函数接受一个参数event,描述了当前点击的对象属性以及所携带的方法。

这种模式可以实现:

  1. 响应式用户界面,可对用户交互做出反应
  2. 解耦时间处理逻辑,提高代码的可维护性
  3. 同一事件的多个处理程序
  4. 动态时间处理
  5. 事件委托,用户有效处理多个元素上的事件

说明:如果没有回调,交互式web程序几乎不可能实现,因为无法以非阻塞的方式响应用户操作。

示例2 - 带有回调的数组方法

js 复制代码
const numbers = [1, 2, 3, 4, 5]

// forEach
numbers.forEach(function(number, index, array) {
    // number: 当前的元素
    // index: 当前元素的索引值
    // array: 数组本身
    console.log(number, index, array)
})

// map
// 对数组中每个元素乘以2
const doubled = numbers.map((number, index) => {
    return number * 2
})

// ...

带有回调的数组方法是用于数据转换和操作的强大工具,这些方法实现了函数式编程模式,使代码更具声明性,可读性。

  1. forEach:对每个元素执行回调;无法停止(与for循环不同);返回未定义(undefined);非常适日志记录或DOM更新等副作用;比传统的for循环更易读的替代方案。
  2. map:使用转换后的元素创建一个新数组;必须在回调中返回一个值;保持相同的数组长度;非常适合进行数据转换而不改变原数组;常用于框架渲染。
  3. 其他关于带有回调的数组方法:filter、find、findIndex、some、sort、flatMap等。

示例3 - 带有回调的异步操作

js 复制代码
function fetchData(url, successCallback, errorCallback, loadingCallback) {
    loadingCallback(true);
    
    fetch(url)
        .then(response => {
            if (!response.ok) {
                throw new Error('')
            }
            return response.json();
        })
        .then(data => {
            loadingCallback(false)
            successCallback(data)
        })
        .catch(error => {
            loadingCallback(false)
            errorCallback(error)
        })
}

异步操作是回调真正发挥作用的地方,并展示了在JavaScript中重要作用。

好处:

  1. 非阻塞操作可以让应用程序保持响应
  2. 为应用程序提供适当的错误处理
  3. 加载状态的管理可以获得更好的用户体验
  4. 针对不同的场景灵活响应处理
  5. 数据获取和Ui更新分离

提示:虽然JavaScript提供了Promise和async/await作为处理异步函数更优雅的解决方法,但是理解回调至关重要,因为他们构成了这些新模式的基础,并且许多框架和库都有使用。

示例4 - 带有回调的自定义高阶函数

js 复制代码
// 创建一个自定义高阶函数
function retryOperation(operation, maxAttempts, delay, callback) {
    let attempts = 0;
    
    function attempt() {
        attempts++;
        
        try {
            const relult = operation();
            callback(null, result)
        } catch (error) {
            console.error()
            
            if (attempts < maxAttempts) {
                setTimeout(attempt, delay)
            } else {
               callback()
           }
        }
    }
    
    attempt()
}

提示: 处理多个异步操作时,请考虑使用Promise.all() 或async/await 而不是嵌套回调,以保持代码的可读性,并避免'回调地狱'和'末日金字塔',这些会使代码难于维护。

常见的面试题

  1. 什么是回调函数,为什么使用回调函数
  2. 什么是回调地狱已经如何避免它
  3. 回调和异步编程有何关系

备注:本人前端菜鸟一枚,欢迎指出其中的不足。

相关推荐
森叶2 分钟前
利用 Chrome devTools Source Override 实现JS逆向破解案例
前端·javascript·chrome devtools
市民中心的蟋蟀8 分钟前
第四章: 使用订阅来共享模块状态
前端·javascript·react.js
1_2_3_13 分钟前
js 空值合并操作符(??)
javascript
only-lucky28 分钟前
QT之QML(简单示例)
前端·javascript·qt
巴巴博一29 分钟前
Vue Transition组件类名+TailwindCSS
前端·javascript·vue.js
北极糊的狐44 分钟前
若依项目通用套路——列表页面提前加载数据塞进下拉框待选项
javascript·vue.js·elementui
Stuild Stuil1 小时前
Mysql 字段值批量自增或自减(坐标系数据,(x,y))
java·javascript·mysql
水煮白菜王1 小时前
首屏加载时间优化解决
前端·javascript·react.js
还是鼠鼠1 小时前
Node.js 中间件-中间件的概念与格式
前端·javascript·vscode·node.js·express
路光.1 小时前
Vue3实现锚点定位
前端·javascript·vue.js·vue3