摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里是更完美的,不是全局變量,具有塊級函數(shù)作用域,大多數(shù)情況不會(huì)發(fā)生變量提升。
前端基本功-常見概念(一) 點(diǎn)這里
前端基本功-常見概念(二) 點(diǎn)這里
前端基本功-常見概念(三) 點(diǎn)這里
let 是更完美的var,不是全局變量,具有塊級函數(shù)作用域,大多數(shù)情況不會(huì)發(fā)生變量提升。
const 定義常量值,不能夠重新賦值,如果值是一個(gè)對象,可以改變對象里邊的屬性值
var存在的問題
var有作用域問題(會(huì)污染全局作用域)
var可已重復(fù)聲明
var會(huì)變量提升預(yù)解釋
var不能定義常量
let、const特性
let、const不可以重復(fù)聲明
let、const不會(huì)聲明到全局作用域上
let、const不會(huì)預(yù)解釋變量
const做常量聲明(一般常量名用大寫)
let聲明的變量具有塊級作用域
let聲明的變量不能通過window.變量名進(jìn)行訪問
形如for(let x..)的循環(huán)是每次迭代都為x創(chuàng)建新的綁定
下面是 var 帶來的不合理場景var a = [] for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[5]() // 10
在上述代碼中,變量i是var聲明的,在全局范圍類都有效。所以每一次循環(huán),新的i值都會(huì)覆蓋舊值,導(dǎo)致最后輸出都是10
而如果對循環(huán)使用let語句的情況,那么每次迭代都是為x創(chuàng)建新的綁定代碼如下
var a = [] for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[5]() // 5重溫一下閉包和立即函數(shù)兩種方法
閉包的方法
function showNum(i) { return function () { console.log(i) } } var a = [] for (var i = 0; i < 5; i++) { a[i] = showNum(i) }
立即函數(shù)的方法
var a = [] for (var i = 0; i < 5; i++) { a[i] = (function (i) { return function () { console.log(i) } })(i) } a[2]()
本節(jié)參考文章:前端面試之ES6篇
2.重排/重繪在討論重排(回流)與重繪之前,我們要知道:
瀏覽器使用流式布局模型 (Flow Based Layout)。
瀏覽器會(huì)把HTML成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就產(chǎn)生了Render Tree。
有了RenderTree,我們就知道了所有節(jié)點(diǎn)的樣式,然后計(jì)算他們在頁面上的大小和位置,最后把節(jié)點(diǎn)繪制到頁面上。
由于瀏覽器使用流式布局,對Render Tree的計(jì)算通常只需要遍歷一次就可以完成,但table及其內(nèi)部元素除外,他們可能需要多次計(jì)算,通常要花3倍于同等元素的時(shí)間,這也是為什么要避免使用table布局的原因之一。
一句話:重排必將引起重繪,重繪不一定會(huì)引起重排。重排 (Reflow)
當(dāng)Render Tree中部分或全部元素的尺寸、結(jié)構(gòu)、或某些屬性發(fā)生改變時(shí),瀏覽器重新渲染部分或全部文檔的過程稱為重排。
會(huì)導(dǎo)致重排的操作:
頁面首次渲染
瀏覽器窗口大小發(fā)生改變
元素尺寸或位置發(fā)生改變
元素內(nèi)容變化(文字?jǐn)?shù)量或圖片大小等等)
元素字體大小變化
添加或者刪除可見的DOM元素
激活CSS偽類(例如::hover)
查詢某些屬性或調(diào)用某些方法
一些常用且會(huì)導(dǎo)致重排的屬性和方法:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
重繪 (Repaint)當(dāng)頁面中元素樣式的改變并不影響它在文檔流中的位置時(shí)(例如:color、background-color、visibility等),瀏覽器會(huì)將新樣式賦予給元素并重新繪制它,這個(gè)過程稱為重繪。
性能影響回流比重繪的代價(jià)要更高。
有時(shí)即使僅僅回流一個(gè)單一的元素,它的父元素以及任何跟隨它的元素也會(huì)產(chǎn)生回流。
現(xiàn)代瀏覽器會(huì)對頻繁的回流或重繪操作進(jìn)行優(yōu)化:
瀏覽器會(huì)維護(hù)一個(gè)隊(duì)列,把所有引起回流和重繪的操作放入隊(duì)列中,如果隊(duì)列中的任務(wù)數(shù)量或者時(shí)間間隔達(dá)到一個(gè)閾值的,瀏覽器就會(huì)將隊(duì)列清空,進(jìn)行一次批處理,這樣可以把多次回流和重繪變成一次。
當(dāng)你訪問以下屬性或方法時(shí),瀏覽器會(huì)立刻清空隊(duì)列:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
width、height
getComputedStyle()
getBoundingClientRect()
因?yàn)殛?duì)列中可能會(huì)有影響到這些屬性或方法返回值的操作,即使你希望獲取的信息與隊(duì)列中操作引發(fā)的改變無關(guān),瀏覽器也會(huì)強(qiáng)行清空隊(duì)列,確保你拿到的值是最精確的。
如何避免
CSS
避免使用table布局。
盡可能在DOM樹的最末端改變class。
避免設(shè)置多層內(nèi)聯(lián)樣式。
將動(dòng)畫效果應(yīng)用到position屬性為absolute或fixed的元素上。
避免使用CSS表達(dá)式(例如:calc())。
JavaScript
避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式列表定義為class并一次性更改class屬性。
避免頻繁操作DOM,創(chuàng)建一個(gè)documentFragment,在它上面應(yīng)用所有DOM操作,最后再把它添加到文檔中。
也可以先為元素設(shè)置display: none,操作結(jié)束后再把它顯示出來。因?yàn)樵赿isplay屬性為none的元素上進(jìn)行的DOM操作不會(huì)引發(fā)回流和重繪。
避免頻繁讀取會(huì)引發(fā)回流/重繪的屬性,如果確實(shí)需要多次使用,就用一個(gè)變量緩存起來。
對具有復(fù)雜動(dòng)畫的元素使用絕對定位,使它脫離文檔流,否則會(huì)引起父元素及后續(xù)元素頻繁回流。
本節(jié)參考文章:[瀏覽器的回流與重繪 (Reflow & Repaint)](https://juejin.im/post/5a9923e9518825558251c96a)3.函數(shù)節(jié)流(throttle)與函數(shù)去抖(debounce)
Debounce:一部電梯停在某一個(gè)樓層,當(dāng)有一個(gè)人進(jìn)來后,20秒后自動(dòng)關(guān)門,這20秒的等待期間,又一個(gè)人按了電梯進(jìn)來,這20秒又重新計(jì)算,直到電梯關(guān)門那一刻才算是響應(yīng)了事件。
Throttle:好比一臺(tái)自動(dòng)的飲料機(jī),按拿鐵按鈕,在出飲料的過程中,不管按多少這個(gè)按鈕,都不會(huì)連續(xù)出飲料,中間按鈕的響應(yīng)會(huì)被忽略,必須要等這一杯的容量全部出完之后,再按拿鐵按鈕才會(huì)出下一杯。
4.強(qiáng)緩存/協(xié)商緩存瀏覽器緩存分為強(qiáng)緩存和協(xié)商緩存,優(yōu)先讀取強(qiáng)制緩存。
當(dāng)客戶端請求某個(gè)資源時(shí),獲取緩存的流程如下:
先根據(jù)這個(gè)資源的一些 http header 判斷它是否命中強(qiáng)緩存,如果命中,則直接從本地獲取緩存資源,不會(huì)發(fā)請求到服務(wù)器;
當(dāng)強(qiáng)緩存沒有命中時(shí),客戶端會(huì)發(fā)送請求到服務(wù)器,服務(wù)器通過另一些request header驗(yàn)證這個(gè)資源是否命中協(xié)商緩存,稱為http再驗(yàn)證,如果命中,服務(wù)器將請求返回,但不返回資源,而是告訴客戶端直接從緩存中獲取,客戶端收到返回后就會(huì)從緩存中獲取資源;
強(qiáng)緩存和協(xié)商緩存共同之處在于,如果命中緩存,服務(wù)器都不會(huì)返回資源;
區(qū)別是,強(qiáng)緩存不對發(fā)送請求到服務(wù)器,但協(xié)商緩存會(huì)。
當(dāng)協(xié)商緩存也沒命中時(shí),服務(wù)器就會(huì)將資源發(fā)送回客戶端。
當(dāng) ctrl+f5 強(qiáng)制刷新網(wǎng)頁時(shí),直接從服務(wù)器加載,跳過強(qiáng)緩存和協(xié)商緩存;
當(dāng) f5 刷新網(wǎng)頁時(shí),跳過強(qiáng)緩存,但是會(huì)檢查協(xié)商緩存;
強(qiáng)緩存Expires(該字段是 http1.0 時(shí)的規(guī)范,值為一個(gè)絕對時(shí)間的 GMT 格式的時(shí)間字符串,代表緩存資源的過期時(shí)間)
Cache-Control:max-age(該字段是 http1.1 的規(guī)范,強(qiáng)緩存利用其 max-age 值來判斷緩存資源的最大生命周期,它的值單位為秒)
協(xié)商緩存協(xié)商緩存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】這兩對Header來管理的
Last-Modified,If-Modified-Since
Last-Modified 表示本地文件最后修改日期,瀏覽器會(huì)在request header加上If-Modified-Since(上次返回的Last-Modified的值),詢問服務(wù)器在該日期后資源是否有更新,有更新的話就會(huì)將新的資源發(fā)送回來
但是如果在本地打開緩存文件,就會(huì)造成 Last-Modified 被修改,所以在 HTTP / 1.1 出現(xiàn)了 ETag
ETag、If-None-Match
Etag就像一個(gè)指紋,資源變化都會(huì)導(dǎo)致ETag變化,跟最后修改時(shí)間沒有關(guān)系,ETag可以保證每一個(gè)資源是唯一的
If-None-Match的header會(huì)將上次返回的Etag發(fā)送給服務(wù)器,詢問該資源的Etag是否有更新,有變動(dòng)就會(huì)發(fā)送新的資源回來
ETag的優(yōu)先級比Last-Modified更高
一些文件也許會(huì)周期性的更改,但是他的內(nèi)容并不改變(僅僅改變的修改時(shí)間),這個(gè)時(shí)候我們并不希望客戶端認(rèn)為這個(gè)文件被修改了,而重新GET;
某些文件修改非常頻繁,比如在秒以下的時(shí)間內(nèi)進(jìn)行修改,(比方說1s內(nèi)修改了N次),If-Modified-Since能檢查到的粒度是s級的,這種修改無法判斷(或者說UNIX記錄MTIME只能精確到秒);
某些服務(wù)器不能精確的得到文件的最后修改時(shí)間。
推薦必讀:http協(xié)商緩存VS強(qiáng)緩存、瀏覽器緩存知識(shí)小結(jié)及應(yīng)用、緩存(二)——瀏覽器緩存機(jī)制:強(qiáng)緩存、協(xié)商緩存
5.原始類型 / 引用類型JavaScript中的內(nèi)存分為棧內(nèi)存和堆內(nèi)存。棧內(nèi)存和堆內(nèi)存區(qū)別:棧內(nèi)存運(yùn)行效率比堆內(nèi)存高,空間相對堆內(nèi)存來說較小。
區(qū)別:
值類型屬于不可變類型, 由于具有固定長度大小, 其地址和具體內(nèi)容都存在與棧內(nèi)存中
而引用類型屬于可變類型, 一個(gè)對象可以賦予多個(gè)屬性及值,屬性值又可以為一個(gè)新的引用對象。其地址存在棧內(nèi)存,其具體內(nèi)容存在堆內(nèi)存中。
6.cookie/tokentoken和cookie一樣都是首次登陸時(shí),由服務(wù)器下發(fā),都是當(dāng)交互時(shí)進(jìn)行驗(yàn)證的功能,作用都是為無狀態(tài)的HTTP提供的持久機(jī)制。
token存在哪兒都行,localstorage或者cookie。
token和cookie舉例,token就是說你告訴我你是誰就可以。
cookie 舉例:服務(wù)員看你的身份證,給你一個(gè)編號(hào),以后,進(jìn)行任何操作,都出示編號(hào)后服務(wù)員去看查你是誰。token 舉例:直接給服務(wù)員看自己身份證,服務(wù)器不需要去查看你是誰,不需要保存你的會(huì)話。
當(dāng)用戶logout的時(shí)候,cookie和服務(wù)器的session都會(huì)注銷;但是當(dāng)logout時(shí)候token只是注銷瀏覽器信息,不查庫。
token優(yōu)勢在于,token由于服務(wù)器端不存儲(chǔ)會(huì)話,所以可擴(kuò)展性強(qiáng),token還可用于APP中。
小結(jié):
Token 完全由應(yīng)用管理,所以它可以避開同源策略
Token 可以避免 CSRF 攻擊
Token 可以是無狀態(tài)的,可以在多個(gè)服務(wù)間共享
如果你的用戶數(shù)據(jù)可能需要和第三方共享,或者允許第三方調(diào)用 API 接口,用 Token,如果之上自己的那就無所謂了。
本節(jié)參考文章:cookie和token的五點(diǎn)區(qū)別
推薦必讀:前后端常見的幾種鑒權(quán)方式
7.cookie/sessionStorage/localStorage 1.cookiecookie分為cookie機(jī)制和session機(jī)制(請大神判斷正確性)
Session: 是在服務(wù)端保存的一個(gè)數(shù)據(jù)結(jié)構(gòu),用來跟蹤用戶的狀態(tài),這個(gè)數(shù)據(jù)可以保存在集群、數(shù)據(jù)庫、文件中,通過在服務(wù)器端記錄信息確定用戶身份
Cookie: 是客戶端保存用戶信息的一種機(jī)制,用來記錄用戶的一些信息,也是實(shí)現(xiàn)Session的一種方式,通過在客戶端記錄信息確定用戶身份
如果說Cookie機(jī)制是通過檢查客戶身上的“通行證”來確定客戶身份的話,那么Session機(jī)制就是通過檢查服務(wù)器上的“客戶明細(xì)表”來確認(rèn)客戶身份。Session相當(dāng)于程序在服務(wù)器上建立的一份客戶檔案,客戶來訪的時(shí)候只需要查詢客戶檔案表就可以了。
cookie機(jī)制
cookie可以通過設(shè)置domain屬性值,可以不同二級域名下共享cookie,而Storage不可以,比如http://image.baidu.com的cookie http://map.baidu.com是可以訪問的,前提是Cookie的domain設(shè)置為.http://baidu.com,而Storage是不可以的
session機(jī)制
當(dāng)程序需要為某個(gè)客戶端的請求創(chuàng)建一個(gè)session時(shí),
服務(wù)器首先檢查這個(gè)客戶端的請求里是否已包含了一個(gè)session標(biāo)識(shí)---稱為session id,
如果已包含則說明以前已經(jīng)為此客戶端創(chuàng)建過session,服務(wù)器就按照session id把這個(gè)session檢索出來使用(檢索不到,會(huì)新建一個(gè)),
如果客戶端請求不包含session id,則為此客戶端創(chuàng)建一個(gè)session并且生成一個(gè)與此session相關(guān)聯(lián)的session id,session id的值應(yīng)該是一個(gè)既不會(huì)重復(fù),又不容易被找到規(guī)律以仿造的字符串,這個(gè)session id將被在本次響應(yīng)中返回給客戶端保存。
比較
session 在服務(wù)器端,cookie 在客戶端(瀏覽器)
session保存在服務(wù)器,客戶端不知道其中的信息;反之,cookie保存在客戶端,服務(wù)器能夠知道其中的信息
session會(huì)在一定時(shí)間內(nèi)保存在服務(wù)器上,當(dāng)訪問增多,會(huì)比較占用你服務(wù)器的性能,考慮到減輕服務(wù)器性能方面,應(yīng)當(dāng)使用cookie
session中保存的是對象,cookie中保存的是字符串
cookie不是很安全,別人可以分析存放在本地的cookie并進(jìn)行cookie欺騙,考慮到安全應(yīng)當(dāng)使用session。用戶驗(yàn)證這種場合一般會(huì)用 session
用戶驗(yàn)證這種場合一般會(huì)用 session,因此,維持一個(gè)會(huì)話的核心就是客戶端的唯一標(biāo)識(shí),即 session id
session 的運(yùn)行依賴 session id,而 session id 是存在 cookie 中的,也就是說,如果瀏覽器禁用了 cookie ,同時(shí) session 也會(huì)失效(但是可以通過其它方式實(shí)現(xiàn),比如在 url 中傳遞?session_id)
session不能區(qū)分路徑,同一個(gè)用戶在訪問一個(gè)網(wǎng)站期間,所有的session在任何一個(gè)地方都可以訪問到,而cookie中如果設(shè)置了路徑參數(shù),那么同一個(gè)網(wǎng)站中不同路徑下的cookie互相是訪問不到的
JavaScript原生的用法。Cookie 以名/值對形式存儲(chǔ)
例如username=John Doe,這里的數(shù)據(jù)是string類型,如要是其他格式注意進(jìn)行格式轉(zhuǎn)換。
JavaScript 可以使用 document.cookie 屬性來創(chuàng)建 、讀取、及刪除 cookie。JavaScript 中,創(chuàng)建 cookie 如下所示:
document.cookie="username=John Doe";
您還可以為 cookie 添加一個(gè)過期時(shí)間(以 UTC 或 GMT 時(shí)間)。默認(rèn)情況下,cookie 在瀏覽器關(guān)閉時(shí)刪除:
document.cookie="username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 GMT";
您可以使用 path 參數(shù)告訴瀏覽器 cookie 的路徑。默認(rèn)情況下,cookie 屬于當(dāng)前頁面。
document.cookie="username=John Doe; expires=Thu, 18 Dec 2013 12:00:00 GMT; path=/";
設(shè)置cookie
function setCookie(cname,cvalue,exdays){ var SetTime = new Date(); //設(shè)置過期時(shí)間 SetTime.setTime(SetTime.getTime()+(exdays*24*60*60*1000)); //設(shè)置過期時(shí)間 var expires = "expires="+SetTime.toGMTString(); //設(shè)置過期時(shí)間 document.cookie = cname + "=" + cvalue + "; " + expires; //創(chuàng)建一個(gè)cookie }
讀取cookie
function getCookie(c_name){ if (document.cookie.length>0) { c_start=document.cookie.indexOf(c_name + "=") if (c_start!=-1){ c_start=c_start + c_name.length+1 c_end=document.cookie.indexOf(";",c_start) if (c_end==-1) c_end=document.cookie.length return unescape(document.cookie.substring(c_start,c_end)) } } return "" }
刪除cookie
將cookie的有效時(shí)間改成昨天。
使用jquery.cookies.2.2.0.min.js插件添加/修改cookie并設(shè)定過期時(shí)間:
$.cookies.set("cookie_id", "cookie_value", { hoursToLive: 10 });
這里設(shè)置的是過期時(shí)間是10小時(shí), 還可以這樣設(shè)置過期時(shí)間:
expireDate = new Date(); expireDate.setTime( expireDate.getTime() + ( 10 * 60 * 60 * 1000 ) ); $.cookies.set("cookie_id", "cookie_value", {expiresAt:expireDate});
獲取cookie
$.cookies.get("cookie_id");
刪除cookie
$.cookies.del("cookie_id");2.Storage:localStorage、sessionStorage
大小:官方建議是5M存儲(chǔ)空間
類型:只能操作字符串,在存儲(chǔ)之前應(yīng)該使用JSON.stringfy()方法先進(jìn)行一步安全轉(zhuǎn)換字符串,取值時(shí)再用JSON.parse()方法再轉(zhuǎn)換一次
存儲(chǔ)的內(nèi)容: 數(shù)組,圖片,json,樣式,腳本。。。(只要是能序列化成字符串的內(nèi)容都可以存儲(chǔ))
區(qū)別:sessionStorage將數(shù)據(jù)臨時(shí)存儲(chǔ)在session中,瀏覽器關(guān)閉,數(shù)據(jù)隨之消失,localStorage將數(shù)據(jù)存儲(chǔ)在本地,理論上來說數(shù)據(jù)永遠(yuǎn)不會(huì)消失,除非人為刪除
注意:數(shù)據(jù)是明文存儲(chǔ),毫無隱私性可言,絕對不能用于存儲(chǔ)
保存數(shù)據(jù)
localStorage.setItem( key, value ); sessionStorage.setItem(keyName,value); // 將value存儲(chǔ)到key字段中 //或者 sessionStorage.keyName="value";
讀取數(shù)據(jù)
localStorage.getItem( key ); sessionStorage.getItem(keyName); //獲取指定key的本地存儲(chǔ)的值 //或者 var keyName=sessionStorage.key;
刪除單個(gè)數(shù)據(jù)
localStorage.removeItem( key ); sessionStorage.removeItem( key );
刪除全部數(shù)據(jù)
localStorage.clear( ); sessionStorage.clear( );
獲取索引的key
localStorage.key( index ); sessionStorage.key( index );監(jiān)聽storage事件
可以通過監(jiān)聽 window 對象的 storage 事件并指定其事件處理函數(shù),當(dāng)頁面中對 localStorage 或 sessionStorage 進(jìn)行修改時(shí),則會(huì)觸發(fā)對應(yīng)的處理函數(shù)
window.addEventListener("storage",function(e){ console.log("key="+e.key+",oldValue="+e.oldValue+",newValue="+e.newValue); })
localstorage是瀏覽器多個(gè)標(biāo)簽共用的存儲(chǔ)空間,可以用來實(shí)現(xiàn)多標(biāo)簽之間的通信(ps:session是會(huì)話級的存儲(chǔ)空間,每個(gè)標(biāo)簽頁都是多帶帶的
觸發(fā)事件的時(shí)間對象(e 參數(shù)值)有幾個(gè)屬性:3.對比
key : 鍵值。
oldValue : 被修改前的值。
newValue : 被修改后的值。
url : 頁面url。
storageArea : 被修改的 storage 對象。
共同點(diǎn):都是保存在瀏覽器端、且同源的,都受同源策略的制約。
區(qū)別:
cookie數(shù)據(jù)始終在同源的http請求中攜帶(即使不需要),即cookie在瀏覽器和服務(wù)器間來回傳遞,而sessionStorage和localStorage不會(huì)自動(dòng)把數(shù)據(jù)發(fā)送給服務(wù)器,僅在本地保存。cookie數(shù)據(jù)還有路徑(path)的概念,可以限制cookie只屬于某個(gè)路徑下
存儲(chǔ)大小限制也不同,cookie數(shù)據(jù)不能超過4K,同時(shí)因?yàn)槊看蝖ttp請求都會(huì)攜帶cookie、所以cookie只適合保存很小的數(shù)據(jù),如會(huì)話標(biāo)識(shí)。sessionStorage和localStorage雖然也有存儲(chǔ)大小的限制,但比cookie大得多,可以達(dá)到5M或更大
數(shù)據(jù)有效期不同,sessionStorage:僅在當(dāng)前瀏覽器窗口關(guān)閉之前有效;localStorage:始終有效,窗口或?yàn)g覽器關(guān)閉也一直保存,因此用作持久數(shù)據(jù);cookie:只在設(shè)置的cookie過期時(shí)間之前有效,即使窗口關(guān)閉或?yàn)g覽器關(guān)閉
作用域不同,sessionStorage不在不同的瀏覽器窗口中共享,即使是同一個(gè)頁面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
本節(jié)參考文章:緩存(三)——數(shù)據(jù)存儲(chǔ)...
其他閱讀:關(guān)于Cookie、session和Web Storage
8.js事件 1.事件事件指可以被 JavaScript 偵測到的行為。即鼠標(biāo)點(diǎn)擊、頁面或圖像載入、鼠標(biāo)懸浮于頁面的某個(gè)熱點(diǎn)之上、在表單中選取輸入框、確認(rèn)表單、鍵盤按鍵等操作。
事件通常與函數(shù)配合使用,當(dāng)事件發(fā)生時(shí)函數(shù)才會(huì)執(zhí)行。
事件名稱:click/mouseover/blur("不帶on")
事件處理程序(事件偵聽器):響應(yīng)某個(gè)事件的函數(shù),名稱為:onclick/onmouseove/onblur,例如
冒泡:往上
捕獲:向下
事件流指從頁面中接收事件的順序,也可理解為事件在頁面中傳播的順序。
DOM2級事件規(guī)定的事件流包括三個(gè)階段:
(1)事件捕獲階段(2)處于目標(biāo)階段(3)事件冒泡階段。
當(dāng)事件發(fā)生時(shí),最先得到通知的是window,然后是document,由上至下逐級依次而入,直到真正觸發(fā)事件的那個(gè)元素(目標(biāo)元素)為止,這個(gè)過程就是捕獲。
接下來,事件會(huì)從目標(biāo)元素開始起泡,由下至上逐級依次傳播,直到window對象為止,這個(gè)過程就是冒泡。
所以捕獲比冒泡先執(zhí)行。
希望注冊在DOM元素上的事件處理程序在捕獲階段還是在冒泡階段觸發(fā),取決于 addEventListener() 方法的第三個(gè)參數(shù)為 true 還是 false 。
其中DOM3級事件在DOM2的基礎(chǔ)之上添加了更多的事件類型。
描述DOM事件捕獲的具體流程
window-->document-->html(document.documentElement)-->body(document.body)...4.DOM級別/DOM事件
DOM級別一共可以分為4個(gè)級別:DOM0級,DOM1級,DOM2級和DOM3級,
而DOM事件分為3個(gè)級別:DOM0級事件處理,DOM2級事件處理和DOM3級事件處理。
其中1級DOM標(biāo)準(zhǔn)中并沒有定義事件相關(guān)的內(nèi)容,所以沒有所謂的1級DOM事件模型。
DOM0:element.onclick = function(){}
DOM2:element.addEventlistenter("click",function(){},flase)
DOM3:element.addEventlistenter("keyup",function(){},flase)
HTML事件處理程序
DOM0級事件處理程序
btn.onclick=function(){ alert("hello"); }
btn.onclick = null;//來刪除指定的事件處理程序。
如果我們嘗試給事件添加兩個(gè)事件,如:
輸出,hello again,很明顯,第一個(gè)事件函數(shù)被第二個(gè)事件函數(shù)給覆蓋掉了, 所以,DOM0級事件處理程序不能添加多個(gè),也不能控制事件流到底是捕獲還是冒泡。
DOM2級事件處理程序
addEventListener() ---添加事件偵聽器
函數(shù)均有3個(gè)參數(shù),
第一個(gè)參數(shù)是要處理的事件名(不帶on前綴的才是事件名)
第二個(gè)參數(shù)是作為事件處理程序的函數(shù)
第三個(gè)參數(shù)是一個(gè)boolean值,默認(rèn)false表示使用冒泡機(jī)制,true表示捕獲機(jī)制。
removeEventListener() //刪除事件偵聽器
可以綁定多個(gè)事件處理程序,但是注意,如果定義了一摸一樣時(shí)監(jiān)聽方法,是會(huì)發(fā)生覆蓋的,即同樣的事件和事件流機(jī)制下相同方法只會(huì)觸發(fā)一次,事件觸發(fā)的順序是添加的順序
``` // 為了兼容IE瀏覽器和標(biāo)準(zhǔn)的瀏覽器,我們需要編寫通用的方法來處理: var EventUtil = { addHandler: function (element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function (element, type, handler) { if (element.removeEventListener()) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } }; ```5.事件對象
事件對象是用來記錄一些事件發(fā)生時(shí)的相關(guān)信息的對象,但事件對象只有事件發(fā)生時(shí)才會(huì)產(chǎn)生,并且只能是事件處理函數(shù)內(nèi)部訪問,在所有事件處理函數(shù)運(yùn)行結(jié)束后,事件對象就被銷毀!
//currentTarget、eventPhase 一個(gè)例子:
當(dāng)然,事件對象也存在一定的兼容性問題,在IE8及以前本版之中,通過設(shè)置屬性注冊事件處理程序時(shí),調(diào)用的時(shí)候并未傳遞事件對象,需要通過全局對象window.event來獲取。解決方法如下:
function getEvent(event) { event = event || window.event; }
在IE瀏覽器上面是event事件是沒有preventDefault()這個(gè)屬性的,所以在IE上,我們需要設(shè)置的屬性是returnValue
window.event.returnValue=false
stopPropagation()也是,所以需要設(shè)置cancelBubble,cancelBubble是IE事件對象的一個(gè)屬性,設(shè)置這個(gè)屬性為true能阻止事件進(jìn)一步傳播。
event.cancelBubble=true
常見屬性 | 解析 |
---|---|
event.preventDefault() | 阻止默認(rèn)行為 |
event.stopPropagation() | 阻止冒泡。使用了stopPropagation()之后,事件就不能進(jìn)一步傳播了,同時(shí)如果是同一個(gè)div上有捕獲和冒泡兩種事件監(jiān)聽,在捕獲階段傳播阻止后冒泡階段事件監(jiān)聽也不會(huì)觸發(fā)。 |
event.stopImmediatePropagation() | 使用了stopImmediatePropagation()之后,綁定的后續(xù)事件監(jiān)聽都會(huì)忽略。 |
event.currentTarget | 當(dāng)前綁定的事件 |
event.target | 事件代理時(shí) 點(diǎn)擊的元素 |
關(guān)于捕獲和冒泡:理解 addEventListener、捕獲和冒泡
6.自定義事件jq
// 添加一個(gè)適當(dāng)?shù)氖录O(jiān)聽器 $("#foo").on("custom", function(event, param1, param2) { alert(param1 + " " + param2); }); $("#foo").trigger("custom", ["Custom", "Event"]);
原生Event:
var eve = new Event("custome") element.addEventListenter("custome",function(){ console.log("custome") }) element.dispatchEvent(eve)
原生CustomEvent
// 添加一個(gè)適當(dāng)?shù)氖录O(jiān)聽器 obj.addEventListener("custom", function(e) { console.log(JSON.stringify(e.detail)); }) // 創(chuàng)建并分發(fā)事件 var event = new CustomEvent("custom", {"detail":{"Custom":true}}); obj.dispatchEvent(event)
本節(jié)參考文章:一個(gè)能拖動(dòng),能調(diào)整大小,能更新bind值的vue指令-vuedragx
7.事件委托(代理)事件委托就是利用事件冒泡,只指定一個(gè)事件處理程序,就可以管理某一類型的所有事件。
案例一:
解決方法:事件委托
案例二:
點(diǎn)擊“添加”按鈕添加一個(gè)按鈕,點(diǎn)擊添加的按鈕移除這個(gè)按鈕
添加繪畫散步靜坐
document.getElementById("wrap").addEventListener("click", function(e){ var target = e.target; while(target !== this){ var type = target.dataset.type; if(type == "btn"){ var feat = target.dataset.feat; switch(feat){ case "add": this.innerHTML += "靜坐" return; case "delete": target.parentNode.removeChild(target); return; } } target = target.parentNode; } }, false);
適合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
推薦閱讀:JavaScript 事件委托詳解
本節(jié)參考文章:前端小知識(shí)--JavaScript事件流
9.link / @import兩者都是外部引用 CSS 的方式,但是存在一定的區(qū)別:
link是XHTML標(biāo)簽,除了能夠加載CSS,還可以定義RSS等其他事務(wù);而@import屬于CSS范疇,只可以加載CSS。
link引用CSS時(shí),在頁面載入時(shí)同時(shí)加載;@import需要頁面完全載入以后再加載。
link是XHTML標(biāo)簽,無兼容問題;@import則是在CSS2.1提出的,低版本的瀏覽器不支持。
link方式的樣式的權(quán)重 高于@import的權(quán)重.
link支持使用Javascript控制DOM改變樣式;而@import不支持。
本節(jié)參考文章:前端面試題-url、href、src
10.異步編程的實(shí)現(xiàn)方式1.回調(diào)函數(shù)
優(yōu)點(diǎn):簡單、容易理解
缺點(diǎn):不利于維護(hù),代碼耦合高
2.事件監(jiān)聽(采用時(shí)間驅(qū)動(dòng)模式,取決于某個(gè)事件是否發(fā)生):
優(yōu)點(diǎn):容易理解,可以綁定多個(gè)事件,每個(gè)事件可以指定多個(gè)回調(diào)函數(shù)
缺點(diǎn):事件驅(qū)動(dòng)型,流程不夠清晰
3.發(fā)布/訂閱(觀察者模式)
類似于事件監(jiān)聽,但是可以通過‘消息中心’,了解現(xiàn)在有多少發(fā)布者,多少訂閱者
4.Promise對象
優(yōu)點(diǎn):可以利用then方法,進(jìn)行鏈?zhǔn)綄懛ǎ豢梢詴鴮戝e(cuò)誤時(shí)的回調(diào)函數(shù);
缺點(diǎn):編寫和理解,相對比較難
5.Generator函數(shù)
優(yōu)點(diǎn):函數(shù)體內(nèi)外的數(shù)據(jù)交換、錯(cuò)誤處理機(jī)制
缺點(diǎn):流程管理不方便
6.async函數(shù)
優(yōu)點(diǎn):內(nèi)置執(zhí)行器、更好的語義、更廣的適用性、返回的是Promise、結(jié)構(gòu)清晰。
缺點(diǎn):錯(cuò)誤處理機(jī)制
document.write只能重繪整個(gè)頁面
innerHTML可以重繪頁面的一部分
isPrototypeOf() 與 instanceof 運(yùn)算符不同。
在表達(dá)式 "object instanceof AFunction"中,object 的原型鏈?zhǔn)轻槍?AFunction.prototype 進(jìn)行檢查的,而不是針對 AFunction 本身。
isPrototypeOf() 方法允許你檢查一個(gè)對象是否存在于另一個(gè)對象的原型鏈上。
function Foo() {} function Bar() {} function Baz() {} Bar.prototype = Object.create(Foo.prototype); Baz.prototype = Object.create(Bar.prototype); var baz = new Baz(); //isPrototypeOf console.log(Baz.prototype.isPrototypeOf(baz)); // true console.log(Bar.prototype.isPrototypeOf(baz)); // true console.log(Foo.prototype.isPrototypeOf(baz)); // true console.log(Object.prototype.isPrototypeOf(baz)); // true if (Foo.prototype.isPrototypeOf(baz)) { // do something safe } //instanceof console.log(baz instanceof Baz); // true console.log(baz instanceof Bar); // true console.log(baz instanceof Foo); // true console.log(baz instanceof Object); // true
var obj1 = { name: "esw" } var obj2 = Object.create(obj1) // isPrototypeOf()方法 Object.prototype.isPrototypeOf(obj1) // true obj1.isPrototypeOf(obj2) // true Object.prototype.isPrototypeOf(obj2) // true13.constructor、__proto__與prototype
在javascript中我們每創(chuàng)建一個(gè)對象,該對象都會(huì)獲得一個(gè)__proto__屬性(該屬性是個(gè)對象),該屬性指向創(chuàng)建該對象的構(gòu)造函數(shù)的原型即prototype,同時(shí)__proto__對象有一個(gè)constructor屬性指向該構(gòu)造函數(shù)。這里我們需要注意的是只有函數(shù)才有prototype,每個(gè)對象(函數(shù)也是對象)都有__proto__,Object本身是個(gè)構(gòu)造函數(shù)。舉例來說:
var obj = new Object() // 也可以使用對象字面量創(chuàng)建,但使用Object.create()情況會(huì)不一樣 // Object本身是個(gè)構(gòu)造函數(shù) Object instanceof Function // true obj.__proto__ === Object.prototype // true obj.__proto__.constructor === Object // true // 我們一般習(xí)慣這樣寫 obj.constructor === Object // true
當(dāng)我們訪問obj.constructor的時(shí)候,obj本身是沒有constructor屬性的,但屬性訪問會(huì)沿著__proto__向上查找,即在obj.__proto__里面尋找constructor屬性,如果找到了就返回值,如果未找到則繼續(xù)向上查找直到obj.__proto__.__proto__...(__proto__) === null為止,沒有找到則返回undefined。這樣由__proto__構(gòu)成的一條查找屬性的線稱為‘原型鏈’。
本節(jié)參考文章:重新認(rèn)識(shí)javascript對象(三)——原型及原型鏈、一篇文章帶你進(jìn)一步了解object屬性
14.淺拷貝/深拷貝1.淺拷貝只能復(fù)制值類型的屬性。對于引用類型的屬性,復(fù)制前后的兩個(gè)對象指向同一內(nèi)存地址,操作其中一個(gè)對象的引用類型屬性,另一個(gè)對象也會(huì)相應(yīng)發(fā)生改變;也就是說只有改變值類型的屬性兩個(gè)對象才不會(huì)相互影響。
2.深拷貝不僅可以復(fù)制值類型的屬性,還可以復(fù)制引用類型的屬性,無論兩個(gè)對象怎么改變都不會(huì)相互影響。
淺復(fù)制
var obj = { a : 1, b: { c: 2 } } // 淺復(fù)制 function lowerClone(obj){ var newObj=obj.constructor === Array ? [] : {}; for(var i in obj){ newObj[i]=obj[i] } return newObj; } var objClone = lowerClone(obj) objClone.a // 1 obj.a // 1 objClone.a = 100 // 改變復(fù)制對象的值類型屬性,值類型屬性的值不變 obj.a // 1 objClone.b.c = 200 // 改變復(fù)制對象的引用類型的屬性,引用類型的屬性值改變 obj.b.c //200
深復(fù)制
var obj = { a : 1, b: { c: 2 } } function deepClone(obj){ if( typeof obj != "object"){ return obj; } var newObj=obj.constructor === Array ? [] : {}; for(var i in obj){ newObj[i]=deepClone(obj[i]); } return newObj; } var objClone = deepClone(obj) objClone.a // 1 obj.a // 1 objClone.a = 100 // 改變復(fù)制對象的值類型屬性,值類型屬性的值不變 obj.a // 1 objClone.b.c = 200 // 改變復(fù)制對象的引用類型的屬性,引用類型的屬性值不變 obj.b.c // 2
本節(jié)參考文章:javascript淺復(fù)制與深復(fù)制
15.apply/call/bindapply 、 call 、bind 三者都是用來改變函數(shù)的this對象的指向的;
apply 、 call 、bind 三者第一個(gè)參數(shù)都是this要指向的對象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后續(xù)參數(shù)傳參;
bind是返回對應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。
call apply 的區(qū)別是他們指定參數(shù)的方式不同。
案例
function fn(a,b){ console.log(this); console.log(a); console.log(b); } // bind(this,args...) bf = fn.bind("Bind this",10); // 沒有任何輸出,也就是說沒有執(zhí)行這個(gè)函數(shù) bf(); // "Bind this",10,undefined bf(20);// “Bind this”,10,20 // 原函數(shù)不受影響 fn(1,2); //window, 1,2 bf2 = fn.bind("Bind this",1,2); bf2(); // "Bind this",1,2 // call(this,args...) fn.call("Call this",1) // "Call this",1,undefined fn.call("Call this",1,2) // "Call this",1,2 // apply(this,[args]) fn.apply("Apply this",[1]) // "Apply this",1,undefined fn.apply("Apply this",[1,2]) // "Apply this",1,2
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/53555.html
摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里什么是原型鏈當(dāng)一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法時(shí)候就會(huì)產(chǎn)生一個(gè)原型鏈。函數(shù)式編程是聲明式而不是命令式,并且應(yīng)用程序狀態(tài)通過純函數(shù)流轉(zhuǎn)。 前端基本功-常見概念(一) 點(diǎn)這里前端基本功-常見概念(二) 點(diǎn)這里前端基本功-常見概念(三) 點(diǎn)這里 1.什么是原型鏈 當(dāng)一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方...
摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里是更完美的,不是全局變量,具有塊級函數(shù)作用域,大多數(shù)情況不會(huì)發(fā)生變量提升。 前端基本功-常見概念(一) 點(diǎn)這里前端基本功-常見概念(二) 點(diǎn)這里前端基本功-常見概念(三) 點(diǎn)這里 1.let、const/var let 是更完美的var,不是全局變量,具有塊級函數(shù)作用域,大多數(shù)情況不會(huì)發(fā)生變量提升。cons...
摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里是更完美的,不是全局變量,具有塊級函數(shù)作用域,大多數(shù)情況不會(huì)發(fā)生變量提升。 前端基本功-常見概念(一) 點(diǎn)這里前端基本功-常見概念(二) 點(diǎn)這里前端基本功-常見概念(三) 點(diǎn)這里 1.let、const/var let 是更完美的var,不是全局變量,具有塊級函數(shù)作用域,大多數(shù)情況不會(huì)發(fā)生變量提升。cons...
摘要:一般來說,聲明式編程關(guān)注于發(fā)生了啥,而命令式則同時(shí)關(guān)注與咋發(fā)生的。聲明式編程可以較好地解決這個(gè)問題,剛才提到的比較麻煩的元素選擇這個(gè)動(dòng)作可以交托給框架或者庫區(qū)處理,這樣就能讓開發(fā)者專注于發(fā)生了啥,這里推薦一波與。 本文翻譯自FreeCodeCamp的from-zero-to-front-end-hero-part。 繼續(xù)譯者的廢話,這篇文章是前端攻略-從路人甲到英雄無敵的下半部分,在...
閱讀 2627·2021-11-23 09:51
閱讀 861·2021-09-24 10:37
閱讀 3612·2021-09-02 15:15
閱讀 1962·2019-08-30 13:03
閱讀 1881·2019-08-29 15:41
閱讀 2624·2019-08-29 14:12
閱讀 1424·2019-08-29 11:19
閱讀 3301·2019-08-26 13:39