记录--闭包,沙箱,防抖节流,函数柯里化,数据劫持......

这篇具有很好参考价值的文章主要介绍了记录--闭包,沙箱,防抖节流,函数柯里化,数据劫持......。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--闭包,沙箱,防抖节流,函数柯里化,数据劫持......

函数创建与定义的过程

  • 函数定义阶段
    • 在堆内存中开辟一段空间
    • 把函数体内的代码一模一样的存储在这段空间内
    • 把空间赋值给栈内存的变量中
  • 函数调用阶段
    • 按照变量名内的存储地址找到堆内存中对应的存储空间
    • 在调用栈中开辟一个新的函数执行空间
    • 在执行空间中进行形参赋值
    • 在执行空间中进行预解析
    • 在执行空间中完整执行一遍函数内的代码
    • 销毁在调用栈创建的执行空间

不会销毁的函数执行空间

  1. 当函数内返回一个引用数据类型
  2. 并且函数外部有变量接收这个引用数据类型
  3. 函数执行完毕的执行空间不会销毁
  4. 如果后续不需要这个空间了,只要让变量指向别的位置即可
function fn() {
    const obj = {
        a: 1,
        b: 2
    }
    return obj
}

const res = fn()
console.log(res)

// 如果后续不需要这个空间了, 只需要让 res 指向别的位置即可
res = 100

闭包

  • 需要一个不会被销毁的执行空间
  • 需要直接或间接返回一个函数
  • 内部函数使用外部函数的私有变量
  • 概念 : 函数里的函数
  • 优点:
    • 可以在函数外访问函数内的变量
    • 延长了变量的生命周期
  • 缺点:
    • 闭包函数不会销毁空间,大量使用会造成内存溢出
function outer () {
    let a = 100
    let b = 200

    // 我们说 inner 是 outer 的闭包函数
    function inner () {
        /**
         * 我使用了一个 a 变量, 但是 inner 自己没有
         * 所以我用的是 外部函数 outer 内部的变量 a
        */
        // console.log(a)
        return a
    }
    return inner
}

// 我们说 res 是 outer 的闭包函数
let res = outer()

let outerA = res()
console.log(outerA)

沙箱模式

  • 利用了函数内间接返回了一个函数
  • 外部函数返回一个对象,对象内书写多个函数
function outer () {
    let a = 100
    let b = 200

    // 创建一个 沙箱, "间接的返回一个函数"
    const obj = {
        getA: function () {
            return a
        },
        getB: function () {
            return b
        },
        setA: function (val) {
            a = val
        }
    }
    return obj
}

// 得到一个沙箱
const res1 = outer()

console.log(res1.getA())    // 100
console.log(res1.getB())    // 200

res1.setA(999)
console.log(res1.getA())    // 999

// 重新得到一个沙箱
const res2 = outer()
console.log(res2.getA())    // 100

沙箱小案例

<button class="sub">-</button>
<input class="inp" type="text" value="1">
<button class="add">+</button>
<br>
<button class="sub1">-</button>
<input class="inp1" type="text" value="1">
<button class="add1">+</button>
// 准备一个沙箱
function outer() {
    let a = 1
    return {
        getA() {
            return a
        },
        setA(val) {
            a = val
        }
    }
}

// 0. 获取元素
const subBtn = document.querySelector('.sub')
const addBtn = document.querySelector('.add')
const inp = document.querySelector('.inp')

// 0. 准备变量
// let count = 1
let res = outer()
subBtn.onclick = function () {
    let count = res.getA()
    res.setA(count - 1)
    inp.value = res.getA()
}
addBtn.onclick = function () {
    // count++
    let count = res.getA()
    res.setA(count + 1)
    inp.value = res.getA()
}

// 0. 获取元素
const subBtn1 = document.querySelector('.sub1')
const addBtn1 = document.querySelector('.add1')
const inp1 = document.querySelector('.inp1')

// 0. 准备变量
let res1 = outer()
subBtn1.onclick = function () {
    let count = res1.getA()
    res1.setA(count - 1)
    inp1.value = res1.getA()
}
addBtn1.onclick = function () {
    let count = res1.getA()
    res1.setA(count + 1)
    inp1.value = res1.getA()
}

沙箱语法糖

  • 尽可能的简化沙箱模式的语法
  • 利用 get 和 set 进行操作数据
  • 语法糖:
    • 在不影响功能的情况下提供一点更适合操作的语法
function outer() {
    let a = 100
    let b = 200

    return {
        get a() { return a },
        get b() { return b },
        set a(val) { a = val }
    }
}

let res = outer()
console.log(res.a)
console.log(res.b)
res.a = 999
console.log(res.a)   // 999 

闭包面试题!!!!

function fun(n, o) {
    console.log(o)

    const obj = {
        fun: function (m) {
            return fun(m, n)
        }
    }

    return obj
}

var a = fun(0)    // undefined
a.fun(1)    // 0
a.fun(2)    // 0
a.fun(3)    // 0

/**
 *  var a = fun(0)
 *  a.fun(1)
 *  a.fun(2)
 *  a.fun(3)
 *
 *  1. var a = fun(0)
 *          调用 fun(QF001) 函数(QF001) 传递一个 参数 0
 *              全局函数 fun (QF001) 的 形参 n == 0     形参 o == undefined
 *          调用 fun 函数后, 会返回一个对象 存储在 变量 a 中, 这个对象内部有一个属性叫做 fun, 属性值为 一个函数(QF002),
 *              所以我们可以通过 a.fun()   去调用这个函数
 *
 *  2. a.fun(1)
 *      2.1 调用这个函数 会 return 一个函数 fun (为全局函数 QF001) 的调用结果,   
 *      2.2 调用全局函数 fun(m, n)       m 此时 传递的是 1, n 传递的是 0
 *      2.3 执行全局函数 fun(m, n) 内部会输出第二个形参
 *
 *  3. a.fun(2)
 *      2.1 调用这个函数 会 return 一个函数 fun(为全局函数 QF001) 的调用结果
 *      2.2 调用全局函数 fun(m, n)  m 此时传递的是 2, n 传递的是 0
 *      2.3 执行全局函数 fun(m, n) 内部会输出第二个形参
 *
*/

防抖与节流

防抖

  • 解释:在短时间内触发一件事,每次都用上一次的时间替代,也就是只执行最后一次
        box.oninput = ((timerID) => {
            return function (e) {
                clearInterval(timerID)
                timerID = setTimeout(() => {
                    console.log('搜索了: ', e.target.value)
                }, 300)
            }
        })(0)

节流

  • 解释:短时间内快速触发一件事,当一个事件处理函数开始执行的时候,不允许重复执行(瀑布流)
        box.oninput = ((flag) => {
            return function (e) {
                if (!flag) return
                flag = false
                setTimeout(() => {
                    console.log('搜索了: ', e.target.value)
                    flag = true
                }, 300)
            }
        })(true)

柯里化函数

  • 定义:本质上还是一个函数,只不过将原本接收多个参数才能正常执行的函数拆分成多个只接收一个的函数
// 原本函数
        function reg (reg, str) {
            return reg.test(str)
        }
        // 柯里化后
        function reg(reg) {
            return (str) => {
                return reg.test(str)
            }
        }
        const res = reg(/^\w{3,5}$/)
        console.log(res('123asd'));   // false
        console.log(res('123'));      // true

封装柯里化函数案例

        /**
         *  函数柯里化封装
         * 
         *  fn 函数能够帮助我们拼接一个 完整的网络地址
         *      a --- 传输协议: http      https
         *      b --- 域名:   localhost   127.0.0.1
         *      c --- 端口号: 0-65535
         *      d --- 地址:   /index.html     /a/b/c/index.html
         *
         *
         *  现在只有我们正确的传递了参数的数量才能够实现最好的拼接, 如果传递的参数数量不够也会运行函数, 但是字符串不太对
         *
         *  需求:
         *      将当前函数处理成柯里化函数, 只有传递的参数数量足够的时候, 在执行函数内容
        */
        // 功能函数
        function fn(a, b, c, d) {
            return a + '://' + b + ':' + c + d
        }
        // 通过柯里化解决
        function keli (callBack, ...args) {
            return function (..._args) {
                _args = [...args, ..._args]
                if (_args.length >= callBack.length) {
                    return callBack(..._args)
                } else {
                    return keli(callBack, ..._args)
                }
            }
         }

数据劫持(代理)

