做一个好的 JS 开发者 | 青训营笔记

67 阅读4分钟

这是我参与「第四届青训营 」笔记创作活动的的第2天

第二部分是掘金前端团队负责人月影关于 JS 开发的一些讨论。

一、 关注点分离

网页开发中一个很重要的原则就是 关注点分离 ,意思是各种技术各司其职,只负责对应的领域,不要耦合在一起。

这也体现在了前端开发中,结构、样式、行为三者的抽离。

  • HTML 语言:负责网页的结构,又称语义层
  • CSS 语言:负责网页的样式,又称视觉层
  • JavaScript 语言:负责网页的逻辑和交互,又称逻辑层或交互层

因此,现行开发中不推荐写“行内样式”和“行内脚本”(此处不包含React)。

但初入门时,经常会还会写出直接在 JS 中 getElementBy... 然后直接操作元素样式的代码。

这其实将 JS 代码与页面结构和样式强力得绑定在了一起,而且难以维护,修改样式时大概没有人会去翻 JS 中的控制逻辑。

简单的改进方式是通过 class 来控制一组元素的表现形式,通过监听事件切换 class 来实现样式的变更。

更进一步则是通过 0CSS 方案使 JS 彻底抽离,专注于页面元素行为的控制。

事实上 CSS 可以实现很多通常认为必须由 JS 来完成的功能,在 you-dont-need/You-Dont-Need-JavaScript 这个库中展现了很多实验性质的代码,论证了 CSS 的强力。当然实际开发中性能也是需要考虑的,在适当的时候用一点 JS 来平衡是更好的选择,只是不必要时应避免直接对样式进行操作。

代码片段

组件封装

将页面局部抽取成可复用组件可以很好的减少将来的工作量。同样将组件行为抽象,用面向对象的方式编写可以降低未来的维护成本,也增加了可扩展性。而插件化则为组件可配置带来了可能。

更进一步则是将 HTML 模板化,为配置项提供函数参数,在 JS 中用 render() 生成 HTML,这样扩展组件不会影响到原先使用的组件,且使用时只需提供标签而不是一整个元素结构。

事实上这些思想正是行业总结的最佳实践,已应用在现在流行前端开发框架中,将组件通用行为抽象出来,就形成了组件框架,为新组件开发提供便利。

Tip: 月影的 Demo 中用自定义事件的触发与捕获实现了父子元素的解耦,这与 Vue 中的父子组件通信十分相似。

总结就是:

  • 通过设计API来解耦行为
  • 通过自定义事件来解耦控制流
  • 通过插件化和依赖注入的方式实现灵活性
  • HTML 模板化提供可扩展性

过程抽象

通常在使用一个 Api 时,对应的程序如同一个黑盒,我们只需知道程序能执行什么任务,不必知道程序是如何执行的。确定的操作产生确定的输出。

而为了让一个需求应用到不同的方面,可以将对应的需求抽取出来,独立成为一个 function(当然它需要是一个黑盒),这称之为过程抽象。

在 JS 中,函数有着一等公民的地位,别的元素能做的他都能做,当然作为一个返回值也是可以的。通常称参数和返回值为函数的函数是一个高阶函数。

function HOF0(fn) {
    return function(...args) {
      return fn.apply(this, args);
    }
  }

利用高阶函数可以对函数的行为进行调整(装饰器),实现 Once Throttle Debounce Consumer Iterative 等功能。

声明式

比起命令式,声明式在抽像行为之后便无需再关注函数内部的执行逻辑,具有更好的扩展性和语义性。

//命令式
switcher.onclick = function(e){
  if(e...){  e...  }
  else{  e...  }
}

//声明式
function toggle(...actions){
  return function(...args){
    ...
}

switcher.onclick = toggle(a1, a2, a3)

漫谈

根据需求选择合适的代码风格和实现,Base On 效率或是别的什么,此处仅贴一段月影的代码体现其精神。

// 判断一个mat2d矩阵是不是单位矩阵
function isUnit(m) {
  return m[0] === 1 && m[1] === 0 && m[2] === 0
    && m[3] === 1 && m[4] === 0 && m[5] === 0;
}

实际课程讲述了几个算法实现的例子,在 Web 应用日益复杂的今天,前端程序员也同样需要一定的算法能力,一方面解决问题实现功能,一方面尽可能提供高效的实现。