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

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

首页模拟经营简单沙箱2更新时间:2024-04-17

函数创建与定义的过程

不会销毁的函数执行空间

  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() } 沙箱语法糖

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) } } } 数据劫持(代理)

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

<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 } 数据劫持升级

自己劫持自己

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)

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);

作者:摸鱼emoji
链接:https://juejin.cn/post/7293341446924468243

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved