函数创建与定义的过程
不会销毁的函数执行空间
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