分类: 笔记

  • js执行机制

    JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.

    单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务.

    这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉.

    同步和异步

    利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程.

    于是,JS 中出现了同步异步.

    同步

    前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。

    异步

    在做一件事的同时,还可以去处理其他事情.

    同步任务

    同步任务都在主线程上执行,形成一个执行栈

    异步任务

    JS 的异步是通过回调函数实现的


    一般而言,异步任务有以下三种类型:

    • 普通事件,如 click、resize 等
    • 资源加载,如 load、error 等
    • 定时器,包括 setInterval、setTimeout 等

    异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)

    异步JavaScript

    https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous/Introducing

    通用异步编程概念

    https://developer.mozilla.org/zh-CN/docs/conflicting/Learn/JavaScript/Asynchronous/Introducing

    执行机制

    • 先执行执行栈中的同步任务
    • 异步任务放入任务队列中
    • 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行

    主线程不断的重复获得任务、执行任务、再获取任务、再执行,这种机制被称为事件循环Event Loop

    并发模型与事件循环 – JavaScript | MDN

  • 定时器-延时函数

    setTimeout() / clearTimeout()

    语法:

    setTimeout(回调函数,延迟毫秒数)

    setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window

    清除延时函数

    let timer = setTimeout(回调函数,延迟毫秒数) //把定时器的ID给变量timer
    clearTimeout(timer) //通过ID清除定时器
    

    需要注意的是setTimeout()setInterval()共用一个编号池,技术上,clearTimeout()和 clearInterval()可以互换。但是,为了避免混淆,不要混用取消定时函数。

    同一个对象上(一个window或者worker),setTimeout()或者setInterval()在后续的调用不会重用同一个定时器编号。但是不同的对象使用独立的编号池。

    window.setTimeout – Web API 接口参考 | MDN

    结合递归函数让setTimeout()实现setInterval()的效果

    let i= 0
    function test(){
      i++
      setTimeout(test,1000)  //注意:setTimeout内的函数不需要写括号
      console.log(i);
    }
    test()
    

    两种定时器对比

    • setInterval() 的特征是重复执行,首次执行会延时
    • setTimeout() 的特征是延时执行,只执行 1 次
    • setTimeout() 结合递归函数,能模拟 setInterval() 重复执行
    • clearTimeout() 清除由 setTimeout() 创建的定时任务

    拓展

    console.log(111)
    
    setTimeout(function () {
      console.log(222)
    	}, 0)
    
    console.log(333)
    
    //结果 111 333 222
    

    详见js执行机制

  • BOM

    BOM ( Browser Object Model ) 是浏览器对象模型

    window 对象下包含了 navigatorlocationdocumenthistoryscreen 5个属性,即所谓的 BOM (浏览器对象模型)

    • 全局变量是 window 对象的属性
    • 全局函数是 window 对象的方法

    注:依附于 window 对象的所有属性和方法,使用时可以省略 window

    Window – Web API 接口参考 | MDN

  • resize事件

    文档视图调整大小时会触发 resize 事件.

    resize事件仅在window对象上触发.

    只有在window对象上注册的处理程序才会接收resize事件.

    window.addEventListener('resize',function(){})

    检测屏幕宽度

    window.addEventListener('resize',function(){
    	let w = document.documentElement.clientWidth
    	console.log(w)
    })

    Window: resize event – Web APIs | MDN

  • scroll/offset/client获取宽高、位置

    scroll

    获取宽高

    scrollWidth

    只读

    Element.scrollWidth 这个只读属性是元素内容宽度的一种度量,包括由于overflow溢出而在屏幕上不可见的内容。

    scrollWidth的值等于内容的width + padding(包含伪元素,不包含滚动条)

    注意是内容的宽度,包括溢出的内容,无论是否隐藏溢出的内容.(scrollHeight同理)

    Element.scrollWidth – Web API 接口参考 | MDN

    scrollHeight

    只读

    Element.scrollHeight 这个只读属性是一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。

    scrollHeight的值等于内容的height + padding(不包含滚动条)

    Element.scrollHeight – Web API 接口参考 | MDN

    获取位置

    scrollTop

    读 / 写

    Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数.

    • 一个元素的 scrollTop 值是这个元素的内容顶部(卷起来的)到它的视口可见内容(的顶部)的距离的度量。
    • 当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0.

    简单理解:向下滚动多少

    例:

    //检测页面滚动的头部距离(被卷去的头部)
    document.documentElement.scrollTop
    

    Element.scrollTop – Web API 接口参考 | MDN

    scrollLeft

    读 / 写

    Element.scrollLeft 属性可以读取或设置元素滚动条到元素左边的距离。

    Element.scrollLeft – Web API 接口参考 | MDN

    offset

    获取宽高

    offsetWidth / offsetHeight

    只读

    offsetWidth是测量包含元素的边框(border)、内边距(padding)、滚动条(scrollbar)(如果存在的话)、以及CSS设置的宽度(width)的值,不包含伪元素.

    它等于元素实际展现的宽度 / 高度

    例:

    <!-- css -->		
    <style>
    .box {
    	width: 100px;
    	height: 100px;
    	padding: 10px;
    	border: 1px solid #333;
    }
    </style>
    
    <!-- html -->
    <div class="box"></div>
    
    <!-- js -->
    <script>
    	let box = document.querySelector('.box')
      console.log(box.offsetWidth)   // 122 = width + padding + border
    	console.log(box.offsetHeight) // 122 =  height + padding + border
    </script>
    

    HTMLElement.offsetWidth – Web API 接口参考 | MDN

    HTMLElement.offsetHeight – Web API 接口参考 | MDN

    获取位置

    offsetTop / offsetLeft

    只读

    获取元素距离自己定位父级元素的上 / 左距离

    如果都没有则以文档左上角为准

    拓展阅读:HTMLElement.offsetParent – Web API 接口参考 | MDN

    client

    获取宽高

    clientWidth / clientHeight

    只读

    获取元素的可见部分宽高(不包含边框,滚动条等)

    Element.clientWidth – Web API 接口参考 | MDN

    获取位置

    clientTop / clientLeft

    只读

    获取上边框border和左边框border宽度

  • 滚动事件与加载事件

    滚动事件

    事件名:scroll

    文档视图或者一个元素在滚动时,会触发元素的scroll事件。

    例:

    //监听整个页面滚动
    window.addEventListener('scroll',function(){})
    //给 window 或 document 添加 scroll 事件
    

    监听某个元素的内部滚动直接给某个元素加即可.

    加载事件

    加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件.

    事件名:load

    当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发load事件。

    它与DOMContentLoaded不同,后者只要页面DOM加载完成就触发,无需等待依赖资源的加载。

    例:

    //监听页面所有资源加载完毕
    window.addEventListener('load',function(){})
    

    注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件

    load | MDN

    事件名:DOMContentLoaded

    document添加

    当纯HTML被完全加载以及解析时,DOMContentLoaded 事件会被触发,而不必等待样式表,图片或者子框架完成加载。

    //监听页面DOM加载完毕:
    //给 document 添加 DOMContentLoaded 事件
    document.addEventListener('DOMContentLoaded',function(){})

    Document: DOMContentLoaded 事件 | MDN

  • transform变换后仍占据原来空间

    HTML

    <div class="wrapper">
    	<div></div>
    </div>

    CSS

    .wrapper {
    	width: 500px;
    	height: 100px;
    	border: 1px solid #000;
    	margin: 20px auto 0;
    }
    .wrapper div {
    	width: 100px;
    	height: 100px;
    	transform: translateX(400px);
    	background: lightblue;
    	transition: all 0.5s;
    }
    .wrapper div:hover {
    	transform: translateX(0px);
    }
    

    如图,元素通过translate位移改变位置后,仍占据原空间,transform: translateX(0px)可以使其回到原位.

  • 针对transform中的几种值的先后顺序

    HTML代码:

    <div class="wrapper">
    	<div></div>
    	<div class="div1">div1</div>
    	<div class="div2">div2</div>
    	<div class="div3">div3</div>
    </div>

    translate和scale

    CSS代码:

    div {
    	width: 100px;
    	height: 100px;
    	background: tomato;
    }
    .wrapper {
    	width: 500px;
    	height: 400px;
    	border: 1px solid #000;
    	margin: 20px auto 0;
    	background: none;
    }
    .div1 {
    	transform: translateX(400px);
      background: lightblue;
    }
    .div2 {
    	transform: translateX(400px) scale(0.5);
      background: pink;
    }
    .div3 {
    	transform: scale(0.5) translateX(400px);
      background: lightcoral;
    }

    translateX(400px)

    translateX(400px) scale(0.5)

    scale(0.5) translateX(400px)

    将光标放在该区域内即可播放(移动端点击该区域)

    (代码作了适配处理,以实际效果为准)

    如上,默认的位移和形变的基点是中心,所以我们按照盒子的中心来做位移的衡量基点。第一个盒子为初始位置,第二个第三个都到了我们想要的位置(位移为400px),但是第四个盒子就出现了问题,位移为200px。

    按照常理,不管scale和translate的顺序如何,都应该达到我们期望的位置,但是如今我们发现如果先位移,后缩放,则可以达到我们期望的位置,但是当顺序反过来,即先缩放,后位移的话,则会将位移的步长同时缩小相应的倍数(这里我们缩放到1/2,所以相应的位移从原来的400px缩放到200px)。

    translate和rotate

    div{
        width:100px;
        height: 100px;
        background: tomato;
        transition: 1s all ease;
    }
    .wrapper{
        width:500px;
        height: 600px;
        border:1px solid #000;
        margin:20px auto 0;
        background: none;
    }
    .div1{
        transform: translateX(200px);
        background: lightblue;
    }
    .div2{
        transform: translateX(200px) rotate(45deg);
        background: pink;
    }
    .div3{
        transform: rotate(45deg) translateX(200px);
        background: lightcoral;
        }
    

    transform: translateX(200px)

    transform: translateX(200px) rotate(45deg)

    transform: rotate(45deg) translateX(200px)

    将光标放在该区域内即可播放(移动端点击该区域)

    (代码作了适配处理,以实际效果为准)

    如图,第二个第三个盒子同样达到了我们的预期,都向右移动了200px;但是第四个盒子又出现了问题,我们给的水平位移,但是在竖直方向也有了位移,不仅如此,向右移动的距离也没有200px.

    其实,当我们先旋转的时候,对于盒子的坐标系也旋转了.

    总结

    当我们在旋转后再进行位移的时候,其实是按照旋转后的坐标系位移,在我们视觉中就产生了向右位移了一部分,向下移动了一部分。

    总而言之,在transform中,当我们同时有位移和其他属性的时候,记得要将位移放到最前,先位移到我们期望的位置,然后再进行其他的形变。

    版权声明:本文为CSDN博主「crazyJialin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

    原文链接:https://blog.csdn.net/crazy_jialin/article/details/77969990

  • 事件委托

    • 优点:给父级元素加事件(可以提高性能)
    • 原理:事件委托其实是利用事件冒泡的特点,给父元素添加事件,子元素可以触发
    • 实现:事件对象.target 可以获得真正触发事件的元素

    小案例

       <div class="father">
    	<p class="son">第1个p标签</p>
                         ···
                         ···
    	<p class="son">第6个p标签</p>
       </div>
    
       <!-- js -->
       <script>
       let father = document.querySelector('.father')
    
       father.addEventListener('click', function (e) {
    
    	e.target.style.color = 'lightblue'
    
       })
       </script>
  • 事件流

    事件流指的是事件完整执行过程中的流动路径

    捕获阶段是从父到子 / 冒泡阶段是从子到父

    事件冒泡

    当一个元素的某一事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。

    简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件

    事件冒泡默认存在

    div --> ··· --> body --> html --> document

    <div class="father">
        <div class="son"></div>
    </div>
    
    <script>
        let fa = document.querySelector('.father')
        let son = document.querySelector('.son')
        //father
        fa.addEventListener('click', function () {
            alert('father')
        })
      	//son
        son.addEventListener('click', function () {
            alert('son')
        })
      	//grandfather
        document.addEventListener('click', function () {
            alert('grandfather')
        })
      
    //点击son  son --> father --> grandfather
    </script>
    

    事件捕获

    从DOM的根元素开始去执行对应的事件 (从外到里)

    document --> html --> body --> ··· --> div

    	btn.addEventListener('click' , function () {} , true)
    //btn.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
    
    • addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
    • 若传入false代表冒泡阶段触发,默认就是false
    • 若是用 L0 事件监听,则只有冒泡阶段,没有捕获

    阻止事件流动

    事件对象.stopPropagation()

    • 若想把事件就限制在当前元素内,就需要阻止事件流动
    • 阻止事件流动需要拿到事件对象
    • 此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效

    鼠标经过事件

    • mouseover 和 mouseout 会有冒泡效果
    • mouseenter 和 mouseleave 没有冒泡效果(推荐)

    阻止默认行为,比如链接点击不跳转,表单域的不提交

    事件对象.preventDefault()


    两种注册事件的区别

    传统on注册(L0)

    • 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
    • 直接使用null覆盖偶就可以实现事件的解绑
    • 都是冒泡阶段执行的

    事件监听注册(L2)

    • 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
    • 后面注册的事件不会覆盖前面注册的事件(同一个事件)
    • 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
    • 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
    • 匿名函数无法被解绑