JS的回调函数 | 青训营笔记

40 阅读5分钟

「青训营」笔记创作活动的的第5天

认识JS中的函数对象

在JS中万物皆为对象,由于函数的特殊性,函数在JS解析和执行时,会被维护成一个对象,这就是要介绍的函数对象

  • 我们可以使用function关键字定义一个函数,并为每个函数指定一个函数名,通过函数名来进行调用。
//函数声明的三种方式

//第一种:普通方式声明(常用)
function f1(num1,num2){
    return num1 + num2
}

//第二种:使用变量初始化的方式声明
var f2 = function(num1,num2){
    return num1 + num2
}

//第三种:使用Function构造函数的方式(微信小程序不允许使用这种方法,new Function('return this')除外)
var f3 = new Function('num1','num2','return num1 + num2')
复制代码
  • 函数是用Function()构造函数创建的Function对象。函数对象(Function)与日期对象(Date)、数组对象(Array)、字符串对象(String)等都称为内部对象,我们可以通过typeof( )函数查看一个对象的类型。
console.log(typeof (Function));  //function
console.log(typeof (Array));  //function
console.log(typeof (Object));  //function
console.log(typeof (new Array()));  //object
console.log(typeof (new Date()));  //object
console.log(typeof (new Object()));  //object
console.log(typeof (new Function()));  //function //小程序不能运行这行代码
console.log(new Function() instanceof Object);    //true

//由此可见,对象是通过函数创建的,而函数本身又是一个类型为Function的特殊对象。
//Function是所有函数对象的基础,而Object则是所有对象(包括函数对象)的基础。
复制代码

回调函数

经过上一节的了解,在JS中函数也是一类对象,这意味着函数能够像对象一样被使用。既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回。

回调函数是从一个叫函数式编程的编程范式中衍生出来的概念。简单来说,函数式编程就是使用函数作为变量。函数式编程中的一个主要技巧就是回调函数。

简单来说回调函数是一个被作为参数传递给另一个函数的函数。

举个例子:你到一个商店去购物,刚好你想要的东西没有货,于是你在店员那里留下了你的电话,叫店员有货了就通知你,过了几天店里有货了,店员于是打电话联系你,然后你接到电话后就去店里取了货。在这个例子里面,你将号码留下并让店员在有货时通知你的这个行为就叫回调函数,店里后来有货了叫做触发了回调关联的事件,店员打电话通知你叫做调用回调函数,你到店里去取货叫做响应回调事件

1、常见的回调函数

(1)DOM事件回调

document.getElementById('btn').onclick = function(){
    alert('hello world!')
}
复制代码

(2)定时器回调

setTimeout(function(){
    alert('hello world!')
},1000)
复制代码

2、回调函数的特点

(1)将回调函数作为变量传递给触发函数,体现了变量的灵活性。可将通用的逻辑抽象,将回调函数作为专职的函数进行分离,提高代码的可维护性和可读性。

//方式一
function a(){
    console.log('a函数原本执行的内容');
    b() //直接在a函数内调用b函数
}
//这样子也能到达由a函数控制b函数执行的目的,但是将b函数写在a函数内就会被限制住了,失去了灵活性

//方式二
function a(callback){
    console.log('a函数原本执行的内容');
    callback();
}
function test(){ //这样子可以灵活地去在a函数执行时触发不同的回调函数
    a(b);
    a(c);
}

//两个回调函数
function b(){
	alert("我是回调函数b");
}
function c(){
    alert("我是回调函数c");
}

复制代码

(2)回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。因此,回调本质上是一种设计模式。

(3)不会立即执行,回调就是一个函数被另一个函数调用的过程。例如:函数a有一个参数,这个参数是函数b,函数a会决定函数b在什么时候被执行。那么这个过程就叫回调,即函数b在某个特定的时间点被函数a回调(通常这个特定的时间点是在函数a中的异步任务执行完后)。

(4)回调函数的闭包,我们将一个回调函数作为变量传递给另一个函数时,这个回调函数在包含它的函数内的某一特定时间点执行,就好像这个回调函数是在包含它的函数中定义的一样。这意味着回调函数本质上是一个闭包。正如我们所知,闭包能够进入包含它的函数的作用域,因此回调函数能获取包含它的函数中的变量,以及全局作用域中的变量。

3、回调函数的使用

(1)使用命名或匿名的函数作为回调

function a(callback){
    console.log('a函数执行了')
    callback()
}

function b(){
    console.log('回调函数b执行了')
}

function test(){
    a(b) //使用命名的回调函数
    a(function(){ //使用匿名的回调函数
        console.log('匿名回调函数函数执行了')
    })
}
复制代码

(2)传递参数给回调函数

var name = 'dj'

function a(name,callback){
    console.log('a函数执行了')
    callback(name)
}

function b(name){ //回调函数b需要一个参数
    console.log('回调函数b执行了')
    console.log('hello '+name)
}

function test(){
    a(name,b) //这里的第一个参数name就是第一行代码定义的name变量,在此时传入给到回调函数b使用;第二个参数就是回调函数b
}
复制代码

(3)在执行前确保回调函数是一个函数,如果不确定传入的callback是否为一个函数,推荐用这种写法,先判定是否为函数再进行调用,以免出现不必要的报错。

function a(callback){
    console.log('a函数执行了')
    //确保callback是一个函数   
    if(typeof callback === "function"){
        callback();
    }
}

function b(){
    console.log('回调函数b执行了')
}

function test(){
    a(b)
}