JavaScript编码 | 青训营笔记

70 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 二 天

今日课程重点

  1. 编码原则-各司其责
  2. 编码原则-组件封装
  3. 编码原则-过程抽象
  4. Leftpad
  5. 代码优化

写好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(控制流)

改进重构轮播图组件

image.png

image.png

image.png

  • 组件设计的原则:封装性,正确性,扩展性,复用性
  • 实现组件的步骤:结构设计,展现效果,行为设计
  • 三次重构:插件化,模板化,抽象化(组件框架)

3.编码原则-过程抽象

  • 用来处理局部细节控制的一些方法
  • 函数式编程思想的基础应用

image.png

高阶函数

image.png

image.png 常用高阶函数

  • Once
  • Throttle
  • Debounce
  • Consumer/2
  • Iterative

编程范式

image.png

image.png

<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

写代码时最应该关注的是

  • 风格
  • 效率
  • 使用场景
  • 约定
  • 设计 评判代码的好坏要根据具体的使用场景

image.png

image.png

image.png

image.png

5. 代码优化

交通灯;状态切换

版本一 版本二 版本三 版本四

判断是否是4的幂

image.png

image.png

image.png 通过正则表达式判断 image.png

洗牌

错误版本

各数字出现概率不平均

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]);

分红包

image.png

image.png