资讯专栏INFORMATION COLUMN

360前端星学习笔记-如何写‘好’JavaScript

arashicage / 2382人阅读

摘要:前言如何写好这门课是由技术专家月影老师讲的。控制流设计原则为什么要用到事件机制呢因为要降低结构之间的耦合度,如果不这样做的话,我们需要做双向的操控的。

前言

《如何写‘好’javascript》这门课是由360技术专家月影老师讲的。

这堂课的ppt

说实话,我一直在纠结要不要写关于js的文章,因为对于js来说,我的实际经验不足,更不要说面向对象编程与函数式编程了,对于过程抽象与行为抽象也没有深入的理解,但想想还是觉得应该分享出来,并且我尽量原汁原味的阐述这门课的内容,尽量不加入自己主观理解,因为对于没有实际经验的我来说,如果添加自己主观的理解只能误导读者,好了,不费话了~

一、关灯吃面

需求:

点击红色按钮

背景变成黑色

字体color由黑色变成白色

红色按钮变成绿色

1.1 版本1
light.onclick = function(evt) {
  if(light.style.backgroundColor !== "green"){
    document.body.style.backgroundColor = "#000";
    document.body.style.color = "#fff";
    light.style.backgroundColor = "green";
  }else{
    document.body.style.backgroundColor = "";
    document.body.style.color = "";
    light.style.backgroundColor = "";    
  }
}

对于我来说,要是让我完成这个需求,大概应该就写成这样吧^_^,

想想这样写好不好呢?

答案肯定是不好的。

这样写的问题:

用js直接去修改了元素的样式。

并且代码只能看出修改了一些元素的样式,看不出这坨代码需要完成哪些需求。

假设:如果以后想改需求了,比如开灯时字体变为红色,或者需要添加一些功能,那我就得去重新看代码,去改这一坨代码,这样的话,维护起来就非常难。

1.2 版本2:
lightButton.onclick = function(evt) {
  if(main.className === "light-on"){
    main.className = "light-off";
  }else{
    main.className = "light-on";
  }
}

这回代码语义化就比较强了,通过js去修改className而不是用js来直接修改style,这样写会比较好一点。

1.3 版本3:其他思路

    今天回到家,
    煮了点面吃,
    一边吃面一边哭,
    泪水滴落在碗里,
    没有开灯。
    

这么写的思路就是不使用js,而是通过input和label关联来切换状态。

二、复杂的UI组件的设计

这是大家最熟悉不过的轮播图组件了,如果用面向过程的写法,可能会出现很多bug,那么如何实现才是最好的呢?

2.1 步骤1:整体思路

整体思路

图片结构是一个列表型结构,所以主体用

  • 使用 css 绝对定位将图片重叠在同一个位置

    轮播图切换的状态使用修饰符(modifier)

    轮播图的切换动画使用 css transition

    2.2 步骤2: API设计

    具体实现:

    class Slider{
      constructor(id){
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll(".slider-list__item, .slider-list__item--selected");
      }
      // 获得当前元素
      getSelectedItem(){
        const selected = this.container.querySelector(".slider-list__item--selected");
        return selected
      }
      // 获得当前元素的索引
      getSelectedItemIndex(){
        return Array.from(this.items).indexOf(this.getSelectedItem());
      }
      // 切换到第index张图片
      slideTo(idx){
        const selected = this.getSelectedItem();
        if(selected){ 
          selected.className = "slider-list__item";
        }
        const item = this.items[idx];
        if(item){
          item.className = "slider-list__item--selected";
        }
      }
      // 切换到下一张图片
      slideNext(){
        const currentIdx = this.getSelectedItemIndex();
        const nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
      }
      // 切换到上一张图片
      slidePrevious(){
        const currentIdx = this.getSelectedItemIndex();
        const previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);  
      }
    }
    // 通过new来实例化
    const slider = new Slider("my-slider");
    setInterval(() => {
      slider.slideNext()
    }, 3000)
    2.3 步骤3:控制流设计 (下方小圆点与左右按钮设计)

    控制结构

    
    
    

    自定义事件

        const detail = {index: idx}
        const event = new CustomEvent("slide", {bubbles:true, detail})
        this.container.dispatchEvent(event)

    因为下方原点与图片自动切换的下标(index)是一致的,所以可以通过事件机制,在图片slide时候直接给container派发一个事件,这样的话呢,通过container去监听这个事件,去更新控制结构上小圆点的状态。

    具体实现:

    class Slider{
      constructor(id, cycle = 3000){
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll(".slider-list__item, .slider-list__item--selected");
        this.cycle = cycle;
    
        const controller = this.container.querySelector(".slide-list__control");
        if(controller){
          const buttons = controller.querySelectorAll(".slide-list__control-buttons, .slide-list__control-buttons--selected");
          controller.addEventListener("mouseover", evt=>{
            const idx = Array.from(buttons).indexOf(evt.target);
            if(idx >= 0){
              this.slideTo(idx);
              this.stop();
            }
          });
          
          controller.addEventListener("mouseout", evt=>{
            this.start();
          });
          // 监听slide事件
          this.container.addEventListener("slide", evt => {
            // 拿到slide事件传来的index
            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";
          })
        }
        
        const previous = this.container.querySelector(".slide-list__previous");
        if(previous){
          previous.addEventListener("click", evt => {
            this.stop();
            this.slidePrevious();
            this.start();
            evt.preventDefault();
          });
        }
        
        const next = this.container.querySelector(".slide-list__next");
        if(next){
          next.addEventListener("click", evt => {
            this.stop();
            this.slideNext();
            this.start();
            evt.preventDefault();
          });
        }
      }
      getSelectedItem(){
        let selected = this.container.querySelector(".slider-list__item--selected");
        return selected
      }
      getSelectedItemIndex(){
        return Array.from(this.items).indexOf(this.getSelectedItem());
      }
      slideTo(idx){
        let selected = this.getSelectedItem();
        if(selected){ 
          selected.className = "slider-list__item";
        }
        let item = this.items[idx];
        if(item){
          item.className = "slider-list__item--selected";
        }
      
        const detail = {index: idx}
        const event = new CustomEvent("slide", {bubbles:true, detail})
        this.container.dispatchEvent(event)
      }
      slideNext(){
        let currentIdx = this.getSelectedItemIndex();
        let nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
      }
      slidePrevious(){
        let currentIdx = this.getSelectedItemIndex();
        let previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);  
      }
      start(){
        this.stop();
        this._timer = setInterval(()=>this.slideNext(), this.cycle);
      }
      stop(){
        clearInterval(this._timer);
      }
    }
    
    const slider = new Slider("my-slider");
    slider.start();

    这个实现的构造函数会复杂一些,但是把timer定时器也封装进去了,会有轮播的时间默认为3秒钟,同样的也是获得container,items,cycle(时间)通过事件机制将控制流中的小圆点与图片联动起来。并且还判断了controler是否存在,假如以后我们不需要小圆点这个功能了,我们只需要把html中相关的结构去掉,js也不会报错,但是这里还有一个优化的点就是slider与controler之间有着比较强的耦合度。

    2.4 控制流设计原则

    为什么要用到事件机制呢?因为要降低结构之间的耦合度,如果不这样做的话,我们需要做双向的操控的。

    举个栗子

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108041.html

相关文章

  • 360前端学习笔记-HTML

    摘要:前端与一枚大三学生,非常感谢前端星计划,在这里学习了很多,非常幸运获得的校招实习,非常感谢面试我的王峰老师和卢岳文老师总的来说,这天的学习,让我坚定了走前端这条路。使用在模式下可以使元素水平居中,但在模式下却会失效。 前端与HTML 一枚大三学生,非常感谢360前端星计划,在这里学习了很多,非常幸运获得360的校招实习offer~,非常感谢面试我的王峰老师和卢岳文老师!总的来说,这7天...

    kuangcaibao 评论0 收藏0
  • 360前端学习笔记-深入CSS

    摘要:深入课程链接一的版本规范在之前,把所有标准放在一起去管理,这样推进起来版本升级比较难,后来在的版本中,将标准分成几个模块来管理。 深入CSS 课程ppt链接 一、CSS的版本(level) css Level 1 css Level 2(CSS2.2规范) css Level 3 Color Module Level 3 Selectors Level 3 Media Queri...

    xcold 评论0 收藏0
  • 360前端学习笔记-深入css(2)

    摘要:课程一继承某些元素会自动继承其父元素的计算值举例上述代码,标签里的就会继承父元素的,为。显示继承给设置显示继承根元素下所有元素除独自设置如的都是。二初始值当向上继承到顶点还是没找到值的话,就需要初始值了。 课程ppt 一、css继承 1.1 某些元素会自动继承其父元素的计算值 举例: This is a test of inherit. p { color: #666; ...

    William_Sang 评论0 收藏0
  • 360前端负责人月影:赛跑项目和跳水项目的金牌含量其实一样

    摘要:不久,传说中的月影大大进入了视线。目前担任奇虎副总监技术委员会委员兼前端技术委员会主席,前端最大团队奇舞团负责人,顾问。图灵访谈我知道月影大大在前端方面特别有名,图灵社区的好多留言也都感叹终于有机会访谈到月影大大了。 本文仅用于学习和交流,不用于商业目的。非商业转载请注明作译者、出处,并保留本文的原始链接:http://www.ituring.com.cn/Art... 编者语 通往...

    taohonghui 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<