这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
一、JavaScript 编码原则之各司其责
1.写好JavaScript的一些原则
- 各司其职: 让 HTML 、CSS、JavaScript职能分离
- 组件封装: 好的 UI 组件具备正确性、扩展性、复用性
- 过程抽象: 应用函数式编程思想
2.各司其职
- HTML/CSS/JS各司其责
- 应当避免不必要的由JS直接操作样式
- 可以用class来表示状态
- 纯展示类交互寻求零JS方案
二、JavaScript 编码原则之组件封装
组件:组件是指Web页面上抽出来一个个包含模板(HTML) 、功能(JS)和样式(CSS)的单元。好的组件具备封装性、正确性、扩展性、复用性。
1.轮播图实现
-
结构:轮播图是一个典型的列表结构,我们可以使用无序列表ul元素来实现
-
表现:
- 使用CSS绝对定位将图片重叠在同一个位置
- 轮播图切换的状态使用修饰符(modifier))
- 轮播图的切换动画使用CSS transition
-
行为
-
Slider(API)
- +getSelectedltem()
- +getSelectedltemlndex()
- +slideTo()
- +slideNext()
- +slidePrevious()
- 控制流;使用自定义事件来解耦
-
2.总结(基本方法)
-
结构设计
-
展现效果
-
行为设计
- API(功能)
- Event (控制流)
3.重构(插件化)
-
解耦:
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
- 抽象:将组件通用模型抽象出来
3.总结(组件封装)
-
组件设计的原则:封装性、正确性、扩展性、复用性
-
实现组件的步骤:结构设计、展现效果、行为设计
-
三次重构
- 插件化
- 模板化
- 抽象化(组件框架)
三、JavaScript 编码原则之过程抽象
1.过程抽象
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
2.例子:操作次数限制
- 一些异步交互
- 一次性的HTTP请求
3.高阶函数
- Once:为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象
-
HOF:
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
常用的高阶函数
- Once
- Throttle
- Debounce
- Consumer /2
- Iterative
4.编程范式
- 命令式与声明式
-
总结
- 过程抽象/HOF/装饰器
- 命令式/声明式
四、Leftpad 事故背景引入
1.看一段代码
function isUnit(){
return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] == 0 m[5] === 0
}
这是一段真实的代码
2.写代码最应该关注什么?
风格?效率?约定?使用场景? 设计?
3.当年的Leftpad 事故
-
事件本身的槽点
- NPM模块粒度
- 代码风格
- 代码质量/效率
- 源代码
function leftpad(str,len,ch){
str = String(str);
var i = -1;
if(!ch && ch! == 0) ch = ' ';
len = len - str.length;
while(++i < len){
str = ch + str;
}
return str;
}
- 优化后的代码(代码更简介、效率提升)
function leftpad(str,len,ch){
str = "" + str;
const padLen = len - str.length;
if (padLen <= 0){
return str;
}
return ("" + ch).repeat(padLen) + str;
}
五、代码质量优化之路
1.代码实践-交通灯
这个例子的具体需求是,模拟交通灯信号,每隔一段时间,显示不同的颜色,循环切换状态
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', 1000),
setState.bind(null, 'pass', 1000));
(async function() {
// noprotect
while(1) {
await trafficStatePoll();
}
}());
2.代码实践-洗牌
实现一个洗牌算法
function shuffle(cards) {
const c = [...cards];
for (let i = c.length; i > 0; i--) {、
const pIdx = Math.floor(Math.random() * i);
[c[pIdx], c[i - 1]] = [c[i - 1], c[pIdx]];
}
return c;
}
3.代码实践-分红包
在APP中的抢红包功能中,内部进行随机的分红包的算法
function * shuffle(cards){
const c = [...cards];
for(let i = c.length; i > 0; i--) {
const pIdx = Math.floor(Math.random() * i);
[c[pIdx], c[i - 1]] = [c[i - 1], c[pIdx]];
yield c[i - 1];
}
}
function generate(amount, count){
if(count <= 1) return [amount];
const cards = Array(amount - 1).fill(0).map((_, i) => i + 1);
const pick = shuffle(cards);
const result = [];
for(let i = 0; i < count; i++) {
result.push(pick.next().value);
}
result.sort((a, b) => a - b);
for(let i = count - 1; i > 0; i--) {
result[i] = result[i] - result[i - 1];
}
return result;
}
六、总结
1.如何评估一个代码的好坏?
- 其实还是要根据使用场景来定
2.前端不需要学算法?
- 前端的算法也是必不可少
- 一些应用场景依旧需要依赖算法
- 使用正确的算法可以提高代码的效率
3.总结(写好JavaScript的重要因素)
- 函数要做好封装,降低函数耦合性
- 要确保函数尽量不要直接使用和修改外部的变量,要用到外部变量,应该使其成为参数传入函数中
- 函数是一个处理数据的最小单元。它包含数据和处理过程
- 做好数据抽象,将用到的数据抽象出去形成对象或数组,可以提高函数的复用性
- 做好过程抽象,将过程进行抽象形成独立的函数,可以提高函数的复用性、灵活性
- 将异步操作进行promise化,可以提高函数的可读性