这是我参与「第四届青训营 」笔记创作活动的第1天
如何写好JavaScript?
推荐书籍
- 犀牛书 《JavaScript权威指南》
- 红宝书 《JavaScript高级程序设计》
写好JS的一些原则
各司其责
让HTML、CSS、JavaScript职能分离。
例子
写一段JS,控制一个网页,切换浅色深色主题
思路一
利用document API获取按钮 绑定事件 切换body的style样式 同时利用事件对象改变按钮Html内容 [JS代码多且不遵循各司其责]
思路二
与思路一的差别在于 先设置好对应主题样式的class 点击改变className即可 [更加简洁且遵循各司其责]
思路三
零JS实现 利用伪类选择器
核心代码
<input id="check" type="checkbox">
<label id="btn" for="check"> </label>
<style>
#check{
display:none; //隐藏
}
//伪类选择器
#check:checked+.content{
background-color:black;
color:white;
transition:all 1s;
}
</style>
总结
- HTML/CSS/JS各司其责
- 应当避免不必要的由JS直接操作样式
- 可以用class来表示状态
- 纯展示类交互寻求零JS方案
组件封装
好的UI组件具备正确性、拓展性、复用性
例子
用原生JS写一个电商网站的轮播图
结构
典型的列表结构,可以使用无序列表ul元素来实现。
表现
- 使用CSS绝对定位将图片重叠在同一个位置
- 轮播图切换的状态使用修饰符 modifier
- 轮播图的切换动画使用CSS transition
行为
-
Slider
- getSelectedItem()
- getSelectedItemIndex()
- slideTo()
- slideNext()
- slidePrevious()
核心代码
class Swiper{ constructor(id){ this.container = document.getElementById(id); this.items = this.container.querySelectorAll('.item-selected,.item') console.log(this.items); } getSelectedItem(){ const selected = this.container.querySelector('.item-selected'); console.log(selected); return selected //获取选中元素 } getSelectedItemIndex(){ return Array.from(this.items).indexOf(this.getSelectedItem) //获取选中元素的下标 from方法转化为数组 } slideTo(idx) { //切换至对应下标 const selected = this.getSelectedItem() if(selected){ selected.className = 'item' //把原来的选中类名去除 } const item = this.items[idx] console.log(item); if(item){ item.className = 'item-selected' } } slideNext(){ //切换至下一个 const cur = this.getSelectedItemIndex(); const next =( cur + 1 ) %this.items.length; this.slideTo(next) } slidePrevious(){ //切换至前一个 const cur = this.getSelectedItemIndex(); const pre = (this.items.length+cur-1)%this.items.length; this.slideTo(pre) } } const swiper = new Swiper('swiper') swiper.slideTo(2)
-
结构设计
-
展现效果
-
行为设计
- API(功能)
- Event(控制流)
思考
重构组件
解耦
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
- 将HTML模板化,更易于扩展,能通过传入数据灵活改变结构
总结
-
组件设计的原则:封装性、正确性、扩展性、复用性
-
实现组件的步骤:结构设计、展现效果、行为设计
-
三次重构
- 插件化
- 模板化
- 抽象化(组件框架)
过程抽象
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
例子
操作次数限制
- 一些异步交互
- 一次性的HTTP请求
核心代码
const list = document.querySelector('ul');
const buttons = list.querySelectorAll('button');
buttons.forEach((button) => {
button.addEventListener('click', (evt) => {
const target = evt.target;
target.parentNode.className = 'completed';
setTimeout(() => {
list.removeChild(target.parentNode);
}, 2000);
});
});
连续快速多次点击报错
解决方法
利用高阶函数
Once:为了能够让“只执行一次“的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。
function once(fn) {
return function(...args) {
if(fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
HOF
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
function HOF0(fn) {
return function(...args) {
return fn.apply(this, args);
}
}
function HOF0(fn) {
return function(...args) {
return fn.apply(this, args);
}
}
常用高阶函数
编程范式
- 声明式
let list = [1, 2, 3, 4];
const double = x => x * 2;
list.map(double);
- 命令式
let list = [1, 2, 3, 4];
let mapl = [];
for(let i = 0; i < list.length; i++) {
mapl.push(list[i] * 2);
}
例子
\