跟着月影学 JavaScript | 青训营笔记

52 阅读3分钟

跟着月影学 JavaScript | 青训营笔记

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

详细知识点介绍:

写好 JS 的一些原则

  • 各司其责:让 HTML、CSS 和 JavaScript 职能分离。
  • 组件封装:好的 UI 组件具备正确性、扩展性、复用性。
  • 过程抽象:应用函数式编程思想。

例子 深夜食堂

写一段JS,控制一个页面,让它支持浅色和深色两种浏览模式。如果是你来实现,你会怎么做?

方法一:JS直接操作样式

const btn = document.querySelector('#modeBtn')
btn.addEventListener('click', (e) => {
  const body = document.body
  if (e.target.innerHtml === '☀') {
    body.style.backgroundColor = 'black'
    body.style.color = 'white'
    e.target.innerHtml === '🌙'
  } else {
    body.style.backgroundColor = 'white'
    body.style.color = 'black'
    e.target.innerHtml === '☀'
  }
})

JS直接操作样式,操作的是页面元素中的 style 属性,操作这个属性就是写的内联样式,这样书写不仅不能使样式和结构相分离,还不容易后期的修改。

方法二:JS引用class样式

const btn = document.querySelector('#modeBtn')
btn.addEventListener('click', (e) => {
  const body = document.body
  if (body.className === 'night') {
    body.className = 'night'
  } else {
    body,className = ''
  }
})

做到了样式与行为分离,直接引用之前已经写好的样式。我一般也会这样写。

方法三:纯CSS实现

<body>
  <input id="modeCheckBox" type="Checkbox">
  <div class="content">
    <header>
      <label id="modeBtn" for="modeCheckBox"></label>
      <h1>深夜食堂</h1>
    </header>
    <main>
      ...
    </main>
  </div>
</body>
#modeCheckBox:checked + .content {
  background-color: black;
  color: white;
  transition: all 1s;
}

纯 css 实现的浅色系和深色系切换,利用 checkbox 的选中进行切换,利用 label 标签来绑定切换。很巧妙的运用,这个方法不需要使用 JS 来编写。完全的实现了各司其职。

深夜食堂--结论

  • HTML/CSS/JS 各司其责
  • 应当避免不必要的由 JS 直接操作样式
  • 可以用 class 来表示状态
  • 纯展示类交互寻求零 JS 方案

image.png

例子 组件封装 轮播图

用原生 JS 写一个电商网站的轮播图,应该怎么实现?

轮播图一:

表现 CSS
  • 使用 CSS 绝对定位将图片重叠在同一个位置
  • 轮播图切换的状态使用修饰符(modifier)
  • 轮播图的切换动画使用 CSS transition
行为 JS
  • Slider
    • +getSelectedItem()
    • +getSelectedItemIndex()
    • +slideTo()
    • +slideNext()
    • +slidePrevious()

这个代码可以实现一个具有简单功能的轮播图
通过封装轮播图,书写轮播图原型上的API
直接调用实例里面的方法,可以做到切换图片
在外面加了一个定时器就可以实现自动轮播
很多功能还不完善

轮播图二:控制流

添加了控件小圆点和前后按钮来切换图片
轮播图的所有功能基本实现
但是constructor里面的内容非常的沉重
而且独立性不好,不可以选择性添加控件

轮播图三:插件化

constructor里面只放必要的属性
其他的通过插件的形式注册进来
通过依赖注入的方式将this传参到插件函数中,使其能访问class显式原型上的方法
可以很好的控制哪些控件需要哪些控件不需要
但是不需要的控件还是会显示在页面上

轮播图四:模板化

HTML页面非常的干净,跟平时用vue框架写一样非常的干净,只有一个展位的容器

轮播图五:抽象

抽象之后,有一个组件的框架,后续可以在里面添加各种plugin来完善组件

高阶函数

Once
为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。
这个过程我们称为过程抽象
function once(fn) {
  return function(...args) {
    if (fn) {
      const ret = fn.apply(this, args)
      fn = null
      return ret
    }
  }
}
  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器
function HOF0(fn) {
  return function(...args) {
    return fn.apply(this, args)
  }
}

image.png

常用的高阶函数 HOF

  • Once 调用一次
  • Throttle 节流函数
function throttle(fn, time = 500) {
  let timer
  return function(...args) {
    if (timer === null) {
      fn.apply(this, args)
      timer = setTimeout(() => {
        timer = null
      }, time)
    }
  }
}
  • Debounce 防抖函数
function debounce(fn, dur = 500) {
  var timer
  return function(...args) {
    clearTimeout(timer)
    timer = setTimerout(() => {
      fn.apply(this, args)
    }, dur)
  }
}
  • Consumer 延迟函数
function consumer(fn, time) {
  let tasks = [], timer
  
  return function(...args) {
    tasks.push(fn.bind(this, ...args))
    if (timer === null) {
      timer = setInterval(() => {
        tasks.shift().call(this)
        if (tasks.length <= 0) {
          clearInterval(timer)
          timer = null
        }
      }, time)
    }
  }
}
  • Iterative 可迭代方法
function iterative(fn) {
  return function(subject, ...rest) {
    if (isIterable(subject)) {
      const ret = []
      for(let obj of subject) {
        ret.push(fn.apply(this, [obj, ...rest]))
      }
      return ret
    }
    return fn.apply(this, [subject, ...rest])
  }
}

纯函数

  • 确定的输入产生确定的输出(不使用作用域链上的数据)
  • 不会产生副作用(不修改入参参数的状态,比如对象中的值)

编程范式

  • 命令式(怎么做)
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)

image.png

声明式编程比命令式编程更具有扩展性