JavaScript初学习 | 青训营笔记

72 阅读4分钟

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

写好JS的一些原则

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

各司其职

写一段JS,控制一个网页,让他支持深色浅色两种浏览模式。

版本一

const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e)=>{
    const body = document.body;
    if(e.target.innerHTML === '☀'){
    body.style.backgroundColor = 'black';
    body.style.color = 'white';
    e.target.innerHTML ='🌙';
    } else {
    body.style.backgroundColor = 'white';
    body.style.color = 'black';
    e.target.innerHTML ='☀';
    }
});

版本二

const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e)=>{
    const body = document.body;
    if(body.className !== 'night ') {
        body.className = 'night ';
    }else {
    body.className = '';
    }
});

css代码:
body.night {
    background-color: black;
    color: white;
    transition: all 1s;
}

版本一与版本二,功能都已完成,但二与一的区别就在于二是写了一个night的样式,点击之后根据类名切换,默认白天点击黑夜。

版本二只操作了元素状态,而版本一直接操作了css样式,这里就表现出了结构表现分离的原则。优势就在于更直观的表明了代码的功能。

当然如果是纯css样式变动的话,也可以不使用JS代码,直接对css进行操作。

结论:

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

组件封装

用原生JS写一个电商网站的轮播图

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

  • 结构:HTML 使用无序列表ol实现

  • 表现:CSS

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

  • 用来处理局部细节控制的一些方法

  • 函数式编程思想的基础应用 操作次数限制

  • 一些异步交互

  • 一次性的HTTP请求

一个学习列表功能,点击表示任务完成,从列表中消失

function once(fn) {
    return function(...args) {
        if(fn){
            const ret =fn.apply(this, args);
            fn = null;
            return ret;
        }
    }
}

const list=docunent.querySelector('ul');
const buttons =list.querySelectorAll("button");
buttons.forEach((button) => {
    button.addEventListener('click', once((evt)=>{
        const target = evt.target;
        target.parentNode.className = "completed";
        setTimeout(() => {
            list.romoveChild(target.parentNode);
        }, 2000);
    }));
});

经典的过程抽象,抽象了once这个过程,参数fn是函数,作用为把里面的函数在任何函数进行调用时只被调用一次。 once保证只进行一次调用。

高阶函数 HOF

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器
function HOFO(fn){
    return function(...args){
        return fn.apply(this, args);
    }
}

常用高阶函数

  • Once
  • Throttle
  • Debounce
  • Consumer/2
  • lterative

当一定时间间隔内只需触发一次时使用 Throttle

function throttle(fn, time = 500){
    let timer;
    return function(...args){
        if(timer == null) {
            fn.apply(this,args);
            timer = setTineout(()=>{
                timer = null;
            }, time)
        }
    }
}
btn.onclick = throttle(function(e){
    circle.innerHTML = parseInt(circle.innerHTML)+1;
    circle.classNane= 'fade';
    setTimeout(()=>cirele.className = '',250);
});

实现每500只能调用一次的功能 其余的高阶函数就不多介绍

实现多个交通灯状态切换功能

版本一

const traffic = document.getElementById('traffic');

(function reset(){
    traffic.className= 's1';

    setTimeout(function(){
        traffic.className= 's2';
        setTimeout(function(){
            traffic.classNane = 's3';
            setTimeout(function(){
                traffic.className = 's4';
                setTimeout(function(){
                    traffic.classMane = 's5';
                    setTimeout(reset, 1000)
                }, 1000)
            }, 1000)
        }, 1000)
    }, 1000)
})();

多个异步嵌套,较难维护

版本二(数据抽象)

<ul id="traffic" class="wait">
  <li></li>
  <li></li>
  <li></li>
</ul>
#traffic {
    display: flex;
    flex-direction: column;
  }
  
  #traffic li {
    display: inline-block;
    width: 50px;
    height: 50px;
    background-color: gray;
    margin: 5px;
    border-radius: 50%;
  }
  
  #traffic.stop li:nth-child(1) {
    background-color: #a00;
  }
  
  #traffic.wait li:nth-child(2) {
    background-color: #aa0;
  }
  
  #traffic.pass li:nth-child(3) {
    background-color: #0a0;
  }
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);

版本三(过程抽象)

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();
  }
}());

版本四(异步+函数式)

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