前言
这是我参与「第四届青训营」笔记创作活动的的第3天
好JavaScript的原则
在知道如何写好JavaScript之前,我们会产生一个疑问,什么是好的JavaScript呢?我们应该用什么标准来衡量我们写的JavaScript代码好不好呢?
好JavaScript的原则可以分为以下三条:
- 各司其职
- 组件封装
- 过程抽象
各司其职
我们都知道前端三剑客是HTML,CSS,JavaScript。他们分别负责什么呢?HTML负责内容的呈现,CSS负责样式的呈现,JavaScript负责行为。让代码各司其职可以降低我们维护代码的成本,出bug的时候更快的找到责任的所在。
深夜食堂的例子
我们需要实现一个页面,这个页面可以切换白天模式和黑夜模式。我们可以如何实现呢?
第一段代码
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 = '🌞';
}
});
绑定按钮点击事件,根据事件赋予css的样式。
第二段代码
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if(body.className !== 'night') {
body.className = 'night';
} else {
body.className = '';
}
});
事先写好两个样式class,绑定点击事件切换class。
第三段代码
<input id="modeCheckBox" type="checkbox">
<div class="content">
<header>
<label id="modeBtn" for="modeCheckBox"></label>
<h1>深夜食堂</h1>
</header>
<main>
<div class="pic">
<img src="https://p2.ssl.qhimg.com/t0120cc20854dc91c1e.jpg">
</div>
<div class="description">
<p>
这是一间营业时间从午夜十二点到早上七点的特殊食堂。这里的老板,不太爱说话,却总叫人吃得热泪盈眶……
</p>
</div>
</main>
</div>
#modeCheckBox {
display: none;
}
#modeCheckBox:checked + .content {
background-color: black;
color: white;
transition: all 1s;
}
不需要JavaScript来控制行为,而是用到了checkedbox的两种状态来转换页面的状态。
结论
- HTML/CSS/JS 各司其责
- 应当避免不必要的由 JS 直接操作样式
- 可以用 class 来表示状态
- 纯展示类交互寻求零 JS 方案
组件封装
在前端的主流框架中,我们很常见的一种思想就是组件化的思想。在组件化的思想下,我们编写组件,然后将组件应用于很多项目。在组件化的帮助下,如果我们的项目是得到一辆自行车,那么相当于我们就是拿着轮子的组件,车身的组件去拼装,而不是从制作一颗颗螺丝做起。组件化封装使得代码复用性高,提高了代码的价值。
组件的概念
组件是指Web页面上抽出来一个个包含模版(HTML)、功能(JS)和样式(CSS)的单元。好的组件具备封装性、正确性、扩展性、复用性。
组件的编写
- 组件设计的原则:封装性、正确性、扩展性、复用性
- 实现组件的步骤:结构设计、展现效果、行为设计
- 三次重构
- 插件化
- 模板化
- 抽象化(组件框架)
过程抽象
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
高阶函数
函数的结果return一个函数。
常用的高阶函数
once
function once(fn) {
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){
if(timer == null){
fn.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, time)
}
}
}
debounce
function debounce(fn, dur){
dur = dur || 100;
var timer;
return function(){
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, 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]);
}
}