zoukankan      html  css  js  c++  java
  • 深入浅出原生JS:One

    Arguments 对象:

    在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。

    例如,在函数 sayHi() 中,第一个参数是 message。用 arguments[0] 也可以访问这个值,即第一个参数的值(第一个参数位于位置 0,第二个参数位于位置 1,依此类推)。

    可以用于模拟函数重载:

    function doAdd() {
      if(arguments.length == 1) {
    //但只有一个参数的时候,加5 alert(arguments[
    0] + 5); } else if(arguments.length == 2) {
    //但有两个参数的时候,这两个参数相加 alert(arguments[
    0] + arguments[1]); } } doAdd(10); //输出 "15" doAdd(40, 20); //输出 "60"

    虽然不如重载那么好,不过已足以避开 ECMAScript 的这种限制。

    eval() 函数:函数可计算某个字符串,并执行其中的的 JavaScript 代码;表示JavaScript表达式,语句或一系列语句的字符串。表达式可以包含变量以及已存在对象的属性。

    在非严格的js模式下:

    var x = 2;
    console.log(eval("var x = 5; x")); //5
    console.log(x);            //5

    在严格模式下:

    "use strict";
    var x = 2;
    console.log(eval("var x = 5; x"));//5
    console.log(x);           //2

    正常模式下,eval语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval内部。

    eval() 通常比替代方法慢,因为它必须调用 JS 解释器,而许多其他结构则由现代 JS 引擎进行优化。 

    箭头函数和普通函数的区别:

    箭头函数属于匿名函数,匿名函数是要通过赋值语句赋值给变量,这个赋值的过程是在代码执行阶段进行的,不是在声明阶段,所以没有函数声明的提升属性。

    箭头函数中的 this 和调用时的上下文无关,而是取决于定义时的上下文:

    function make () {
      return ()=>{  
        console.log(this);  
      }  
    }  
    var testFunc = make.call({name:'foo'});
    testFunc(); //{ name: 'foo' }
    testFunc.call({name:'bar'}); //{ name: 'foo' }
    //如果是普通函数的话,结果就是{ name: 'bar'}

    这个例子可以看到,确实箭头函数在定义之后,this 就不会发生改变了,无论用什么样的方式调用它,this 都不会改变;但严格来说,这并不是“取决于定义时的上下文”, 因为箭头函数根本就没有绑定自己的 this,在箭头函数中调用 this 时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个 this 拿来使用罢了;在普通函数中,会自动绑定上的各种局部变量,箭头函数都是十分单纯的沿着作用域链向上寻找.

    当然普通函数也可以实现和箭头函数一样的效果:

    function make () {
        //保存当前作用域的this
      var self = this;
      return function () {
        console.log(self);
      }
    }
    
    function make () {
      return function () {
        console.log(this);
      }.bind(this);
      //绑定当前作用域的this
    }
    var testFunc = make.call({name:'foo'});
    testFunc();         //{ name: 'foo' }
    testFunc.call({name:'bar'});//{ name: 'foo' }

     JS中的冒泡流和捕获流

    addEventListener第三个参数useCapture ,true时为捕获,false时为冒泡

    冒泡从目标对象开始,向父级元素至window传递;捕获从window底层逐级至目标对象传递!

    事件捕获
    当你使用事件捕获时,父级元素先触发,子级元素后触发,即div先触发,p后触发。

    事件冒泡
    当你使用事件冒泡时,子级元素先触发,父级元素后触发,即p先触发,div后触发。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    
    </head>
    <body>
        <div id="divAlert" onclick="divAlert();">
            <div onclick="anothor();"></div>
            <input type="button" id="btn" value="click">
        </div>
    </body>
    <script type="text/javascript">
            let btn = document.getElementById('btn');
            let divParent = document.getElementById('divAlert');
        const anothor = () => {
                 alert('you click this same level');
            }
            const alertMessage = (e) => {
                // e.stopPropagation();
                alert('you click this button');
            }
            const divAlert = () => {
                 alert('you click this div');
            }
            //divParent.addEventListener('click',divAlert,true);
            btn.addEventListener('click',alertMessage,false);
    </script>
    </html>

    执行顺序是:

    alert('you click this button'); 
    alert('you click this div');
    当我们把false改成true试下:
    执行顺序:
    alert('you click this button'); 
    alert('you click this div');
    说好的true是捕获呢,这是因为上一个div没有设置addEventListener,再试下:
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
        <div id="divAlert" onclick="divAlert();">
            <div onclick="anothor();"></div>
            <input type="button" id="btn" value="click">
        </div>
    </body>
    <script type="text/javascript">
            let btn = document.getElementById('btn');
            let divParent = document.getElementById('divAlert');
            const alertMessage = (e) => {
                //e.stopPropagation();
                alert('you click this button');
            }
            const divAlert = (e) => {
                 alert('you click this div');
            }
            divParent.addEventListener('click',divAlert,true);
            btn.addEventListener('click',alertMessage,true);
    </script>
    </html>

    执行结果是:

    alert('you click this div');
    alert('you click this button');
    alert('you click this div');
    刚开始的结果是没错的,div先然后子元素,但是为什么是三次呢?
    第一次:button触发了捕获事件弹出的,这时就触发了他本身的click事件但由于事件的优先级不同,所以没有再次弹出
    第二次:捕获从window底层逐级至目标对象,因为这里只要一个父级绑定了click,所以现在是button自身的click。
    第三次:就是第一次时触发了div本身的click事件
    但我们只想要前两个提示怎么办,把上面的注释去掉就可以了,就是第二次弹出后,强制阻止事件捕获或冒泡。
    结果当然就是我们所期望的。

    这里介绍stopImmediatePropagation() 和 stopPropagation()

    后者只会阻止冒泡或者是捕获。 但是前者除此之外还会阻止该元素的其他事件发生,但是后者就不会阻止其他事件的发生。

    这里的逻辑比较简单就用不到。

    JS中的鼠标事件:

    鼠标事件是JS最经常用的事件,其中:

    onmousedown:在用户按下任何鼠标时触发

    onmousemove:当鼠标指针在元素内部移动时重复地触发

    onmouseup:会在鼠标按键被松开时发生

    我们来做个例子来看下各个事件时怎么触发的:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
        #dragBox {
            width: 400px;
            height: 250px;
            background: deepskyblue;
            /* 这个是关键,没有这个的话,子元素的margin:10px auto;不起作用*/
            overflow: hidden;
            position: absolute;
         /*为了一开始强行让它在页面中居中*/ top: 50%; left: 50%; /* absolute居中 */ /* 宽度的一半 */ margin-left: -200px; /* 高度的一半 */ margin-top: -125px; } .target { box-sizing: border-box; width: 50%; height: 50px; background-color: deeppink; margin: 10px auto; overflow: hidden; text-align: center; line-height: 50px; } .target:hover { cursor: pointer; } </style> <body> <div id="dragBox"> <div class="target"> 可拖动区域 </div> </div> </body> </html>

     接下来就是写JS了,获取class的话一般都会想到document.getElementsByClassName(''),但是这个效率感人,所以我们自己写个效果差不多的函数,但效率较高:

    const getByClass = (clsName, parent) => {
                    let oParent = parent ? document.getElementById(parent) : document,
                        eles = [],
                        elements = oParent.getElementsByTagName('*');
        
                    for (let i = 0; i < elements.length; i++) {
                        if (elements[i].className == clsName) {
                            eles.push(elements[i]);
                        }
                    }
                    return eles;
                }

    接下来写鼠标拖动这个div的JS:

        <script>
                //找出className想符合的元素集可以指定父级元素的ID,速度会更快点
                const getByClass = (clsName, parent) => {
                    let oParent = parent ? document.getElementById(parent) : document,
                        eles = [],
                        elements = oParent.getElementsByTagName('*');
        
                    for (let i = 0; i < elements.length; i++) {
                        if (elements[i].className == clsName) {
                            eles.push(elements[i]);
                        }
                    }
                    return eles;
                }
        
                function fnDown(event) {
                    event = event || window.event;
                    console.log(event);
                    let dragBox = document.getElementById('dragBox'),
                        //计算出盒子和左边框的距离,因为要设置absolute又要让他居中,
                        //所以我设置了margin所以这边得减掉marginLeft和marginTop
                        relativeBoxX = event.clientX - dragBox.offsetLeft - dragBox.offsetWidth/2,
                        relativeBoxY = event.clientY - dragBox.offsetTop - dragBox.offsetHeight/2;
                    document.onmousemove = function (event) {
                        event = event || window.event;
                        fnMove(event, relativeBoxX, relativeBoxY);
                    }
                    document.onmouseup = function(){
                       document.onmousemove = null;
                       document.onmouseup = null;
                    }
                }
        
                const drag = () => {
                    let onTitle = getByClass('target', 'dragBox')[0];
                    let dragBox = document.getElementById('dragBox');
                    onTitle.onmousedown = fnDown;
                }
        
        
        
                const fnMove = (e, Posx, Posy) => {
                    let l = e.clientX - Posx,
                        t = e.clientY - Posy,
                        oDrag = document.getElementById('dragBox'),
                        //窗口的宽度
                        winW = document.documentElement.clientWidth || documentb.body.clientWidth,
                        //窗口的高度
                        winH = document.documentElement.clientHeight || document.body.clientHeight,
                        //oDrag.offsetWidth是当前div宽度
                        //oDrag.offsetHeight是当前div高度
                        maxW = winW - oDrag.offsetWidth,
                        maxH = winH - oDrag.offsetHeight;
                        //为了不让div上下左右边框超过屏幕,提高用户体验
                        
                    //跟上面一样,这边因为div是向右偏移
                    //所以要加上marginLeft和marginTop
                    if (l < 0 + dragBox.offsetWidth/2) {
                        l = 0 + dragBox.offsetWidth/2;
                    }
                    else if (l > maxW + dragBox.offsetWidth/2) {
                        l = maxW + dragBox.offsetWidth/2;
                    }
                    if (t < 0 + dragBox.offsetHeight/2) {
                        t = 0 + dragBox.offsetHeight/2;
                    } else if (t > maxH + dragBox.offsetHeight/2) {
                        t = maxH + dragBox.offsetHeight/2;
                    }
                    console.log(`left${l}top${t}`);
                    
                    oDrag.style.left = l + 'px';
                    oDrag.style.top = t + 'px';
                }
                window.onload = drag;
            </script>

    现在运行,拖动可拖动区域就可以满屏幕拖动了,上面的代码有注释,但我还是要讲两个较难理解的点:

    在fnDown()中relativeBoxX = event.clientX - dragBox.offsetLeft - dragBox.offsetWidth/2event.clientX是鼠标点击下位置与左边窗口的距离,dragBox.offsetLeft是当前div与左边窗口的距离,dragBox.offsetWidth/2是margin-left:-200px,也就是当前div宽度的一半是我为了让div一开始强制在页面居中,可以在纸上画一画,下面给出我画的图,看下应该就可以理解了。

    还有比上面简单的重点:但我们用鼠标滑到窗口边缘时,我们的div的边缘跟着跑出边缘,这样有点难看,所以可以优化下,
    let l = e.clientX - Posx,
                        t = e.clientY - Posy,
                        oDrag = document.getElementById('dragBox'),
                        //窗口的宽度
                        winW = document.documentElement.clientWidth || documentb.body.clientWidth,
                        //窗口的高度
                        winH = document.documentElement.clientHeight || document.body.clientHeight,
                        //oDrag.offsetWidth是当前div宽度
                        //oDrag.offsetHeight是当前div高度
                        maxW = winW - oDrag.offsetWidth,
                        maxH = winH - oDrag.offsetHeight;
                        //为了不让div上下左右边框超过屏幕,提高用户体验
                        
                    //跟上面一样,这边因为div是向右偏移
                    //所以要加上marginLeft和marginTop
                    if (l < 0 + dragBox.offsetWidth/2) {
                        l = 0 + dragBox.offsetWidth/2;
                    }
                    else if (l > maxW + dragBox.offsetWidth/2) {
                        l = maxW + dragBox.offsetWidth/2;
                    }
                    if (t < 0 + dragBox.offsetHeight/2) {
                        t = 0 + dragBox.offsetHeight/2;
                    } else if (t > maxH + dragBox.offsetHeight/2) {
                        t = maxH + dragBox.offsetHeight/2;
                    }
     

    因为我们的l=e.clientX - Posx,所以当鼠标移到窗口左边边缘时,肯定是负数的,left也跟着负数,这是只要判断为负数时,变为0,div到窗口左边的距离就是下图所示:

    然后上下边缘意思都一样了,最后记得在fnDown()中添加

    document.onmouseup = function(){
                       document.onmousemove = null;
                       document.onmouseup = null;
                    }
    这样鼠标放开就没有再触发onmousemove事件了,最后我想说的是,其实这个并不难,只是开头为了让它强制居中,加了margin-left,margin-top,导致每个地方都要加
    dragBox.offsetWidth/2,
    dragBox.offsetHeight/2,

    其实把css中的margin删掉,js中所有
    dragBox.offsetWidth/2 和 dragBox.offsetHeight/2都可以删掉,就变得非常简单易懂。
    最后记一点笔记,下面是常用的内置属性:
    网页可见区域宽: document.documentElement.clientWidth;
    网页可见区域高: document.documentElement.clientHeight;  
    网页正文全文宽: document.documentElement.scrollWidth;
    网页正文全文高: document.documentElement.scrollHeight;
    网页被卷去的高(ff):document.body.scrollTop; 
    网页被卷去的高(ie): document.documentElement.scrollTop; 
    网页被卷去的左:document.body.scrollLeft; 
    网页正文部分上:window.screenTop; 
    网页正文部分左:window.screenLeft; 
    某个元素的宽度:obj.offsetWidth;
    某个元素的高度:obj.offsetHeight;
    某个元素的上边界到body最顶部的距离:obj.offsetTop;(在元素的包含元素不含滚动条的情况下)
    某个元素的左边界到body最左边的距离:obj.offsetLeft;(在元素的包含元素不含滚动条的情况下)
    返回当前元素的上边界到它的包含元素的上边界的偏移量:obj.offsetTop(在元素的包含元素含滚动条的情况下)
    返回当前元素的左边界到它的包含元素的左边界的偏移量:obj.offsetLeft(在元素的包含元素含滚动条的情况下)
  • 相关阅读:
    Atitit opencv3.0  3.1 3.2 新特性attilax总结
    Atitit html5.1 新特性attilax总结
    Atitit http2 新特性
    Atitit 大龄软件工程师的出路attilax总结
    Atitit 软件项目系统托盘图标解决方案
    Atitit js canvas的图像处理类库attilax总结与事业
    Atitit 切入一个领域的方法总结 attilax这里,机器学习为例子
    css知多少(8)——float上篇
    css知多少(7)——盒子模型
    css知多少(6)——选择器的优先级
  • 原文地址:https://www.cnblogs.com/doudoublog/p/8550911.html
Copyright © 2011-2022 走看看