JavaScript 编码原则(2) | 青训营笔记

64 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第4天。

一、本堂课重点内容

  • 过程抽象
  • JavaScript代码质量优化

二、详细知识点介绍

闭包

以计数器函数为例。

var counter = 0; 
function add(){ 
    return counter += 1; 
} 
add(); 
add(); 
add();

以上代码确实能实现计数器,但counter是全局变量,任何代码都可以访问到,即使不通过add()函数。

function add() { 
    var counter = 0; 
    return counter += 1; 
}

而通过此种方式不会导致counter的自增,因为每次调用都会是新的counter,而函数外部无法访问到counter。

如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

var add = (function () { 
    var counter = 0; 
    return function () {return counter += 1;} })(); 
add(); 
add(); 
add();

此时,add函数返回一个匿名函数,这个匿名函数将counter的值+1后返回。这个叫作 JavaScript 闭包。 它使得函数拥有私有变量变成可能。计数器受匿名函数的作用域保护,只能通过 add 方法修改。

可以通过闭包完成更多功能。

高阶函数

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于函数装饰器
function HOF0(fn){
    return function(...args){
        return fn.apply(this,args);
    }
}

这是一个最简单的高阶函数,接收一个fn,返回该fn,HOf0的行为与fn完全一致,是等价高阶函数。

常用高阶函数:

  • once 只调用一次
  • throttle 节流函数,mousemove事件太快,影响性能,不需要很高的频率
  • debounce 防抖函数,代码自动保存,编辑完成后再保存,快速变化字符串内容时不保存,一段时间后无操作再执行
  • consumer 异步函数转同步执行,或实现延时调用
  • iterative 可迭代方法,对数组里的元素迭代地调用fn

详见实践练习例子。

过程抽象

一些程序的行为和动作可以复用,即函数可以复用,将这些函数抽象出来称为过程抽象,JavaScript可以通过高阶函数实现过程抽象。

  • 用来处理局部细节控制的一些方法
  • 函数式编程的基础应用

当代组件库,MVVM框架中普遍使用过程抽象,React hooks

纯函数:不改变外部环境的函数,调用时不需要创建专门的context。高阶函数一般是纯函数,输入输出确定。

非纯函数:有副作用的函数,调用时需要在专门的context中,会改变外部环境。

编程范式

编程语言:

  • 命令式
    • 面向过程:FORTRAN、C
    • 面向对象:C++、Java
  • 声明式
    • 逻辑式:Prolog
    • 函数式:Haskell、Erlang

JavaScript既有命令式的特点也有声明式的特点,可以使用2种风格写。

命令式:强调怎么做

let list = [1, 2, 3, 4];
let mapl = [];
for(let i = 0; i < list.length; i++){
    mapl.push(list[i] * 2);
}

声明式:强调做什么

let list = [1, 2, 3, 4];
const double = x => x * 2;
list.map(double);

声明式比命令式更有可扩展性。

三、实践实习例子

高阶函数-once

const once=(fn)=>{
  return function(...args){
    if(fn){
      const ret=fn.apply(this,args);
      fn=null;
      return ret;
    }
  }
}

命令式与声明式

以交通灯切换为例:

命令式:强调的是怎么做

switcher.onclick = function(evt){
  if(evt.target.className === 'on'){
    evt.target.className = 'off';
  }else{
    evt.target.className = 'on';
  }
}

声明式:强调的是做什么

function toggle(...actions){
  return function(...args){
    let action = actions.shift();//移除数组最前面一个
    actions.push(action);
    return action.apply(this, args);
  }
}

switcher.onclick = toggle(
  evt => evt.target.className = 'off',
  evt => evt.target.className = 'on'
);

若增加一种灯的状态,如黄灯,命令式就要新加一个分支,而声明式只需新加一个函数,实现了多态的切换。

四、课后个人总结

本节课学习了JavaScript过程抽象的编程思想,与数据抽象类似,过程抽象是将程序的功能或行为进行抽象,主要是对函数进行抽象。高阶函数是实现过程抽象的重要方式,与普通函数不同,高阶函数的参数和返回值均是参数,可以实现函数装饰器的功能。

本节课涉及的一些基础知识,如函数闭包,高阶函数等,尚有不了解的部分,在此一并记录。

五、参考链接

JavaScript闭包详解_旧味清欢|的博客-CSDN博客_javascript闭包

JavaScript 闭包 | 菜鸟教程 (runoob.com)

⁡‬‬​​‍⁤​⁡‌⁢​‍⁣​⁢⁡​‌⁡‬​​‍⁤‍⁡⁢⁣‌⁣⁡​​⁤⁣​‍⁣⁡⁣​​‍⁣‬⁤跟着月影学 JavaScript(下).pptx - 飞书云文档 (feishu.cn)