跟着月影学 JavaScript | 青训营笔记

49 阅读4分钟

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

一、JavaScript 编码原则之各司其责

1.写好JavaScript的一些原则

  • 各司其职: 让 HTML 、CSS、JavaScript职能分离
  • 组件封装: 好的 UI 组件具备正确性、扩展性、复用性
  • 过程抽象: 应用函数式编程思想

2.各司其职

  • HTML/CSS/JS各司其责
  • 应当避免不必要的由JS直接操作样式
  • 可以用class来表示状态
  • 纯展示类交互寻求零JS方案

二、JavaScript 编码原则之组件封装

组件:组件是指Web页面上抽出来一个个包含模板(HTML) 、功能(JS)和样式(CSS)的单元。好的组件具备封装性、正确性、扩展性、复用性。

1.轮播图实现

  1. 结构:轮播图是一个典型的列表结构,我们可以使用无序列表ul元素来实现

  2. 表现:

    • 使用CSS绝对定位将图片重叠在同一个位置
    • 轮播图切换的状态使用修饰符(modifier))
    • 轮播图的切换动画使用CSS transition
  1. 行为

    • Slider(API)

      • +getSelectedltem()
      • +getSelectedltemlndex()
      • +slideTo()
      • +slideNext()
      • +slidePrevious()
    • 控制流;使用自定义事件来解耦

2.总结(基本方法)

  • 结构设计

  • 展现效果

  • 行为设计

    • API(功能)
    • Event (控制流)

3.重构(插件化)

  1. 解耦:

    • 将控制元素抽取成插件
    • 插件与组件之间通过依赖注入方式建立联系

image-20230117105258255.png

  1. 抽象:将组件通用模型抽象出来

image-20230117105429087.png

3.总结(组件封装)

  • 组件设计的原则:封装性、正确性、扩展性、复用性

  • 实现组件的步骤:结构设计、展现效果、行为设计

  • 三次重构

    • 插件化
    • 模板化
    • 抽象化(组件框架)

三、JavaScript 编码原则之过程抽象

1.过程抽象

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

2.例子:操作次数限制

  • 一些异步交互
  • 一次性的HTTP请求

3.高阶函数

  1. Once:为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象
  1. HOF:

    • 以函数作为参数
    • 以函数作为返回值
    • 常用于作为函数装饰器

常用的高阶函数

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

image-20230117110722531.png

4.编程范式

  1. 命令式与声明式

image-20230117110803022.png

  1. 总结

    • 过程抽象/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 事故

  1. 事件本身的槽点

    • NPM模块粒度
    • 代码风格
    • 代码质量/效率
  1. 源代码
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;
}
  1. 优化后的代码(代码更简介、效率提升)
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化,可以提高函数的可读性