[JavaScript | 青训营笔记]
这是我参与「第五届青训营」伴学笔记创作活动的第 2 天 🎉
📌 课程主要内容
📌 写好 JS 的三大原则
📃 各司其职
为了更清晰地解释这一原则,让我们开始于一个简单的案例
💼 案例
控制一个网页,让它支持浅色和深色两种浏览模式
📋 方案 I
🧠 实现思路
核心 JS 代码如下:
const btn = document.getElementById('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 代码中直接对 body 的 CSS 进行了修改
📋 方案 II
🧠 实现思路
核心 JS 代码如下:
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if (body.className !== 'night') {
body.className = 'night';
} else {
body.className = '';
}
});
🍰 优点
- 没有对元素的 CSS 进行直接的操作,而是通过修改元素的类名间接实现了页面样式的变化
💊 存在问题
- 对于仅页面样式的改变仍是由 JS 实现的,没有完全的做到各司其职
📋 方案 III
🧠 实现思路
在 HTML 中:
- 添加一个
type="checkbox"的input,并用一个label与之相关联
在 CSS 中:
- 利用
label的::after选择器使其显示为 ☀️ 或 🌙 - 利用
display: none;将input隐藏 - 通过
input标签的:checked选择器实现正文部分背景和文字颜色的变更,以及日/夜模式图标的变化
核心 CSS 代码如下:
#modeCheckBox:checked + .content {
background-color: black;
color: white;
}
🍰 优点
- 页面整体样式变化的逻辑不再由 JS 实现,而是利用 CSS 的伪类选择器来实现,真正做到了各司其职
📦 总结
- HTML/CSS/JS 各司其职
- 应当避免不必要的由 JS 直接操作样式
- 可以用 class 来表示状态
- 纯展示类的交互应寻求零 JS 方案
📃 组件封装
组件,是指 Web 页面上抽离出来的一个个包含模板(HTML)、功能(JS)和样式(CSS)的单元
好的组件应具备:
- 封装性
- 正确性
- 扩展性
- 复用性
为了更直观体现出封装组件的重要性,让我们仍用一个案例作为开始
💼 案例
用原生 JS 写一个电商网站的轮播图,应该如何实现
📋 方案 I
🧠 实现思路
在 HTML 中:
- 使用
ul无序列表存放图片 - 利用其它标签实现轮播图的翻页控件和控制条
- 为当前图片与非当前图片添加不同的类名
- 为控制条中的已选中按钮和未选中按钮添加不同的类名
样式:
- 对
ul设置list-style-type: none;取消掉原有列表样式 - 利用绝对定位,令所有图片重叠在同一位置
- 利用
transition为轮播图的切换添加动画 - 利用绝对定位,调整翻页控件和控制条的位置
- 利用
:hover状态选择器,实现鼠标移入和移出时翻页控件的显示与隐藏 - 微调细节
行为:
-
编写一个
Slider类,其内包含以下方法:constructor (id, cycle = 3000) {...},构造方法,初始化必要数据getSelectedItem () {...},返回当前轮播图片的 DOM 节点getSelectedItemIndex () {...},返回当前轮播图片的索引slideTo (idx) {...},切换到指定索引的图片slideNext () {...},切换至下一张图片slidePrevious () {...},切换至上一张图片start () {...},开始轮播stop () {...},停止轮播
📃 过程抽象
过程抽象,是用于处理局部细节控制的方法,是函数式编程思想的基础应用
为了进一步理解何为过程抽象,我们仍然以一个案例开始
💼 案例 I
📋 方案
🧠 实现思路
核心 JS 代码如下:
function once () {
return function (...args) {
if (fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
🍰 优点
- 将"只执行某种行为一次"抽象为了一个独立的函数,这样不仅降低了耦合,还实现了封装和复用
📈 高阶函数
在上述案例中,once 函数是采用了一种名叫高阶函数(HOF) 的方法实现的,它具备以下特点:
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
常用高阶函数
once
function once () {
return function (...args) {
if (fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
throttle
function throttle (fn, time = 500) {
let timer;
return function (...args) {
fn.apply(this, arge);
timer = setTimeout(() => {
timer = null;
}, timer);
}
}
debounce
function debounce (fn, dur) {
dur = dur || 100;
var timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, dur);
}
}
consumer / 2
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]);
}
}