Js函数(六) 回调函数和闭包函数 2021-11-21

JavaScript025

Js函数(六) 回调函数和闭包函数 2021-11-21,第1张

回调函数概念:    函数的参数callback是另一个参数,这个参数在原始数据中执行

例如:

  let arr = [11, 22, 33, 44, 55, 66, 77, 88, 99]

    function liFor(arr, callback) {

        for (let i = 0i <arr.lengthi++) {

            if (callback(arr[i])) {

                console.log(arr[i])

            }

        }

    }

    console.log('---------遍历数组----------')

    liFor(arr, val =>true)

    console.log('---------输出奇数----------')

    liFor(arr, val =>val % 2 !== 0)

    console.log('---------输出被3整除----------')

    liFor(arr, val =>val % 3 === 0)

    console.log('---------输出被3和6整除----------')

    liFor(arr, val =>val % 3 === 0 &&val % 6 === 0)

闭包函数概念:定义一个a方法,在a方法中定义一个b方法,并且b方法里面用到了a方法里面定义的变量,那么此时就形成了闭包函数,由于内部方法里面,用到外部方法里面的变量,外部方法里面的那个变量会一直在内存中存保存着。两个方法嵌套定义,里面的方法,用到了外面方法里面定义的变量,此时这两个方法就形成了闭包。

例如:

function a() {

            console.log('a函数被调用了...')

            let num1 = 100

            let num2 = 200

            function b() {

                console.log('b函数被调用了...')

                console.log(num1 + num2)

            }

            //返回的返回值也是一个函数,那么a函数就是高阶函数。

            return b

        }

        // 通常情况下,函数执行完成后,函数里面定义的变量,会被销毁。

        // a函数,已经调用完毕了,但是a函数里面定义变量,始终在内存中,因为b函数中用到了a函数中定义的变量。

        // 那么此时这两个函数,就称之为:闭包函数。

        let c = a()

        c()

        console.log('------------------------------------------')

        // 闭包函数的实际案例

        function calc(num1, num2, type) {

            switch (type) {

                case '+':

                    console.log(`${num1}+${num2}=${num1 + num2}`)

                    break

                case '-':

                    console.log(`${num1}-${num2}=${num1 - num2}`)

                    break

            }

        }

        // 在实际开发中,我们在做num1和num2的计算之前,可能需要先做其他事情

        let num1 = 100

        let num2 = 50

            // 在做其他事情的过程中,我们的数据很有可能会被篡改。

        console.log('查看用户是否登录')

        num1 = 555

        num2 = 145

        console.log('检查用户的权限')

        calc(num1, num2, '+') //运行结果不对,因为变量的值被篡改了。

        console.log('------------------------------------------')

        // 定义一个闭包函数,实现计算器功能

        function myCalc(num1, num2, type) {

            switch (type) {

                case '+':

                    return function() {

                        return num1 + num2

                    }

                case '-':

                    return function() {

                        return num1 - num2

                    }

            }

        }

        //先准备好你的数据

        let n1 = 100

        let n2 = 50

            //将你的数据传给计算器方法,由计算器方法,返回一个计算方法。

        let js = myCalc(n1, n2, '+')

            //在做具体的计算之前,还先做些其他的事情

        console.log('查看用户是否登录')

        n1 = 555

        n2 = 145

        console.log('检查用户的权限')

        //其他事件准备好了后,执行计算方法

        console.log(js())

在A函数中嵌套B函数,B函数访问A函数中的变量。将A函数复制给C函数并执行。

那么在大多数的理解中,包括许多著名的书籍,文章里都以函数C的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。

而我的理解是:闭包更准确的说是一项技术或者一个特性:只要运用具备阻止垃圾回收机制回收和突破作用域链限制的技术,就是闭包。像是《JavaScript权威指南》打的比方,像是把变量包裹了起来,形象的称为“闭包”。

如果非要指明哪个函数是闭包的话,我愿意将A函数称为定义闭包的函数,C函数为执行闭包的函数。

a. 在函数内部创建新的函数;

b. 新的函数在执行时,访问了函数的变量对象。

c. 闭包是在函数被调用执行的时候才被确认创建的。

一句话总结:==函数中闭包判定的准则,即执行时是否在内部定义的函数中访问了上层作用域的变量。==

闭包,阻止垃圾回收机制。

闭包,突破作用域链接。

《你不知道的JS中》的示例:

模块模式需要具备两个必要条件:

1.必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。

2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态。

什么时候使用模块模式?

如果必须创建一个对象并一某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。

哪些地方有用到模块模式?

最典型的就是JQuery库,jQuery和$标识符就是JQuery模块的公共API,但它们本身都是函数(由于函数也是对象,它们本身也可以拥有属性)

以后单独拿一个章节,来具体讲讲现在的模块化和未来的模块化机制。

具体的内容在函数的柯里化的章节中详细分析。

戳此传送门

输出6的原因是:被封闭在一个共享的全局作用域中,实际上只有一个i。

依次输出1-5的原因是:在迭代内部使用IIFE(立即表达函数)为每个迭代生成一个新的作用域,将外部变量的值传递进去并被引用(阻止垃圾回收机制回收),使得每个回调函数都能访问到正确值的变量。

戳此传送门

闭包是:指有有访问另一个函数作用域中的变量的函数。

创建闭包的常用方式:在一个函数内部创建一个函数。例子:

functioncreateFunction(name){

    return function(obj1, obj2){

        var value1 = obj1[name]

        var value2 = obj2[name]

       

        return value1 + ", " +value2

    }

}

了解创建作用域以及作用域有什么作用的细节,对彻底理解闭包到头重要。当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后,使用arguments和其他参数的值来初始化函数的活动对象

一、闭包

1、createFunction()创建时,它(compare)的作用域包含createFunction()函数的活动对象和全局对象。

//创建函数

var compare = createFunction("name")

console.log(typeofcompare)  //function

2、匿名函数被执行时,创建自己的活动对象和可以访问createFunction()的执行环境。

//调用函数

var result =compare({name: "jjaiy"}, {name:"ascy"})  //jjaiyy, ascy

3、createFunction()函数执行完后,其执行环境的作用域不会被销毁。原因在于它的活动对象还在内存中,直到匿名函数被销毁。

//解除对匿名函数的引用(以便释放内存)

compare = null

原因在于:

浏览器的垃圾收集方式有:

标记清除(主要针对变量)

引用计数(跟踪记录每个值被引用的次数)

二、实例运行

封装功能

var route = {

         routes: {},

         for: function(path, handler){ //闭包

                   this.routes[path] = handler  //引用回调函数

         }

}

//执行二个匿名函数

route.for("/start",function(request, response){

         response.writeHead(200,{"Content-Type": "text/plain"})

         response.write("Hello")

         response.end()

})

 

route.for("/finish",function(request, response){

         response.writeHead(200,{"Content-Type": "text/plain"})

         response.write("Goodbye")

         response.end()

})

//先判断argument中的path变量是否一致,能否调用其函数中的回调函数。

if(typeof route.routes["/finish"] === 'function'){

        route.routes["/finish"](request, response)

    }