将来在框架中我们通常都是 数据驱动视图 也就是说: 修改完毕数据, 视图自动更新

  • 数据劫持:以原始数据为基础,对数据进行一份复制
  • 复制出来的数据是不允许被修改的,值是从原始数据里面获取的
  • 语法:Object.defineproperty(哪一个对象,属性名,{配置项})
  • 配置项:
    • value:该属性对应值
    • writable:该属性确定是否允许被重写,默认值是false
    • emunerable:该属性是否可被枚举(遍历), 默认是 false
    • get:是一个函数,叫做getter获取器,可以用来决定改属性的属性值
      • get属性的返回值就是当前属性的属性值
    • set:是一个函数,叫做setter设置器,当修改属性值的时候会触发函数
    • set和get不能和其他三个属性一起用
    <h1>姓名: <span class="name">默认值</span> </h1>
    <h1>年龄: <span class="age">默认值</span> </h1>
    <h1>性别: <span class="sex">默认值</span> </h1>
    请输入年龄:<input type="text" name="" id="name">
    <br>
    请输入年龄:<input type="text" name="" id="age">
    <br>
    请输入性别:<input type="text" name="" id="sex">


        const nameEl = document.querySelector('.name')
        const ageEl = document.querySelector('.age')
        const sexEl = document.querySelector('.sex')
        const inp1 = document.querySelector('#name')
        const inp2 = document.querySelector('#age')
        const inp3 = document.querySelector('#sex')
        const obj = {
            name:'张三',
            age:18,
            sex:'男'
        }
        function bindHtml(res) {
            nameEl.innerHTML = res.name
            ageEl.innerHTML = res.age
            sexEl.innerHTML = res.sex
        }
        const app =  observer(obj, bindHtml)

        inp1.oninput = function () {
            app.name = this.value
        }
        inp2.oninput = function () {
            app.age = this.value
        }
        inp3.oninput = function () {
            app.sex = this.value
        }

    function observer (origin, callBack) {
    const target = {}
    // 数据劫持
    for (let key in origin) {
        Object.defineProperty(target, key, {
            get () {
                return origin[key]
            },
            set (val) {
                origin[key] = val
                callBack(target)
            }
        })
    }
    // 首次调用 
    callBack(target)
    return target
}

数据劫持 + 渲染 (vue功能实现)

<div id="app">
        <p>姓名:{{name}}</p>
        <p>年龄:{{age}}</p>
        <p>性别:{{sex}}</p>
        <p>用户:{{id}}</p>
    </div>

    用户id:<input type="text" name="" id="">

    <script src="js/vue.js"></script>

    <script>
        const app = createApp({
            el:'#app',
            data:{
                name:'张三',
                age:20,
                sex:'男',
                id:'0001'
            }
        })
        const inp = document.querySelector('input')
        inp.oninput = function () {
            app.id = this.value
        }

    </script>

function createApp(options) {
    // 安全判断(传参判断)
    // 1.1 el
    if (options.el === undefined) {
        throw new Error('el选项必须传递')
    }
    // 1.2 data
    if (Object.prototype.toString.call(options.data) !== '[object Object]') {
        throw new Error('data 属性必须是个对象')
    }
    // 1.3 el 不能为空
    const root = document.querySelector(options.el)
    if (root === null) throw new Error('el 必须传入,且root为有效元素')
    // 2 数据劫持
    const target = {}
    for (let key in options.data) {
        Object.defineProperty(target, key, {
            get() {
                return options.data[key]
            },
            set(val) {
                options.data[key] = val
                // 每次修改数据调用渲染函数
                bindHtml(root, target, rootStr)
            }
        })
    }
    // 拿到根元素下面的结构(字符串形式)
    const rootStr = root.innerHTML
    // 首次调用
    bindHtml(root, target, rootStr)
    return target
}
function bindHtml(root, _data, _str) {
    // 定义一个正则拿到{{......}}
    const reg = /{{ *(\w+) *}}/g
    const arr = _str.match(reg)
    arr.forEach(item => {
        const key = /{{ *(\w+) *}}/.exec(item)[1]
        _str = _str.replace(item, _data[key]) 
    });
    root.innerHTML = _str
}

数据劫持升级

自己劫持自己

  • 语法:Object.defineProperties(想要劫持得对象,配置项)
        const obj = {
            username:'admin',
            password:'123456',
            id:'0001'
        }
        for (let key in obj) {
            Object.defineProperties(obj, {
                // 对象里面默认把key当作字符串,通过[]语法实现将其当作变量
                ['_' + key]:{
                    value:obj[key],
                    writable:true
                },
                [key]:{
                    get () {
                        // 如果returnobj[key],每次return都会访问,
                        // 然后触发get方法会形成死循环
                        return obj['_' + key]
                    },
                    set (val) {
                        obj['_' + key] = val
                    }
                }
            })
        }

