函数式编程 && 命令式编程

函数式编程

把具体的操作过程“封装”到一个函数中,我们无需关注内部是如何处理的(How),只需要关注处理的结果(What)即可;

  • 使用便捷,开发效率高
  • 减少页面冗余代码「低耦合高内聚」

    命令式编程

    具体如何去处理,是由自己实现及掌控的,关注How的过程!

  • 操作灵活,可以自主把控处理的每一个步骤

  • 处理性能一般比函数式编程式要好「例如:forEach循环要慢于for循环」
    • 总结:处理的数据量“较多”的情况下,使用命令式编程来提高性能!操作逻辑较为复杂,需要自己灵活把控处理步骤的情况下,也使用命令式编程!其余情况,优先推荐函数式编程!

匿名函数具名化

含义:给匿名函数强制设置一个名字 特点:原本应该是匿名函数「例如:自执行函数、函数表达式、回调函数等」,但是我们会为其设置一个名字

  • 更符合规范
  • 有助于实现递归
  • 举例

    1. let arr = [10, 20, 30, 40, 50]
    2. //函数式编程
    3. arr.forEach((item, index) => {
    4. // 本质循环五次
    5. if(index%2===0){
    6. console.log(item, index)
    7. }
    8. })
    9. // 命令式
    10. for (let i = 0; i < arr.length; i += 2) {
    11. // 本质循环三次
    12. console.log(arr[i], i)
    13. }
    14. // 需求:迭代五次
    15. // for (let i = 0; i < 5; i++) { } 命令式编程
    16. new Array(5).fill(null).forEach(() => {//函数式编程
    17. // ...
    18. })

    匿名函数

    没有名字(或没必要设置名字)的函数,以下:

  1. 函数表达式:
  2. const xxx = function(){}
  3. 回调函数(或自执行函数):
  4. fn(()=>{ ... })
  5. (function(){ ... })()
  6. .....
  1. (function fn() {
  2. // 在函数产生的私有上下文中,可以使用fn:存储的是当前函数本身
  3. // fn = 100 //且直接修改其值,是不会生效的
  4. // let fn = 100 //但是可以基于其它方式声明一下这个名字,那么此时名字就不再代表函数本身了
  5. console.log('我是自执行函数', fn)
  6. })()
  7. // console.log(fn) //Uncaught ReferenceError: fn is not defined 设置的名字不会被其外部上下文(宿主环境)所声明,所以在外面无法使用!
  1. "use strict"//使用严格模式
  2. let i = 0
  3. ~function fn() {
  4. if (i >= 2) return
  5. i++
  6. console.log(i)
  7. // console.log(arguments.callee) //函数本身 BUG:在严格模式下,会报错 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
  8. fn()
  9. }()

堆栈内存

