在函數代碼中,使用特殊對象 arguments,開發者無需明確指出參數名,就能訪問它們。javascript
例如,在函數 sayHi() 中,第一個參數是 message。用 arguments[0] 也能夠訪問這個值,即第一個參數的值(第一個參數位於位置 0,第二個參數位於位置 1,依此類推)。css
能夠用於模擬函數重載:html
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 的這種限制。java
在非嚴格的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內部。ui
eval()
一般比替代方法慢,由於它必須調用 JS 解釋器,而許多其餘結構則由現代 JS 引擎進行優化。 箭頭函數屬於匿名函數,匿名函數是要經過賦值語句賦值給變量,這個賦值的過程是在代碼執行階段進行的,不是在聲明階段,因此沒有函數聲明的提高屬性。this
箭頭函數中的 this 和調用時的上下文無關,而是取決於定義時的上下文:spa
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 拿來使用罷了;在普通函數中,會自動綁定上的各類局部變量,箭頭函數都是十分單純的沿着做用域鏈向上尋找.3d
固然普通函數也能夠實現和箭頭函數同樣的效果:
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最常常用的事件,其中:
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/2,event.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(在元素的包含元素含滾動條的狀況下)