数据代理(ES6)

  • 通过内置构造函数代理
  • 语法:new Proxy(想要代理得对象,)
  • 数据代理完成后,在向对象中添加属性,也可以自动完成代理
        const obj = {
            name:'zhangsan',
            age:18
        }
        const res = new Proxy(obj, {
            get (target, property) {
                return target[property]
            },
            set (target, property, val) {
                target[property] = val
            }
        })
        res.age = 20
        console.log(res.age);
        console.log(res.name);
        // 数据代理后添加的数据也可以被代理
        res.abc = 123
        console.log(res);

本文转载于:

https://juejin.cn/post/7293341446924468243

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--闭包,沙箱,防抖节流,函数柯里化,数据劫持......文章来源地址https://www.toymoban.com/news/detail-747483.html

到了这里,关于记录--闭包,沙箱,防抖节流,函数柯里化,数据劫持......的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序全局封装防抖节流函数

    什么是防抖节流? 防抖:  简单来说就是指触发事件后在  n  秒内函数只能执行一次(输入框搜索自动补全事件、频繁点赞和取消点赞、提交事件等等) 节流:  简单来说就是指连续触发事件但是在  n  秒中只执行一次函数(发送验证码、表单验证、鼠标移动事件等等)

    2024年01月25日
    浏览(43)
  • JS手写防抖和节流函数(超详细版整理)

    防抖(debounce) :每次触发定时器后,取消上一个定时器,然后重新触发定时器。防抖 一般用于用户未知行为的优化 ,比如搜索框输入弹窗提示,因为用户接下来要输入的内容都是未知的,所以每次用户输入就弹窗是没有意义的,需要等到用户输入完毕后再进行弹窗提示。

    2024年02月04日
    浏览(36)
  • vue项目使用lodash节流防抖函数问题与解决

    在lodash函数工具库中,防抖 _.debounce 和节流 _.throttle 函数在一些频繁触发的事件中比较常用。 创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟  wait  毫秒后调用  func  方法。 参数 func   (Function) : 要防抖动的函数。 [wait=0]   (number) : 需要延迟的毫秒数。

    2024年02月08日
    浏览(40)
  • 前端解决按钮重复提交数据问题(节流和防抖)

    🍿*★,°*:.☆( ̄▽ ̄)/$:*.°★* 🍿 🍟欢迎来到前端初见的博文,本文主要讲解在工作解决按钮重复提交数据问题(节流和防抖) 👨‍🔧 个人主页 : 前端初见 🥞喜欢的朋友可以关注一下,下次更新不迷路🥞 当我们在页面进行点点点的操作时,很可能遇到点击两次的行为,

    2024年02月09日
    浏览(70)
  • Vue 3 中的极致防抖/节流(含常见方式防抖/节流)

    各位朋友你们好呀。今天是立春,明天就是正月十五元宵节了,这种立春 + 元宵相隔的时候,可是很难遇到的,百年中就只有几次。在这提前祝大家元宵快乐。 今天给大家带来的是 Vue 3 中的极致防抖/节流(含常见方式防抖/节流) 这篇文章,文章中不仅会讲述原来使用的防

    2024年02月01日
    浏览(43)
  • javascript常见100问|前端基础知识|问ajax-fetch-axios-区别请用 XMLHttpRequestfetch 实现 ajax节流和防抖px em rem vw/箭头函数的缺点

    HTML CSS JS HTTP 等基础知识是前端面试的第一步,基础知识不过关将直接被拒。本章将通过多个面试题,讲解前端常考的基础知识面试题,同时复习一些重要的知识点。 扎实的前端基础知识,是作为前端工程师的根本。基础知识能保证最基本的使用,即招聘进来能干活,能产出

    2024年04月27日
    浏览(49)
  • 性能优化---节流与防抖

    1、什么是节流和防抖         防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。         节流是指规定一个单位时间,在这个单位时间内,只

    2024年04月17日
    浏览(37)
  • 前端优化 ----防抖 节流

    如果一个事件在短时间内连续触发,则只去执行最后一次。 控制频率 实现方式:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法 缺点:如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟 使一个函数在固定时间内只执行一次。控制

    2024年02月04日
    浏览(39)
  • 前端刷题-防抖节流

    在实际的开发过程中,一般会使用lodash自有的debounce函数和throttle函数对所要防抖/节流的函数进行包裹。例如

    2024年02月10日
    浏览(42)
  • 节流与防抖

    本文可以配合本人录制的视频一起食用 节流和防抖是前端开发中常用的优化技术,主要用于优化一些高频触发的事件。 节流与防抖, 先从字面上理解一下 ,节流就是节制流入或流出,在前端方面我个人理解一下,指的是节制功能或请求的触发次数,所以节流函数字面上的意

    2024年02月14日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包