一个完整的项目实例演示用JavaScript实现某个功能或解决某个问题 | 青训营

214 阅读4分钟

1 JavaScript课程中提到的案例分析

eg.间隔li奇偶标签行颜色不同案例
1 普通隔行变换

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Interlaced_discoloration 隔行变色</title>
  </head>
  <body>
    <div class="inter-color">
      <ul class="til">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
      </ul>
    </div>
  </body>
</html>
* {
        margin: 10;
        padding: 0;
      }
      li {
        list-style: none;
      }
      .til li {
        width: 200px;
        height: 20px;
        /* border: 1px solid; */
      }
function getTag(n) {
    return document.getElementsByTagName(n)
}
var url = getTag('li')
for (var i = 0; i < url.length; i++) {
    if (i % 2 == 0) {
        url[i].style.background = 'lightgray'
    } else {
        url[i].style.background = 'lightgreen'
    }
}

image.png
思路:用css写一个li列表,通过在函数里return出来一个document.getElementsByTagName方法,执行这个函数,传参获取所有的li,是一个类数组,然后用for循环循环出所有li,当循环到第几个li标签的时候,对该li标签的索引'i%2 == 0'判断为偶数则颜色为'orange',否则颜色为'lightgreen'
2 鼠标滑过变色

<div class="inter-color">
    <ul class="til">
       <li>1</li>
       <li>2</li>
       <li>3</li>
       <li>4</li>
       <li>5</li>
    </ul>
</div>
* {
   margin: 10;
   padding: 0;
  }
li {
   list-style: none;
  }
.til li {
    width: 200px;
    height: 20px;
 /* border: 1px solid; */
}
function getTag(n) {
        return document.getElementsByTagName(n)
      }
      var url = getTag('li')
      for (var i = 0; i < url.length; i++) {
        if (i % 2 == 0) {
          url[i].style.background = 'lightgray'
        } else {
          url[i].style.background = 'azure'
        }
      }
      // this的思想:把初始色值存储到了每一个li的自定义属性上,当划出li标签时,再用li标签上自定义的值,赋给li的背景色
      for (var i = 0; i < url.length; i++) {
        url[i].myli = url[i].style.background
        url[i].onmouseover = function () {
          this.style.background = 'orange'
        }
        url[i].onmouseout = function () {
          this.style.background = this.myli
        }
      }

思路:在隔行变色的基础上加上一个for循环,在该for循环中,将初始色值存储到每一个li标签自定义的url[i].myli属性上,当鼠标划到(onmouseover)中,this.style.background赋值为orange,当鼠标移出(onmouseout)时,this.style.background赋值为this.myli

image.png

  • 编程范式:命令式与声明式 eg.命令式--详细的命令机器怎么去处理一件事情以达到想要的结果
    let list = [1,2,3,4]
    let mapl = []
    for(let i = 0; i < list.length; i++){
        mapl.push(list[i] * 2)
    }
    
    eg.声明式--只告诉想要的结果,机器自己摸索过程
    let list = [1,2,3,4]
    const double = x => x * 2
    list.map(double)
    

举例来说:

  • 命令式编程:描述详细路径(1、下一个路口左转;2、下个有红灯的路口右转;3、前进100米;4、在下个路口掉头;5、到达王府井大街出租车停车区)
  • 声明式编程:只告诉目的地(带我到王府井大街)

1.4 一些案例分析

1.4.1 交通灯状态转换

  • 状态模式:实现一个切换多个交通灯状态切换的功能 状态模式的定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。将这句话分为两部分来看,第一部分的意思是将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化;第二部分是从客户的角度看,使用的对象在不同的状态下具有截然不同的行为,这个对象看起来是从不同的类中实例化而来的,实际上这是使用了委托的效果。
    • 方法一:把每种状态嵌套进去
      该方法一开始就获取了class=traffic的元素,然后声明一个reset函数,这个函数的功能是:默认情况下,将这个traffic元素的className属性设置为's1'.这样设置后,就命中了下面这个CSS设置,使得li元素的背景变成了绿色。然后,让setTimeout方法嵌套。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>用JS实现交通信号灯</title>
    <style>
      #traffic {
        display: flex;
        flex-direction: column;
      }
      #traffic li {
        list-style: none;
        width: 50px;
        height: 50px;
        background-color: lightgray;
        margin: 5px;
        border-radius: 50%;
      }
      #traffic.s1 li:nth-child(1) {
        background-color: #a00;
      }
      #traffic.s2 li:nth-child(2) {
        background-color: #0a0;
      }
      #traffic.s3 li:nth-child(3) {
        background-color: #00a;
      }
      #traffic.s4 li:nth-child(4) {
        background-color: #a0a;
      }
      #traffic.s5 li:nth-child(5) {
        background-color: #0aa;
      }
    </style>
  </head>
  <body>
    <div class="traffic-light-page">
      <ul id="traffic" class="wait">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </div>
    <script>
      const traffic = document.getElementById('traffic')(function reset() {
        // 默认情况下,将traffic的className属性设置为's1'
        traffic.className = 's1'
        setTimeout(function () {
          traffic.className = 's2'
          setTimeout(function () {
            traffic.className = 's3'
            setTimeout(function () {
              traffic.className = 's4'
              setTimeout(function () {
                traffic.className = 's5'
                setTimeout(reset, 1000)
              }, 1000)
            }, 1000)
          }, 1000)
        }, 1000)
      })()
    </script>
  </body>
</html>

image.png
缺点:1、如果修改了HTML代码,元素就不叫做traffic了,这个函数就不工作了 2、如果想把这个函数复用到其他地方,还得在那个地方重建这个traffic对象

  • 方法二:抽象数据做状态的切换
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>用JS实现交通信号灯--方法二:抽象数据做状态的切换</title>
    <style>
      #traffic {
        display: flex;
        flex-direction: column;
      }
      #traffic li {
        display: inline-block;
        width: 50px;
        height: 50px;
        background-color: lightgray;
        margin: 5px;
        border-radius: 50%;
      }
      #traffic.stop li:nth-child(1) {
        background-color: #a00;
      }
      #traffic.wait li:nth-child(2) {
        background-color: rgb(245, 245, 12);
      }
      /* 将class=traffic&class=pass元素下的第一个li的元素的背景色设为绿色 */
      #traffic.pass li:nth-child(3) {
        background-color: #0a0;
      }
    </style>
  </head>
  <body>
    <div class="traffic-light-page">
      <ul id="traffic" class="wait">
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </div>
    <script>
      // 获取class=traffic的元素
      const traffic = document.getElementById('traffic')
      // 抽象数据状态
      const stateList = [
        {
          state: 'stop',
          last: 1000,
        },
        {
          state: 'wait',
          last: 1000,
        },
        {
          state: 'pass',
          last: 1000,
        },
      ]

      // 异步的递归调用
      function start(traffic, stateList) {
        // 会根据stateIdx进行切换
        function applyState(stateIdx) {
          const { state, last } = stateList[stateIdx]
          traffic.className = state
          setTimeout(() => {
            applyState((stateIdx + 1) % stateList.length)
          }, last)
        }
        applyState(0)
      }
      start(traffic, stateList)
    </script>
  </body>
</html>

数据抽象就是把数据定义并聚合成能被过程处理的对象,交由特定的过程处理。简单来说,就是数据的结构化。经过数据抽象的代码可以适应不同状态和时间的业务需求,只需要修改数据抽象即可,而不修改start方法。
缺点:采用数据抽象重构后,start方法还未达到完全封装。因为start函数中存在一部分改变外部状态的代码。把改变外部状态的部分叫做代码的副作用。通常情况下,可以考虑把函数体内部分有副作用的代码剥离出来,这样往往能提升函数的通用性、稳定性和可测试性。
image.png

  • 方法三:过程抽象出一个轮询的方法
// 其他不变
    <script>
      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, 'pass', 3000), setState.bind(null, 'stop', 3000))
      ;(async function () {
        while (1) {
          await trafficStatePoll()
        }
      })()
    </script>

image.png

  • 方法四:
    <script>
      const traffic = document.getElementById('traffic')

      // 等待多长时间
      function wait(time) {
        return new Promise((resolve) => setTimeout(resolve, time))
      }

      // 状态的切换
      function setState(state) {
        traffic.className = state
      }

      async function start() {
        while (1) {
          setState('wait')
          await wait(1000)
          setState('pass')
          await wait(3000)
          setState('stop')
          await wait(3000)
        }
      }
      start()
    </script>

原理:将setTimeout方法封装成wait函数,这个函数将setTimeout方法用promise包裹起来,并返回这个Promise对象。有了这个wait函数之后,setTimeout嵌套,比较容易改写成一个async函数中的await函数。 image.png

  • 判断是否是4的幂

    • 直接判断是否能被4整除(性能不是最好的)
    • 按位与0b11
    • O1时间复杂的一种算法
  • 洗牌

1.5 一些JavaScript性能优化小技巧

2 用JavaScript实现一个完整的项目实例演示

解决一个问题或实现一个功能

2.1 完成“动态选中并删除表格多行”的案例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>用javascript动态选中并删除表格多行</title>
    <style>
      h2 {
        margin-left: 350px;
      }
      table {
        width: 800px;
        height: 400px;
        border: 2px solid lightgray;
        border-collapse: collapse;
        background-color: lightgoldenrodyellow;
        padding: 5px;
      }
      table tr td {
        color: rgb(82, 82, 82);
        text-align: center;
      }
    </style>
  </head>
  <body>
    <div id="tb">
      <caption>
        <h2>我的购物车</h2>
      </caption>
      <table>
        <tr>
          <td><input type="checkbox" id="all" value="全选" onclick="all_check()" /></td>
          <td>商品</td>
          <td>价格(元)</td>
          <td>数量</td>
        </tr>
        <tr>
          <td><input type="checkbox" name="Cfj" value="1" onclick="single_check()" /></td>
          <td>飞科吹风机</td>
          <td>79</td>
          <td>12</td>
        </tr>
        <tr>
          <td><input type="checkbox" name="Cfj" value="2" onclick="single_check()" /></td>
          <td>松下小吹筒家用大吹风机</td>
          <td>139</td>
          <td>9</td>
        </tr>
        <tr>
          <td><input type="checkbox" name="Cfj" value="3" onclick="single_check()" /></td>
          <td>博锐吹风机</td>
          <td>49</td>
          <td>14</td>
        </tr>
        <tr>
          <td><input type="checkbox" name="Cfj" value="4" onclick="single_check()" /></td>
          <td>飞利浦吹风机</td>
          <td>56</td>
          <td>6</td>
        </tr>
        <tr>
          <td colspan="4" onclick="tremove()"><button>删除选中项</button></td>
        </tr>
      </table>
      <script>
        var allcheck = document.getElementById('all')
        var oinput = document.getElementsByName('Cfj')
        var tb = document.getElementById('tb')

        function all_check() {
          for (var i = 0; i < oinput.length; i++) {
            if (allcheck.checked == true) {
              oinput[i].checked = true
            } else {
              oinput[i].checked = false
            }
          }
        }

        function single_check() {
          var j = 0
          for (var i = 0; i < oinput.length; i++) {
            if (oinput[i].checked == true) {
              j = j + 1
            }
          }
          if (j == oinput.length) {
            // 当所有的都被选中
            allcheck.checked = true
          } else {
            allcheck.checked = false
          }
        }
        function tremove() {
          for (var k = 0; k < oinput.length; k++) {
            if (oinput[k].checked == true) {
              tb.deleteRow(k + 1)
              k = -1
            }
          }
        }
      </script>
    </div>
  </body>
</html>

image.png

总结

经过这次对JavaScript的学习,回顾并加深了我对JavaScript的理解,且在使用JavaScript完成某个功能或解决问题的过程中体会到使用JavaScript有趣并奇妙的地方。同时,我了解到了,实现一个功能我们要尽可能通过不同的方式去实现,提高页面的展现效果和效率。