这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天。
重点内容:🍊JavaScript好的代码的标准🍋编程范式🍑优化之路
🥑前言🥑
本节课程老师带我们深入JavaScript,了解了什么是好的代码,什么是规范,以及如何做的更优雅!
🍆JavaScript好的代码的标准🍆
各司其职:HTML/CSS/JS 职能分离。HTML负责结构,CSS负责表现,JS负责行为。
- 应当避免不必要的由JS直接操作样式
- 可以用class来表示状态
- 纯展示类交互寻求零JS方案
组件封装:好的UI组件具备正确性、扩展性、复用性。
组件是指web页面上抽出来一个个包含模板、功能和样式的单元。
好的组件具备封装性、正确性、扩展性、复用性。
过程抽象:应用函数式编程思想。
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
🥔编程范式🥔
- 命令式更趋向于怎么做
- 声明式更趋向于做什么
- 声明式的编程思想天然的就比命令式有更强的可扩展性!
🥕JavaScript代码质量的优化🥕
前文中所阐述的种种,都是告诉我们什么是好的,好的规范是什么。接下来,我们就跟随月影老师的交通灯案例,一起来深入了解一下如何提高代码质量,做到简洁而优雅吧~
🌰:
const traffic = document.getElementById('traffic');
(function reset(){
traffic.className = 's1';
setTimeout(function(){
traffic.className = 's2';
setTimeout(function(){
traffic.className = 's3';
setTimeout(function(){
traffic.className = 's4';
setTimeout(function(){
traffic.className = 's5';
setTimeout(reset, 1000)
}, 1000)
}, 1000)
}, 1000)
}, 1000);
})();
这是案例最简单无脑的方法,用setTimeout实现,但因为setTimeout是异步的,所以需要层层嵌套来实现五个切换。当然,代码本身是可以运行的,但是在项目开发或者说团队中,这样的代码太过丑陋而且由于层层嵌套,所以很难去修改,难以维护。
那么该如何进行优化呢?让我们看一下下面的代码。⬇️
const traffic = document.getElementById('traffic');
const stateList = [
{state: 'wait', last: 1000},
{state: 'stop', last: 3000},
{state: 'pass', last: 3000},
];
function start(traffic, stateList){
function applyState(stateIdx) {
const {state, last} = stateList[stateIdx];
traffic.className = state;
setTimeout(() => {
applyState((stateIdx + 1) % stateList.length);
}, last)
}
applyState(0);
}
start(traffic, stateList);
这段代码最大的优点就是对上述代码进行数据抽象,把状态抽象出来放在一个状态列表中。然后定义一个start方法来使用状态列表切换元素。这样做的好处是如果想要添加新的状态,只需要在状态列表中添加即可,而不用去复杂的setTimeout嵌套中去想应该放在哪里,在什么位置。
那么有没有更通用的模板呢?⬇️
const traffic = document.getElementById('traffic');
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function poll(...fnList){
let stateIndex = 0;
return async function(...args){
let fn = fnList[stateIndex++ % fnList.length];
return await fn.apply(this, args);
}
}
async function setState(state, ms){
traffic.className = state;
await wait(ms);
}
let trafficStatePoll = poll(setState.bind(null, 'wait', 1000),
setState.bind(null, 'stop', 3000),
setState.bind(null, 'pass', 3000));
(async function() {
// noprotect
while(1) {
await trafficStatePoll();
}
}());
乍一看,这段代码又长又复杂,事实上,他确实如此。😂但是它实现了可扩展性呀!
这一段代码是通过过程抽象来实现红绿灯的转换的。先定义一个wait方法表示等待多长时间,poll表示轮询,即后续绑定后,直接可以进行循环。setState设置元素状态和持续时间。调用trafficStatePoll,将绑定相关的状态和等待参数,直接进行循环。虽然写的代码很多,但是可以扩展到其他领域的切换。有灵活性和可扩展性!在其他需要循环显示的场景里,可以直接在poll中添加绑定信息。
可是它还是好复杂,有没有简单易懂更符合我们标准的方法呢?⬇️
const traffic = document.getElementById('traffic');
function wait(time){
return new Promise(resolve => setTimeout(resolve, time));
}
function setState(state){
traffic.className = state;
}
async function start(){
//noprotect
while(1){
setState('wait');
await wait(1000);
setState('stop');
await wait(3000);
setState('pass');
await wait(3000);
}
}
start();
这段代码是采用异步+函数式的编程实现的。主要思路是定义一个等待若干时间函数wait,再定义一个设置状态函数,最后用一个异步函数start来启动,每调用一次setState,就等待,如此循环即可实现功能。怎么样?惊不惊喜,意不意外!😁
🍄总结🍄
代码的世界,就是这样丰富。我们了解了什么是好的之后,一定不能局限于解决某一问题即可,也要想一想有没有更简单的方法?有没有更好的写法?能不能少一点?如你所见,同样的问题,当我们不断优化代码之后,就是可以做到简洁而优雅~