深入淺出原生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(在元素的包含元素含滾動條的情況下)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM