JS 中的深浅拷贝,闭包,沙箱模式,函数柯里化

156 阅读5分钟

深浅拷贝

深浅拷贝的含义:
    通常是指将一个 引用数据类型, 拷贝到另外一个变量中,但是根据拷贝方法的不同,展示出来的效果也有差异
浅拷贝:
    将一份数据拷贝到另一个变量中,修改 第一层 数据是 不会 相互影响,但是修改 第二层数据时 会 相互影响
    注意:将 浅拷贝 与 赋值 区分开
例:
    //赋值
    let obj = {
        name:'秦霄贤',
        age:'24',
        info:{
            width:120,
            height:180
        }
    }
    let obj2 = obj
    obj2.name = '彭于晏'
    obj2.age = 41
    console.log(obj)
    console.log(obj2) //因为是直接赋值,所以修改 obj2会影响 obj,所以它内部 age的值也被更改为 41
    
    //浅拷贝
    let obj = {
        name:'秦霄贤',
        age:'24',
        info:{
            width:120,
            height:180
        }
    }
    let newObj = {}
    for(let key in obj){
        newObj[key] = obj[key]
    }
    newObj.name = '彭于晏'
    newObj.age = 41
    console.log(obj)
    console.log(newObj)
深拷贝:
    将一份数据拷贝到另一个变量中,不管修改哪一层数据,两个对象之间都不会相互影响
    在日常工作中使用快捷语法: deepClone(target,origin)
例:
    let obj = {
        name:'秦霄贤',
        age:'24',
        info:{
            width:120,
            height:180
        },
        arr:[1, 2, 3, 4]
    }
    let newObj = {}
    function fn(newObj,obj){
        for(let key in obj){
            if(Object.prototype.toString.call(obj[key]) === 'Object Object'){
                newObj[key] === {}
                fn(newObj[key],obj[key])
            }else if(Object.prototype.toString.call(obj[key]) === 'Object Array'){
                newObj[key] === []
                fn(newObj[key],obj[key])
            }else{
                newObj[key] === obj[key]
            }
        }
    }
    const n1 = fn(newObj,obj)
    newObj.name = '彭于晏'
    newObj.age = 41
    newObj.info.width = 140
    newObj.info.height = 183
    console.log(obj)
    console.log(newObj)

永不销毁的执行空间

函数的定义与调用:
    1.定义
        => 在堆内存开一个空间
        => 将函数的函数体内的代码 保存在堆内存中
        => 将堆内存的地址保存在变量名中(函数名), 最后将存储到栈内存内
    2.调用
        => 根据变量名(函数名)中的地址,找到对应的函数
        => 然后再调用栈中开一个新的空间(函数的执行空间)
        => 在执行空间中,对函数的形参进行赋值
        => 在执行空间中,进行变量的预解析
        => 在执行空间中,执行函数代码
        => 销毁当前函数的执行空间
 永不销毁的执行空间:
     1.正常书写一个函数
     2.在这函数内 向外返回一个 引用数据类型
     3.当满足上述条件时,这个函数的执行空间将不会被销毁
     例:
         function fn () {
             let obj = {
                 name:'彭于晏',
                 age:41
             }
             return obj
         }
         const newObj = fn()
         console.log(newObj)
         // 可以利用 newObj = null 销毁空间
         

闭包

闭包是指有权访问外层函数作用域中变量的函数
创建闭包的最常见的方式就是在一个函数内创建一个内层函数,通过内层函数访问外层函数的局部变量
闭包的特性:
    => 函数内再嵌套函数
    => 内部函数可以 引用 外层函数的 参数 和 变量
    => 函数的执行空间不会被销毁
闭包的优点:
    可以避免全局变量的污染
闭包的缺点:
    闭包会常驻内存,会增大内存的使用量,使用不当很容易造成内存泄漏
闭包最大的用处:
    => 可以读取函数内部的变量
    => 函数内部的变量始终保持在内存中
 闭包使用时的注意点:
     由于闭包会使函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,可能导致内存泄漏
     解决方法: 在退出函数之前,将不适用的局部变量全部删除
 例:
     function fn(){
         let a = 20;
         let b = 50;
         let obj = {
             name:'彭于晏',
             age:41
         }
         function fn2 () {
             return obj
         }
         return fn2
     }
     const newFn = fn()
     comnsole.log(newFn)
     const f2 = newFn()
     console.log(f2)

沙箱模式

条件:
    => 利用了函数内 "间接" 的返回了一个函数
    => 外部函数 返回了一个对象, 这个对象内书写多个函数
例:
    function outer(){
        //1.放置一些外部函数的私有变量
        let a = 0;
        let b = 999;
        //2.对象内放置若干个 函数
        const obj = {
            getA(){
                return a
            },
            setA(val){
                a = val
            },
            getB(){
                return b
            },
            setB(val){
                b = val
            }
        }
        //3.讲对象返回函数中
        return obj
    }
    const res = outr()
    res.setA(23)
    console.log(res.getA())

沙箱模式语法糖

语法糖: 在不影响功能的情况下,给我们提供了一些更简单的方法(其实就是利用了 gettersetter 帮助我们简化了一些操作和代码)
例:
    function outer () {
        let a = 1;
        return {
            get a () {
                return a
            },
            set a (val) {
                a = val
            }
        }
    }
    const res = outer()
    console.log(res.a)  //1
    res.a = 999
    console.log(res.a)
    console.log(res.a + 1)

函数的柯里化

柯里化函数:
    一个函数接收多个参数能够完成功能,柯里化函数就是将这一个函数拆分为两个函数,每个函数都只接收一个参数,然后完成的功能相同
实例: //需求:使用正则完成一个文本匹配
    
    function fn (fnReg) {  //外层函数,负责接收正则
        return function (fnStr) {   //内层函数,负责接收字符串,并完成正则校验
            return fnReg.test(fnStr)  //将校验结果返回出去给外部使用
        }
    }
    const num = fn(/^\d\w{6,12}$/)
    console.log(num('12sad56fds'))

函数的柯里化封装

函数柯里化内
    外层函数: 收集参数
    内层函数: 负责处理功能
curry 函数需要接收两个参数
    callback: 当参数传递足够时需要执行的函数
    ...arg: 接收后续这个参数传递所有实参(以数组的形式存储)
例:
    功能:拼接地址栏字符串
    地址栏:
        传输协议:http  https
        域名: 127.0.0.1  localhost
        端口名: 0~65535  7777  8080  8081
        地址:/a   /a/b.html  /index.html
    
    function fn(a, b, c, d){
        return a + '://' + b + ':' + c + d
    }
    function curry(callback, ...arg) {
        return function(...arg){
            _arg = [...arg, ..._arg]
            if(_arg.length === callback.length){
                return callback(..._arg)
            }else{
                return curry(callback, ..._arg)
            }
        }
    }
    const newFn = curry(fn)
    const newFn2 = newFn('https')
    const newFn3 = newFn2('127.0.0.1')
    const newFn4 = newFn3('7777')
    const newFn5 = newFn4('/index.html')
    console.log(newFn5)