闭包

// 闭包
// 何为闭包?: 有权访问另外一个函数作用域中变量的函数,简单形式就是:外部函数中生成一个内部函数,并通过return来返回,内部函数可以访问外部函数作用域中的函数,因此,外部函数调用之后变量对象本该被销毁,但由于闭包的存在,我们仍然可以在全局访问外部函数的变量。
// 关键:闭包引用外部函数的变量;在外部函数的外部调用闭包
// 注意事项:函数的作用域会一直保存到闭包不存在
// example:
function arrFn(){
    var arr = []
    for(var i=0;i<10;i++){
        arr[i] = function(){
            return i
        }
    }
    return arr
}
arrFn().map(((ele)=>{
    console.log(ele())
}))
// 输出为10个10
// arr数组中为10个匿名函数,当arrFunc执行完毕之后,作用域被销毁,但是变量对象仍然保存在内存中得以被匿名访问,遍历数组执行数组中的每个匿名函数时,匿名函数访问的i均为10

function arrFunc(){
    var arr = []
    for(var i=0;i<10;i++){
        arr[i] = (function(){
            return i;
        })()
    }
    return arr;
}
console.log("arrFunc",arrFunc())
// 返回[0,1,2,3,4,5,6,7,8,9] arr数组中保存的是匿名函数立即执行后的返回值

// 匿名函数外层再嵌套一层函数,来保存循环过程中的每个i值
function arrFunc2(){
    var arr = []
    for(var i=0;i<10;i++){
        arr[i] = function(num){
            return (function(){
                return num;
            })
        }(i)
    }
    return arr
}
arrFunc2().map((ele)=>{
    console.log("arrFunc2",ele())
})
// 输出为 0-9

// 闭包的使用场景

// 1. 封装变量
// 场景:数据请求,上一次数据请求结果返回后,才能进行下次请求,设置flat标志是否请求完成 false-未结束,true--请求结束
// 未使用闭包
var flat = false
var fn = function () {
    if(flat){
        // ajax进行数据请求
        $.ajax({
            url: '',
            data: {},
            type: 'json',
            success: function () {
            },
            complete: function () {
                flat = true
            }
        })
    }else{
        return;
    }
}
// 使用闭包来写
var fn = function () {
    var flat = false
    return (function () {
        if(flat){
            // ajax进行数据请求
            $.ajax({
                url: '',
                data: {},
                type: 'json',
                success: function () {
                },
                complete: function () {
                    flat = true
                }
            })
        }else{
            return;
        }        
    })
}
// var f = fn()
// f()
// VS: 不使用闭包,flat为全局变量暴露在全局,污染全局环境,使用闭包 flat作为局部变量,进行封装,不会被全局中其他方法访问/改变

// 2. 利用闭包,返回/生成其他的函数
// 场景: 邀新banner为例,不同的module_id对应执行不同的方法
// 不使用闭包
// if(module_id === 'invite0'){
//     f1()
// }else if(module_id === 'inviteN'){
//     f2()
// }
// 使用闭包
var action = function(module_id){
    // if(module_id === 'invite0'){
    //     return (function () {
    //         // "invite0"时执行的方法
    //     })()
    // }else if(module_id === 'inviteN'){
    //     return (function () {
    //         // "inviteN"时执行的方法
    //     })()
    // }
    // 优化:
    var map = {
        "invite0": function () {
            console.log("0")
        },
        "inviteN": function () {
            console.log("N")
        }
    }
    return map[module_id]()
}
// action("invite0")
// VS: 利用闭包将不同module_id执行不同方法 封装成一个方法,使用时,直接调用action即可,

// 3. 解决浏览器兼容性问题
// 添加事件监听为例
// 不使用闭包

// 使用闭包
// 闭包一
var addEventListener = function (dom, type, fn) {
    if(dom.addEventListener){
        return (function () {
            dom.addEventListener(type, fn, false)
        })()
    }else{
        return (function () {
            dom.attachEvent("on"+type, fn)
        })()
    }
}
// 闭包二: 上述闭包 每次调用闭包方法都会进行一次dom.addEventListener的判断 -- 只在第一次运行的时候判断
var addEventListener = function (dom, type, fn) {
    if(dom.addEventListener){
        return (function () {
            addEventListener = dom.addEventListener(type, fn, false)
        })()
    }else{
        return (function () {
            addEventListener = dom.attachEvent("on"+type, fn)
        })()
    }
}

// 4. 函数科里化
// 预设情景: add方法,实现 add(1) add(2) add(3) add() : 调用add方法,当没有参数时,对前面添加的
var add = function (arr) {
    var result = 0
    for(let i=0;i<arr.length;i++){
        result += arr[i]
    }
    return result
}
var addEv = function (...args) {
    var array = args
    return (function(...arg){
        if(arg.length === 0){
            return add(array)
        }else{
            array = array.concat(arg)
        }
    })
}
var a = addEv(1,2)
a(3,4)
a(5)
a() //求和:15


// 闭包中的this指向  --注意下面代码需作为html的脚本文件运行,不要利用run code直接运行脚本文件
var name = "window"
var obj = {
    name: "object",
    getName: function(){
        return function(){
            return this.name
        }
    }
}
console.log(obj.getName()())
// 匿名函数的执行具有全局性,匿名函数中的this指向全局,输出为window
// 要使this指向外部函数环境,下面的写法
var obj2 = {
    name: "object2",
    getName: function(){
        let _this = this
        return function(){
            return _this.name
        }
    }
}
console.log(obj2.getName()()) // object2

// 闭包引起的内存泄漏
// js中的垃圾回收机制
// 1. 标记清除: 垃圾运行机制运行时,会给存储在内存中所有的变量都加上标记,然后会清除掉环境中变量的标记和被变量引用的变量的标记,此后如果变量再被标记,则表示该变量准备被删除
// 2. 引用计数: 跟踪记录每个值被引用的次数,声明一个变量被将一个引用类型的值赋给它,这个值的引用次数为1,当这个值被赋值给另外一个变量,引用次数加1,如果一个变量脱离这个值的引用,这个值的引用次数减1,当一个值的引用次数为0,就会等待垃圾回收器的回收。
// 引用计数策略来进行垃圾回收时:存在循环引用时,会造成内存泄漏(不再使用的内存不能被清楚,占用内存资源)
window.onload = function(){
    var element = document.getElementById("id")
    element.onclick = function(){
        alert(element.id)
    }
}
// 存在循环引用:匿名函数赋给element的onclick属性,匿名函数中又调用了element的id
window.onload = function(){
    var element = document.getElementById("id")
    var id = element.id // 解除循环引用
    element.onclick = function(){
        alert(id)
    }    
    element = null // 引用对象清除
}

// 定时器清除
(function(){
    var a = 0
    let timer = setTimeout(function(){
        console.log(a++)
    },1000)
})()
// 定时器内引用外部函数变量对象,循环引用
// clearTimeout(this.timer)
// this.timer = null

// 总结:
// 闭包优缺点:优: 私有化变量,避免全局环境污染;缺: 内存泄漏
// 面试题:
function fun(n,o){
    console.log(o)
    return {
        fun: function(m){
            return fun(m,n)
        }
    }
}
var a = fun(0) // undefined
a.fun(1) // 0
a.fun(2) // 0
a.fun(3) // 0
var b = fun(0).fun(1).fun(2).fun(3) // undefined 0 1 2
var c = fun(0).fun(1) // undefined 0
c.fun(2) // 1
c.fun(3) // 1

内容概要:本文深入探讨了多种高级格兰杰因果检验方法,包括非线性格兰杰因果检验、分位数格兰杰因果检验、混频格兰杰因果检验以及频域因果检验。每种方法都有其独特之处,适用于不同类型的时间序列数据。非线性格兰杰因果检验分为非参数方法、双变量和多元检验,能够在不假设数据分布的情况下处理复杂的关系。分位数格兰杰因果检验则关注不同分位数下的因果关系,尤其适合经济数据的研究。混频格兰杰因果检验解决了不同频率数据之间的因果关系分析问题,而频域因果检验则专注于不同频率成分下的因果关系。文中还提供了具体的Python和R代码示例,帮助读者理解和应用这些方法。 适合人群:从事时间序列分析、经济学、金融学等领域研究的专业人士,尤其是对非线性因果关系感兴趣的学者和技术人员。 使用场景及目标:①研究复杂非线性时间序列数据中的因果关系;②分析不同分位数下的经济变量因果关系;③处理不同频率数据的因果关系;④识别特定频率成分下的因果关系。通过这些方法,研究人员可以获得更全面、细致的因果关系洞察。 阅读建议:由于涉及较多数学公式和编程代码,建议读者具备一定的统计学和编程基础,特别是对时间序列分析有一定了解。同时,建议结合具体案例进行实践操作,以便更好地掌握这些方法的实际应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值