这是我参与「第五届青训营 」伴学笔记创作活动的第 二 天
今日课程重点
- 编码原则-各司其责
- 编码原则-组件封装
- 编码原则-过程抽象
- Leftpad
- 代码优化
写好JS的一些原则:各司其责,组件封装,过程抽象
1.编码原则-各司其责
eg深夜食堂-普通版本
通过点击按钮逐步修改不同的样式,该过程实现复杂,若有更多状态加入需要切换,则需要大量修改逻辑代码
eg深夜食堂-优化版本一
通过将样式写为一个类,点击切换类即可实现切换不同状态,若需要加入其它状态,则再增加一个类写入样式即可
eg深夜食堂-优化版本二
直接通过CSS实现不同状态的切换,在页面加入一个复选框,通过复选框选中与否实现状态变更,通过label标签的for属性将label与表单控件复选框绑定在一起
案例思考
- HTML/CSS/JS各司其责
- 应当避免由JS不必要的直接操作样式
- 可以用class来表示状态
- 纯展示类交互寻求零JS方案
2.编码原则-组件封装
组件是指Web页面上抽出来一个个包含模板(HTML),功能(JS),样式(CSS)的单元。
好的组件具有封装性,正确性,复用性,扩展性
eg轮播图(原生JS)
轮播图是一个典型的列表结构,可使用无序列表来实现
<div id="my-slider" class="slider-list">
<ul>
<li class="slider-list__item--selected">
<img src="https://p5.ssl.qhimg.com/t0119c74624763dd070.png"/>
</li>
<li class="slider-list__item">
<img src="https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg"/>
</li>
<li class="slider-list__item">
<img src="https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg"/>
</li>
<li class="slider-list__item">
<img src="https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg"/>
</li>
</ul>
</div>
使用定位将图片重叠在同一位置
轮播图切换的状态使用修饰符(modifier)
轮播图的切换动画使用transition
#my-slider{
position: relative;
width: 790px;
}
.slider-list ul{
list-style-type:none;
position: relative;
padding: 0;
margin: 0;
}
.slider-list__item,
.slider-list__item--selected{
position: absolute;
transition: opacity 1s;
opacity: 0;
text-align: center;
}
.slider-list__item--selected{
transition: opacity 1s;
opacity: 1;
}
行为设计:API
API设计应保证原子操作,职责单一,满足灵活性
设计思路:Slider
- +getSelectedItem()
- +getSelectedItemIndex()
- +slideTo()
- +slideNext()
- +slidePrevious()
class Slider{
constructor(id){
this.container = document.getElementById(id);
this.items = this.container
.querySelectorAll('.slider-list__item, .slider-list__item--selected');
}
getSelectedItem(){
const selected = this.container
.querySelector('.slider-list__item--selected');
return selected
}
getSelectedItemIndex(){
return Array.from(this.items).indexOf(this.getSelectedItem());
}
slideTo(idx){
const selected = this.getSelectedItem();
if(selected){
selected.className = 'slider-list__item';
}
const item = this.items[idx];
if(item){
item.className = 'slider-list__item--selected';
}
}
slideNext(){
const currentIdx = this.getSelectedItemIndex();
const nextIdx = (currentIdx + 1) % this.items.length;
this.slideTo(nextIdx);
}
slidePrevious(){
const currentIdx = this.getSelectedItemIndex();
const previousIdx = (this.items.length + currentIdx - 1)
% this.items.length;
this.slideTo(previousIdx);
}
}
const slider = new Slider('my-slider');
slider.slideTo(3);
行为设计:控制流
通过自定义事件来解耦 设计基本方法:
- 结构设计
- 展现效果
- 行为设计-API(功能)-Event(控制流)
改进重构轮播图组件
- 组件设计的原则:封装性,正确性,扩展性,复用性
- 实现组件的步骤:结构设计,展现效果,行为设计
- 三次重构:插件化,模板化,抽象化(组件框架)
3.编码原则-过程抽象
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
高阶函数
常用高阶函数
- Once
- Throttle
- Debounce
- Consumer/2
- Iterative
编程范式
<div id="switcher" class="on"></div>
#switcher {
display: inline-block;
background-color: black;
width: 50px;
height: 50px;
line-height: 50px;
border-radius: 50%;
text-align: center;
cursor: pointer;
}
#switcher.on {
background-color: green;
}
#switcher.off {
background-color: red;
}
#switcher.on:after {
content: 'on';
color: white;
}
#switcher.off:after {
content: 'off';
color: white;
}
eg.Toggle命令式
switcher.onclick = function(evt){
if(evt.target.className === 'on'){
evt.target.className = 'off';
}else{
evt.target.className = 'on';
}
}
Toggle声明式
function toggle(...actions){
return function(...args){
let action = actions.shift();
actions.push(action);
return action.apply(this, args);
}
}
switcher.onclick = toggle(
evt => evt.target.className = 'off',
evt => evt.target.className = 'on'
);
Toggle三态
function toggle(...actions){
return function(...args){
let action = actions.shift();
actions.push(action);
return action.apply(this, args);
}
}
switcher.onclick = toggle(
evt => evt.target.className = 'warn',
evt => evt.target.className = 'off',
evt => evt.target.className = 'on'
);
4. Leftpad
写代码时最应该关注的是
- 风格
- 效率
- 使用场景
- 约定
- 设计 评判代码的好坏要根据具体的使用场景
5. 代码优化
交通灯;状态切换
版本一 版本二 版本三 版本四
判断是否是4的幂
通过正则表达式判断
洗牌
错误版本
各数字出现概率不平均
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function shuffle(cards) {
return [...cards].sort(() => Math.random() > 0.5 ? -1 : 1);
}
console.log(shuffle(cards));
版本一
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function shuffle(cards) {
return [...cards].sort(() => Math.random() > 0.5 ? -1 : 1);
}
const result = Array(10).fill(0);
for(let i = 0; i < 1000000; i++) {
const c = shuffle(cards);
for(let j = 0; j < 10; j++) {
result[j] += c[j];
}
}
console.log(result);
版本二
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
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;
}
const result = Array(10).fill(0);
for(let i = 0; i < 10000; i++) {
const c = shuffle(cards);
for(let j = 0; j < 10; j++) {
result[j] += c[j];
}
}
console.log(shuffle(cards));
console.log(result);
版本三
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function * draw(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];
}
}
const result = draw(cards);
console.log([...result]);