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

105 阅读5分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 3 天,作为一个前端基础一般的人,这几天的知识着实需要慢慢消化啊T^T

一、本堂课重点内容:

  • JavaScript的职能
  • 如何优化JavaScript代码
  • 通过三个代码实践分析优化思路

二、详细知识点介绍:

引入

  • HTML、CSS、JavaScript 应 各司其职,职能分离

  • 例子:主题切换

image.png

样式的变换避免直接写在JavaScript中,JS只负责行为,具体样式设置放在CSS中是最好的。

为什么:

  • 便于其他开发者理解

  • 便于维护

此外,纯展示类互动应追求零JS方案

组件封装

组件是指web页面上抽出来一个包含模板(HTML),功能(JavaScript),样式(CSS)的单元。

组件应该具备:

  • 封装性
  • 正确性
  • 扩展性
  • 复用性

以轮播图为例

  • 结构:HTML 列表

  • 表现:CSS 设置其样式,如过渡动画,重设排版

  • 行为:JavaScript API +自定义事件实现轮播,手动切换等

总结:基本方法:

  • 结构设计

  • 展现效果

  • 行为设计(包括功能和控制流)

重构组件

插件化

我感觉这一步就是函数封装的思想,将不同的功能细分后封装成不同的插件,然后注入组件,就能实现功能解耦,方便代码的维护调整

更进一步:HTML模板化

将各个功能及其构件进一步封装成模板,大大提高扩展性

再进一步:组件抽象化

抽象出通用模型,变成组件框架,进一步提高扩展性

老师提醒:各司其职不代表要各自写在不同文件中,而是说其本质负责的功能不混淆,各自负责自己的任务。

总结:

优化思路:二次重构

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

提高代码的复用性与扩展性

过程抽象

以高阶函数 (以函数为参数及返回值) once为例

为了能够让只执行一次的需求覆盖不同的事件处理,将这个需求剥离出来。这个过程称为过程抽象。

  • 我个人感觉还是函数封装的思想

高阶函数的作用

这里讲了很久纯函数和非纯函数,有点晕

例子:

利用高阶函数iterative将非纯函数转化为纯函数简化测试,(这点我没有听懂,我cpu过载了) 这一点真的抽象

总结

高阶函数可以减少非纯函数,尽量使用纯函数以保证过程的可预测性,提高代码的可维护性,减少错误。

编程范式:

  • 命令式:强调怎么做

  • 声明式:强调做什么

JavaScript同时具备两种特点

  • 命令式写法更简洁
  • 声明式写法更容易封装扩展。

不能单凭代码风格去评判代码好坏,要根据实际应用场景灵活运用,有时“笨办法”也很好用

  • 例子:2016年LeftPad事故

三、实践练习例子:

时间有限,所以只做了部分代码实践。

主题切换

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主题切换</title>
    <link href="CSS/1.css" type="text/css" rel="stylesheet">
</head>
<body>
<header>
    <button id="modeBtn"></button>
    <h1>JavaScript初尝试</h1>
</header>
<main>
    <div class="pict">
        <img src="kara-art.jpg" alt="" >
    </div>
    <div class="description">
        <p>
            本节课从实践维度解读在实际编码过程中何种类型的 JavaScript 代码
            称之为“好代码”,并从 JS 出发,总结其他语言编码可遵循的共性原则,
            由浅入深,该小节将集中讲解三大原则之一的“各司其职”原则。
        </p>
    </div>
</main>
<script src="JS/1.js"></script>
</body>
</html>
body,html {
    width: 100%;
    height: 100%;
    max-width: 600px;
    padding: 0;
    margin: 0;
    overflow: hidden;
}
body {
    padding: 10px;
    box-sizing: border-box;
    transition: all 1s;
}
div.pict img {
    width: 100%;
}

#modeBtn {
    font-size: 2rem;
    float: right;
    border: none;
    outline: none;
    cursor: pointer;
    background: inherit;
}
body.night {
    background-color: black;
    color: white;
    transition: all 1s;
}
#modeBtn::after {
    content: '☀';
}
body.night #modeBtn::after{
    content: '🌙';
}
const btn = document.getElementById('modeBtn');
btn.addEventListener('click',(e)=>{
    const body=document.body;
    if(body.className !== 'night'){
        body.className = 'night';
    } else {
        body.className = '';
    }
});

实现效果:

image.png

image.png

分红包(切西瓜法)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>红包</title>
    <link href="CSS/2.css" type="text/css" rel="stylesheet">
</head>
<body>
<header>
  <h1>红包模拟器</h1>
</header>
<main>
  <div id="setting">
    <div><label>红包金额:<input id="amount" value=100.00></label></div>
    <div><label>红包数量:<input id="count" value="10"></label></div>
    <div><button id="generateBtn">分红包</button></div>
  </div>
  <ul id="result">
  </ul>
</main>
<script src="JS/2.js"></script>
</body>
</html>
#setting button {
    margin-top: 20px;
    color: red;
}
#setting label{
    color: red;
}
#result {
    padding: 0;
    border: 2px solid #ec0505;
}

#result li {
    border: 2px solid #ec0505;
}
function generate(amount, count){
    let ret = [amount];

    while(count > 1){
        //挑选出最大一块进行切分
        let cake = Math.max(...ret),
            idx = ret.indexOf(cake),
            part = 1 + Math.floor((cake / 2) * Math.random()),
            rest = cake - part;

        ret.splice(idx, 1, part, rest);

        count--;
    }
    return ret;
}

const amountEl = document.getElementById('amount');
const countEl = document.getElementById('count');
const generateBtn = document.getElementById('generateBtn');
const resultEl = document.getElementById('result');

generateBtn.onclick = function(){
    let amount = Math.round(parseFloat(amountEl.value) * 100);
    let count = parseInt(countEl.value);

    let output = [];

    if(isNaN(amount) || isNaN(count)
        || amount <= 0 || count <= 0){
        output.push('输入格式不正确!');
    }else if(amount < count){
        output.push('钱不够分')
    }else{
        output.push(...generate(amount, count));
        output = output.map(m => (m / 100).toFixed(2));
    }
    resultEl.innerHTML = '<li>' +
        output.join('</li><li>') +
        '</li>';
}

代码参考月影老师的课程源码

切西瓜法可以避免直接用随机数时出现的先分了一部分红包,剩下的不够分的情况,它每次分都是基于“最大块”的“自我分离”,避免了出现溢出的情况。

效果:

image.png

四、课后个人总结:

难点:

  • 本节课个人认为最难懂是过程抽象中的高阶函数作用,那个iterative函数的部分很容易绕晕

总结:又是知识点满满的一天,讲了很多内容,但老师的讲解让我进一步地理解了代码优化的重要性。如何写好前端代码,首先要记住设计步骤:

  • 结构设计

  • 展现效果

  • 行为设计(包括功能和控制流)

其次是代码优化:

  • 插件化
  • 模块化
  • 抽象化

总而言之,要提高代码的可读性、复用性、扩展性。最重要一点是要根据应用场景灵活使用代码,不要墨守成规,哪种代码更适合就用哪种,不必过于追求无意义的优化。

文章如有错漏,请多多包涵

五、引用参考:

青训营-分红包 - 码上掘金 (juejin.cn)