![7}G1{9C9RW(L5HR2M~IO%E_tmb.jpg :::info

  • 浏览器:执行 html/css/js 代码
  • html/css -> W3C GUI线程
  • js -> ECMA-262 JS引擎线程「谷歌的引擎 V8」
  • 浏览器内核
    • 谷歌:blink「webkit分支」
    • Safari:webkit
    • 火狐:Gecko
    • IE:Trident
    • Edge:Chromeium
    • 欧朋:之前是 Presto,从14版本后是 webkit
    • 国产浏览器:之前是 Trident ,后来是 webkit
    • 移动端浏览器:目前都是 webkit
  • webkit内核的垃圾回收机制:自动回收,浏览器会定期查找没有被使用的内存,把其释放掉!!
    IE浏览器用的是引用计数法
    :::

    闭包8_NIPBE[T@T4G])YWZ32M3V.jpg

    1. let x = 5;
    2. const fn = function fn() {
    3. return function (y) {
    4. console.log(y + (++x));
    5. }
    6. };
    7. let f = fn(6);
    8. f(7);
    9. fn(8)(9);
    10. f(10);
    11. console.log(x);

    练习题1
    1. let a = 0,
    2. b = 0;
    3. function A(a) {
    4. A = function (b) {
    5. alert(a + b++);
    6. };
    7. alert(a++);
    8. }
    9. A(1);
    10. A(2)

    O_[95DV_OZ@D)4E5~`]KKSF.jpg

    练习题2

    下⾯代码是否可以,每隔1000MS依次输出 0 1 2 3 4 ?如果不可以,说明为啥?以及如何解决? ```javascript for (var i = 0; i < 5; i++) {

    setTimeout(function () {

    1. console.log(i);

    }, (i + 1) * 1000);

}

  1. 解决方案1:把 var 改为 let
  2. ```javascript
  3. for (let i = 0; i < 5; i++) {
  4. setTimeout(function () {
  5. console.log(i);
  6. }, (i + 1) * 1000);
  7. }
  • 使用 let 会产生块级私有上下文,而在每一轮产生的 私有的块级上下文 中,会创建一个小函数「callback」,其作用域就是本轮产生的私有上下文,把 callback 赋值给了全局的定时器,所以此 块级上私有下文 不会被释放「也就是闭包」
  • 迭代5轮,产生5个闭包,每个闭包中都有私有变量i,分别存储0~4的值

    第一轮/闭包1:i = 0
    第二轮/闭包2:i = 1

  • 当定时器到时候,执行 callback 函数的时候,输出的 i 不是 callback 中私有的,则按照作用域链,找到对应的闭包,使用其存储下来的私有 i 的值
    => 这样就实现了:每间隔1秒,分别输出0~4

})7R4OPZHKYJP9_Q)R~}0UD.jpg`J8A(`)9RW4BZ5S2ACYWB~R.jpgH6]S{$K~K0M1C2(NXZ61CGR.jpg
解决方案2:不基于 let 产生闭包,而是自己把函数执行,让其产生闭包

  1. for (var i = 0; i < 5; i++) {
  2. (function (i) {
  3. setTimeout(function () {
  4. console.log(i)
  5. }, (i + 1) * 1000)
  6. })(i);
  7. }

解决方案3:基于定时器内部的闭包处理机制

  1. for (var i = 0; i < 5; i++) {
  2. setTimeout(
  3. function (i) {
  4. console.log(i)
  5. },
  6. (i + 1) * 1000,
  7. // 定时器一但设置第三个及以后的实参,其内部就会产生一个闭包,先把传递的实参值保存下来
  8. // 当定时器到达时间,执行 callback 函数的时候,会把之前闭包中存储的那些实参值,传递给 callback
  9. i
  10. )
  11. }

回忆选项卡
  1. <div id="box">
  2. <button index="0">按钮1</button>
  3. <button index="1">按钮2</button>
  4. <button index="2">按钮3</button>
  5. </div>
  6. <script>
  7. let buttons = Array.from(document.querySelectorAll('#box button'))
  8. </script>

方案1:基于闭包解决「性能最差」

  1. for (let i = 0; i < buttons.length; i++) {
  2. buttons[i].onclick = function () {
  3. console.log('此按钮索引:', i)
  4. }
  5. }
  6. for (var i = 0; i < buttons.length; i++) {
  7. (function (i) {
  8. buttons[i].onclick = function () {
  9. console.log('此按钮索引:', i)
  10. }
  11. })(i);
  12. }
  13. for (var i = 0; i < buttons.length; i++) {
  14. buttons[i].onclick = (function (i) {
  15. return function () {
  16. console.log('此按钮索引:', i)
  17. }
  18. })(i);
  19. }
  20. //forEach本质也是闭包
  21. buttons.forEach((button, index) => {
  22. button.onclick = function () {
  23. console.log('此按钮索引:', index)
  24. }
  25. })

方案2:自定义属性

  1. let i = 0
  2. for (; i < buttons.length; i++) {
  3. buttons[i].myindex = i
  4. buttons[i].onclick = function () {
  5. console.log('此按钮索引:', this.myindex)
  6. }
  7. }

方案3:事件委托「比上面的两种方式,性能可以提高 40%~60% 以上」

  1. let box = document.querySelector('#box')
  2. box.onclick = function (ev) {
  3. let target = ev.target,
  4. targetTag = target.tagName
  5. if (targetTag === 'BUTTON') {
  6. console.log('此按钮索引:', target.getAttribute('index'))
  7. }
  8. }