闭包
切身感受表达式
●两个表达式分成表达式的表述和表达式的继续执行
表达式的表述
●他们手写两个表达式, 但不能继续执行表达式胃部的标识符
●那表述两个表达式做了甚么事
○在堆缓存中开拓几段储存容量
○把你手写在表达式胃部的标识符, 全数以数组的方式储存在这储存容量中, 这时不能导出表达式
○把表达式储存容量门牌号, 表达式给cp(表达式名)
表达式的继续执行
●采用(调用)表达式, 会把表达式胃部的标识符继续执行
●调用表达式又具体内容做了甚么事
○依照cp储存的地址, 找出表达式储存容量
○间接开拓两个捷伊表达式继续执行外部空间
○在继续执行外部空间内展开实参表达式
○在继续执行外部空间内展开表达式外部的预导出
○把以后储存的标识符在继续执行外部空间内完备继续执行两遍, 这时才会导出表达式
○继续执行完那个开拓出来的执行外部空间封存 let num = 100 function fn(a, b) { // 证明确实没有导出 num 储存 // 如果这里在储存的时候, 是导出了 num 储存的, 那么存起来的就是 100 // 后面的 num = 200 这句标识符不能影响到这里的值 // 将来打印出来就是 100 // 如果这里在储存的时候, 没有导出 num 储存, 那么存起来的就是 num 表达式 // 后面的 num = 200 这句标识符会影响到这里的值 // 将来打印出来就是 200 // console.log(num) const res = a + b console.log(res) } num = 200 fn(10, 20) fn(100, 200)表达式实参表达式和与导出先后问题
function fn(a) { console.log(a) function a() { console.log(111) } } fn(100)分析
●当表达式调用的时候, 如果先展开的 实参表达式
○1、给 a 表达式表达式为 100
○2、展开表达式内与导出的时候, 给 a 表达式表达式为两个 表达式体
■这时给 a 表达式表达式的表达式体就会把 100 覆盖
○3、继续执行的第一行标识符, 打印 a 表达式
■在这里就会打印出 表达式体
●当表达式调用的时候, 如果先展开的 预导出
○1、展开表达式内预导出的时候, 给 a 表达式表达式为两个 表达式体
○2、展开实参表达式的时候, 给 a 表达式表达式为 100
■这时给 a 表达式表达式的 100 就会把表达式体覆盖
○3、继续执行的第一行标识符, 打印 a 表达式
■在这里就会打印出 100
●结论
○如果控制台打印出 100, 说明 先 预导出 后 实参表达式
○如果控制台打印出 表达式体, 说明 先 实参表达式 后 预导出
不能封存的表达式继续执行外部空间
●当你的表达式内返回两个 复杂数据类型
●并且在表达式外面有表达式接受那个复杂数据类型的时候
●这时表达式的继续执行外部空间不能封存
不能封存的表达式继续执行外部空间有甚么作用
●延长了表达式的生命周期
如何能让那个外部空间封存
●让你的外部表达式指向别的位置, 只要不在保存那个外部门牌号
●那个继续执行外部空间就会被 浏览器 封存掉 function fn() {const num = 100 const obj = { name: Jack } return obj } let res = fn() const res2 = fn() // 封存了继续执行外部空间 res = 200闭包
表述
●官方表述:就是能够读取其他表达式外部表达式的表达式
●个人理解:
○需要两个不能封存的表达式继续执行外部空间
○表达式内 间接 或者 间接 的返回两个表达式
○外部表达式采用着外部表达式的私有表达式
○他们称外部表达式是外部表达式的闭包表达式
作用(优点)
●延长表达式的生命周期
●在表达式外部操作表达式外部的私有表达式
1.间接返回两个表达式function outer() { var num = 100 function inner() { returnnum }return inner } const res = outer() // 当你需要在 outer 外面访问 outer 内的私有表达式 num 的时候 // 没有办法间接访问 // 但你可以通过调用 res 来访问 // 因为 res 储存的就是 inner 表达式门牌号 // 所以这里就是在调用 outer 内的 inner 表达式 // 又因为 inner 内没有私有表达式 num, 会自动去到 父级作用域查找 // 也就是去到 outer 内查找私有表达式 num , 发现有 // 索引返回的就是 outer 表达式内的 私有表达式 num // n 得到的就是 outer 内私有表达式 num 的值 const n = res()2.间接返回两个表达式
●不间接返回表达式, 把表达式放在两个容器内返回function outer() { var num = 100 var str = hello world // 如果我向对象内放置两个表达式, 那么返回的虽然是对象, 但对象内有表达式 var obj = { getNum: function () { return num }, setNum: function (val) { // 给 num 表达式进行表达式 // 自己作用域内有, 给自己的表达式, 自己没有给父级的表达式 // 因为 setNum 内没有 num 表达式 // 所以, 这里的表达式实际上是给 outer 表达式内的私有表达式 num 表达式num = val } }return obj } // res 接受的就是 outer 表达式内返回的 obj 对象的门牌号 // res 和 outer 表达式内的 obj 指向两个对象储存容量 var res = outer() // 当你调用 res.getNum() 的时候, 就是在调用 outer 表达式内的 obj 里面的 getNum 表达式 // 因为 getNum 表达式是 outer 的子级作用域 // 所以 getNum 内没有 num 的时候, 访问的还是 outer 表达式内的私有表达式 num var n = res.getNum() console.log(n) // 因为 res 就是 outer 表达式内的 obj 对象 // 所以, res.setNum 就是 obj 内的 setNum 表达式 // 调用了 obj 内的 setNum 表达式, 并且给 val 表达式为了 500 // 因为 setNum 表达式内是在给 outer 表达式的私有表达式 num 表达式 // 所以这里的 500 被表达式给了 outer 表达式的私有表达式 num // 在 outer 表达式外面给 outer 表达式外部的 私有表达式 num 的值修改了 res.setNum(500) console.log(res.getNum())沙箱模式
●沙箱:
○就是两个箱子里面装的都是沙子
○筛子会一点儿一点儿的漏出来
○不能一下子都漏出来
●利用 表达式内 间接返回两个表达式
○间接就是不间接
○把表达式包裹在另两个数据结构中返回
○一般是 两个表达式内返回两个对象 , 对象中手写多个表达式
○意义就是为了返回多个表达式function outer() {let a = 100 let b = 200 let str = hello world // 创建两个对象 const obj = { getA: function () { return a }, getB () { return b }, setA: function (val) { a = val } } return obj } // 目的是为了得到 “箱子” const res = outer() // 利用 “箱子” 去访问 outer 表达式内的指定数据 // 需要访问 a console.log(res.getA()) // 100 console.log(res.getB())// 200 // 需要修改 a res.setA(1000) console.log(res.getA()) // 1000 // 重新做了两个 “箱子” const res2 = outer() console.log(res2.getA()) // 100沙箱模式语法糖
●尽可能的简化沙箱模式的语法
●利用的是 getter 和 setter 来展开操作数据
●所有的框架的底层采用的大部分是这样的操作<script> /* 沙箱语法糖 + 尽可能的简化沙箱模式的语法 + 利用的是 getter 和 setter 来展开操作数据 */ function outer() { let a = 100 let b = 200 constobj = { get a () { return a }, get b () { return b }, // setA 目的是为了设置 a 成员, 采用 setter 设置器 set a (val) { a = val }, set b (val) { b = val } } return obj } // res 得到的是两个 对象数据类型 const res = outer() // 因为闭包的方式, 导致他们的所有操作 // 对象.方法名() console.log(res)// console.log(res.a) // 在对象内, 间接给设置器名称表达式即可 // res.a = 1000 // console.log(res.a) </script>案例-循环绑定事件
<!DOCTYPEhtml> <html lang=“en”> <head> <meta charset=“UTF-8”> <meta http-equiv=“X-UA-Compatible” content=“IE=edge”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <title>Document</title> </head> <body> <button>1</button><button>2</button><button>3</button><button>4</button><button>5</button> <script> // 在没有let出现以后 const btns = document.querySelectorAll(button) for (var i = 0; i < btns.length; i++) {// 给每两个按钮添加两个点击事件 // 采用的是两个自继续执行表达式 btns[i].onclick = (function (index) { // 返回两个表达式 return function () { // 采用传递进来的 i console.log(index) } })(i) // i当做实参传递到表达式里面 } </script> </body> </html>表达式柯理化
●最简单的柯理化表达式就是把
●一次传递两个参数 , 变成两次每次传递两个参数
●利用了闭包 , 把第一次传递的参数保存下来(延长表达式的生命周期)// 需求: 求 10 + x 的和 // 以后的方式 function sum(x) { return 10 + x } // 采用 const res = sum(10) console.log(res); // 需求: 求 20 + x 的和 function sum1(x) { return 20 + x } // 采用 const res1 = sum1(10) console.log(res1);●柯理化表达式实现
// 他们利用闭包(柯理化)实现 function curry(init) { return function (num) { return num + init } } // 采用 // 求 10 + x 的和 // const sum10 = curry(10); // console.log(sum10(20)); // 标识符优化 console.log(curry(10)(20)); // 求 20 + x 的和 // const sum20 = curry(20) // console.log(sum20(20)); // 标识符优化 console.log(curry(20)(20));表达式柯理化封装
●通过他们上面采用的柯理化
●他们发现柯理化有两个功能
○两个是用来保存表达式(收集表达式)
○两个是用来实现逻辑(功能表达式)
●他们柯理化表达式的封装有可能是多个参数在相互利用
柯理化表达式封装
●把两个功能表达式依照 柯理化的方式 封装起来
●让那个功能表达式变成一种 柯理化的 调用方式// 准备功能表达式 function fn(a, b, c, d) { return a + :// + b + : + c + d } // 准备柯理化表达式 function currying(fn, …arg) { // …arg 把除了功能表达式以外的参数全数收集了 // console.log(功能表达式 : , fn) // console.log(收集的基础参数 : , arg) // 保存外部 arg 表达式 let _arg = arg || [] // 记录: 功能表达式需要几个参数 // 语法: 表达式名.length // 得到: 该表达式的 实参的个数 constlen = fn.length// 返回两个外部表达式 return function (…arg) { // 那个表达式将来被调用的时候, 依旧需要接受参数 // 把本次调用的时候接受的实参, 和以后准备好的拼接在一起_arg = [ …_arg, …arg ]// 目前 _arg 就是基础准备的参数 和 本次调用时候传递的参数 放在一起的 数组 if (_arg.length < len) { // 再次调用 currying 函数, 继续去收集参数 return currying(fn, …_arg) } else { // 继续执行功能表达式 return fn(…_arg) } } } // 将来采用的时候 // const r1 = currying(fn, http, localhost, 8080) // const r2 = currying(fn, http, localhost) // const r3 = currying(fn, http) // const r4 = currying(fn) // 提前准备三个参数 // const r1 = currying(fn, http, localhost, 8080) // const r2 = r1() // const r3 = r2() // const r4 = r3(/a) // console.log(r4) // 提前准备两个参数 // const r1 = currying(fn, https, localhost) // const r2 = r1() // const r3 = r2(9999) // const r4 = r3(/b/c/d) // console.log(r4) // 提前准备两个参数 // const r1 = currying(fn, https, localhost) // const r2 = r1() // const r3 = r2(8080, /a/b/c) // console.log(r3) // 提前准备两个参数 // const r1 = currying(fn, https) // console.log(r1(baidu.com, 8080, /a)) // console.log(r1(baidu.com)(8080)(/a))