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

65 阅读5分钟

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

跟着月影大佬学习如何写好JavaScript

js入门:犀牛书和红宝书

一、本堂课重点内容:

  • 写好js的原则
  • html/css/js各司其职
  • 组件的定义解析及特征
  • 组件封装基本方法
  • 原生js实现轮播图
  • 过程抽象概念
  • 高阶函数使用模式
  • js编程范式

二、详细知识点介绍:

  • 写好js的原则: 各司其职、组建封装、过程抽象

各司其职

看案例

做到各司其职,避免不必要的用js直接操作样式,可以用class表示状态,纯展示类交互寻求零js方案

组件封装

组件是web页面上抽出来一个个包含模板、功能和样式的单元。封装性、正确性、扩展性、复用性

案例:轮播图

结构:列表ul>li

表现:css绝对定位使图片重叠、轮播图切换状态用modifier、切换动画用transition

行为:API: getSelectedItem、getSelectedItemIndex、slideTo、slideNext、slidePrevious;控制流:用自定义事件来解耦(给每个圆点加controller)

改进:

  • 插件化,插件和组件之间通过依赖注入建立联系slider.registerPlugins(pluginController, pluginPrevious, pluginNext)
  • html模板化,js数据驱动形成html模板,在class Slider里写render函数;
  • 抽象化,抽成通用组件

image.png

优化没有止境,重点是组件封装和组件化的思想。

过程抽象

函数式编程

once函数-只执行一次-事件处理逻辑抽象-需求剥离 -> 过程抽象

典型应用:react hooks

js高阶函数

高阶函数的理念是变量状态不可变,一切都可以是函数,参数可以是函数,普通函数的话,通常会通过改变变量的状态,去实现业务逻辑,循环是通过for,while等循环语法,而纯函数式是通过递归自我调用达到循环。

高阶函数用于函数式编程,函数为参数,函数为返回值,常作为函数的修饰器。防抖、节流、可迭代等,以HOF0为基础

image.png

节流:固定时间触发,防抖:每隔一段时间触发一次,能节省开销。

纯函数测试简单,非纯函数需要定义context,要减少非纯函数的数量,高阶函数可以减少

声明式编程更有扩展性。-案例:toggle

image.png

代码规范

关注风格、效率、约定、使用场景、设计

案例:left-pad事件

三、实践练习例子:

  • 深色浅色页面控制-新手版本,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='🌞';
        }
    }); 
    
  • 深色浅色页面控制-优化版本,js控制classname,css控制class的样式,各司其职

    const btn = document .getElementById( ' modeBtn' );
    btn. addEventListener( 'click', (e) => {
        const body = document . body;
        if ( body. className !== 'night') {
            body. className =' night ' ;
        } else {
            body. className =   '';
        }
    }); 
    
  • 深色浅色页面控制-深优化版本,无js,用css高级功能实现切换,更各司其职

    html:
    <input id="modeCheckbox" type="checkbox">
    <div class="content">
        <label id="modebtn" for="modeCheckbox"></label>
    </div>
    css:伪类选择器
    #modeCheckBox {display:none;}
    #modeCheckBox:checked + .content{
        background:black;
        color:white;
        transition: all 1s;
    }   
    #modebtn::after{content:'🌞';}
    #modeCheckBox:checked + .content #modebtn::after{content:'🌛';}
    
  • 轮播图基础版(构造函数里加自定义事件监听器

    this . container .addEventListener('slide', evt =>{
        const idx =evt . detail. index;
        const selected = controller. querySelector(' .slide-list_control-buttons--selected'); 
        if(selected)  selected.className ='slide-list_control-buttons' ;
        buttons[idx].className = 'slide-list_control-buttons--selected';
    });
    
  • 轮播图解耦版

    class Slider{
    constructor(id, opts = {images:[], cycle: 3000}){
        this . container = document. getElementById(id);
        this. options = opts;
        this . container. innerHTML = this. render();
        this.items = this. container. querySelectorAll('.slider-list_item');
        this.cycle = opts.cycle|| 3000;
        this.slideTo(0);
    }
    render(){
        const images = this. options . images ;
        const content = images . map( image =>`
        <li class="slider-list_item"> 
            <img src="$(image)">
        </li>
        `.trim());
        
        return `<ul>$(content.join('')}</ul>` ;
    }
    ……
    }
    
  • 轮播图抽象化

    image.png

  • once

    function once(fn) {
    //outer scape cLosure... .
        return function (...args) {
            //inner scope
            if(fn) {
                const ret = fn. apply(this, args);
                fn = null;
                return ret ; 
            }
        }}
     button. addEventListener('click' , once( ( evt )=>{
        const target = evt. target;
        target. parentNode.className = ' completed';
        setTimeout(() => {
            list. removeChild( target . parentNode);
        }, 2000 );
      }));
    
  • 可迭代

    const isIterable = obj => obj != null 
     && typeof obj[Symbol.iterator] === 'function';

   function iterative(fn) {
     return function(subject, ...rest) {
       if(isIterable(subject)) {
         const ret = [];
         for(let obj of subject) {
           ret.push(fn.apply(this, [obj, ...rest]));
         }
         return ret;
       }
       return fn.apply(this, [subject, ...rest]);
     }
   }

   const setColor = iterative((el, color) => {
     el.style.color = color;
   });

   const els = document.querySelectorAll('li:nth-child(2n+1)');
   setColor(els, 'red');
  • 交通灯

        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' );
                avait wait(3000); 
                setState ('pass');
                await wait(3000);
            }
        }
        start();
    

    交通灯课上讲了好几个版本,我们在实践中要结合使用场景进行抽象。

四、课后个人总结:

  • 本章有什么知识点不容易掌握? 组件化思想、函数式编程、高阶函数还要多练习

    学会用移位操作,a&(a-1)可以让a的二进制数少一个1

    写优雅的代码,算法思想,工程思维

  • 什么地方容易与其他内容混淆? 案例讲解里关于洗牌和分红包的概率问题,洗牌注意最基础的做法是要从前面随机抽一张放在最后,然后从剩余部分随机抽一张放在最后。分红包问题可以用抽牌思想,给序列里随机插入几个分隔符

  • 组件化思想:

1. 使用css trasition opactiy等去实现效果。
2. 通过class 去封装各个方法实现效果,hook封装。 
3. 通过自定义事件进行优化
4. 通过用户去传入相应参数,函数内部实现功能,达到插件化,具有灵活性。
5. 将html也通过JS去生成,再利用插件化的特点,需要一个根元素,达到模块化效果
6. 组件抽象化,通过类继承,类数据共享,利用必包数据私有化,最里层类嵌套函数相互关联,再包一层向外暴露,
这样就可以实现实例共享原型方法和属性也能通过传参到达内部函数,串联数据,数据进行响应。

    ——cite mingdu明渡的课堂评论

五、引用参考:

  • 我参考了哪些外部博客/笔记/文章?
  • 文章中有什么地方是我参考引用了外部博客/笔记/文章的?