如何写好JavaScript
写好JS的一些原则:各司其职、组件封装、过程抽象
各司其责
- HTML/CSS/JavaScript各司其责
- HTML -> Structural ; CSS -> Presentational ; JavaScript -> Behavioral
- 应当避免不必要的由JS直接操作样式
- 可以用class来表示状态
- 纯展示类交互应寻求零JS方案
组件封装
组件是指Web页面上抽出来的一个个包含模块(HTML)、样式(CSS)和功能(JS)的单元。好的组件具备封装性、正确性、扩展性、复用性。实现组件的步骤:结构设计、展现效果、行为设计,三次重构:插件化重构、模板化重构、抽象化重构。
- 结构设计:HTML
- 展现效果:CSS
- 行为设计:JS
- API(功能),API 设计应保证原子操作,职责单一,满足灵活性。
- Event(控制流),使用自定义事件来解耦。
- 插件化重构,即解耦
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
- 模板化重构
- 将HTML模板化,更易于扩展
- 抽象化重构(组件框架)
- 将通用的组件模型抽象出来
过程抽象
过程抽象是指用来处理局部细节控制的一些方法,是函数式编程思想的基础应用。
- 高阶函数
- 以函数作为参数
- 以函数作为返回值
- 常用于函数装饰器
//零阶高阶函数,等价于直接调用函数
function HOF0 (fn){
return function(...args){
return fn.apply(this.args);
}
}
- 构造 once 高阶函数,为了能够让“只执行一次”的需求(例如一些异步操作、一次性的HTTP请求)覆盖不同的事件处理,我们可以将这个需求利用闭包剥离出来。这个过程称为过程抽象。
function once ( fn ) {
return function (...args){
if ( fn ) {
const res = fn.allpy (this.args);
fn = null;
return res;
}
}
}
- 防抖函数,在第一次触发事件时,不立即执行函数,而是给出一个期限值,如果在期限值内没有再次触发滚动事件,那么就执行函数,如果在期限值内再次触发滚动事件,那么当前的计时取消,重新开始计时。
const debounce = (fn, delay = 100) => {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this,args);
}, delay);
}
}
- 节流函数,类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活。效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
const throttle = (fn, delay = 500) => {
let timer;
return function(...args){
if(timer == null) {
fn.apply(this,args);
timer = setTimeout(() => {
timer = null;
},delay);
}
}
}
- 为什么要使用高阶函数?
- 减少系统内非纯函数的数量,提高系统的可测试性和稳定性。