落魄前端,整理给自己的前端知识体系复习大纲(上篇,2w字)
作者:逐步前行
https://juejin.im/post/6867715946941775885
閱讀前言
轉眼間9月的到來,十分感慨這時光的飛逝。9月對程序員有著十分重要的意義,想在這個優勝劣汰的代碼世界活下去,金3銀4,金9銀10,都屬于選擇的機會。
在這優勢略汰且經濟回隴的狀態下,筆者身處央企都無法安穩,如今也要考慮工作的問題(有廣州?內推?,歡迎聯系),我們可以選擇的只能是,逐步的打好自己的基礎,才能在不安穩的社會形態下,逆行保持自己的安穩。筆者也該復習復習,在此匯總一下自己過去所學過的基礎。
本文的難度級別,個人定位為中級前端開發工程師。且自負的認為前端重要基礎的均匯總,如有遺漏,希望你評論噴我,噴我如果能學習到知識,愿聽聞。筆者也是逐步前進的小伙,當前也需要及時的補充自己,過程如有不對的地方,盡快指出。如文章對你有幫助,希望能給于鼓勵,手動點個贊吧。
匯總內容包含看過的書籍,自己對程序的理解,以及部分大神的借鑒(很多都是曾經記下的知識點,來源我也忘記是從哪里,如沒有提及希望包涵)。
但文章全文,均為筆者一字一字手敲。寫給自己供于復習,同時分享給在前端一起努力朋友。
一.前端基礎
前端基礎,個人認為就是html + js + css。無論過程如何,無論你用的是less還是sass,無論你用的vue還是react,輸出的結果,只有html + js + css。
此部分列舉,筆者覺得重點的知識點,如有遺漏,歡迎指出。
1)html篇
html章節,本文僅列出筆者任務相對重要的知識點,且介紹上,針對重點。當然,遺漏很正常,希望能收到你的意見。
1.語義化
所謂,語義化的標簽,說明讓標簽有自己的含義。也是近十年。最典型的栗子就是header,footer等,它可以讓你在沒有樣式的情況下,就大概能想到,他就是個頭部或者底部。他存在的意義,就是讓前端開發人員,在開發過程中,更容易去閱讀代碼,以及明白這些代碼的意義。
它的好處是:1.能夠更好的展示內容結構2.便于團隊的維護與開發3.有利于SEO,爬蟲可以分析每個關鍵詞的權重。4.方便其他設備解析 (如屏幕閱讀器)
2.SEO
作為前端,你不得不知道的SEO,這涉及到公司的網站推廣。
SEO,中文稱搜索引擎優化,一種利用搜索引擎的搜索規則來提高目前網站在有關搜索引擎內的自然排名的方式。他的實現原來分別為,頁面抓取,分析入庫,檢索排序。
有興趣深入SEO優化的朋友:segmentfault.com/a/119000001…
3.doctype
前端經常在html頭部看到DOCTYPE的聲明,一般常位于文檔的第一行。那么他的作用是什么,可能對新的瀏覽器或者新的網站暫無什么影響,但是相對古老的瀏覽器或者是網站,可能會出現不同。因為瀏覽器有標準模式與兼容模式,差異相對比較大。
標準模式的渲染方式和 JS 引擎的解析方式都是以該瀏覽器支持的最高標準運行。兼容模式中,頁面以寬松的向后兼容的方式顯示 ,模擬老式瀏覽器的行為以防止站點無法工作。
而DOCTYPE的存在,就是為了聲明,該頁面使用標準模式。不聲明,可能一些舊的網站會出現兼容模式。
4.link與@import
link與import , 本質使用上,我們都是用他來引入css,但是他們有一定的區別。
link是一種引入資源的標簽,import是引入css的方式。所以,import引入的只能是css,而link可以引入所有的資源,包括圖片,RSS等。
加載順序上也有一些差異。link引用的CSS會同時被加載。import引用的CSS會等到頁面全部被下載完再加載。
兼容性的差別。link無任何兼容問題,import兼容IE5以上。(當然,IE5估計也找不到了)
動態引入樣式link可以后期引入樣式,而import是不可以后期引入的,只能初始化頁面之前引入。
復用率的問題import可以復用之前的css文件,而link只能一次引用一個文件。當然,import復用文件時,在瀏覽器實際上是加載了多個文件,會有多個請求。而每一個link只是一個http請求。
5.async與defer
首先這兩個東西為什么而存在的問題。在日漸復雜的前端,異常已經是程序的一部分。如果出現一些小問題,或者服務器加載上出現延遲。而我們默認的引入的script腳本,會阻塞后續的DOM渲染。一旦沒有部分異常無法及時加載完成,那么我們的頁面因為阻塞問題,將整個白屏。
也許我們可以保證自己服務器的正常,但是你決定保證不了第三方服務器的正常,于是引入了async和defer來優化這個問題。
再來談談script的默認,async,defer的之前的差異。
默認情況下:瀏覽器會立即加載并執行指定的腳本。指定的腳本,指在script標簽之上的腳本。所以,如果script放在header中,而對應的文件還未加載完成,會形成阻塞。所以這就是現在很多頁面,都會使用默認且把scipt放在頁面結尾的原因。
async情況下:async ,加載和渲染后續文檔元素的過程將和 script.js 的加載與執行并行進行(異步)。async是亂序的。
defer情況下:defer,加載后續文檔元素的過程將和 script.js 的加載并行進行(異步),但是 script.js 的執行要在所有元素解析完成之后,DOMContentLoaded 事件觸發之前完成。defer是順序執行。
此外,async跟defer,不支持或者不兼容IE9一下瀏覽器,總體來說,筆者還是覺得script放最下方靠譜一些。
6.文本元素的冒泡與委托
適合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
優點:1.減少事件注冊,節省內存。例如上面代碼,只指定 父元素的處理程序,即可管理所有所有子元素的“click”事件;2.簡化了dom節點更新時,相應事件的更新
缺點:1.利用事件冒泡的原理,不支持不冒泡的事件;2.層級過多,冒泡過程中,可能會被某層阻止掉;3. 理論上委托會導致瀏覽器頻繁調用處理函數,雖然很可能不需要處理。所以建議就近委托,比如在ol上代理li,而不是在document上代理li。4. 把所有事件都用代理就可能會出現事件誤判。比如,在document中代理了所有button的click事件,另外的人在引用改js時,可能不知道,造成單擊button觸發了兩個click事件。
2)css篇
css章節,本文僅列出筆者任務相對重要的知識點,且介紹上,針對重點。當然,遺漏很正常,希望能收到你的意見。
1.盒子模型
盒子模型,個人的理解,就是一個來裝html標簽的容器,包裝的內容包括content+padding+border+margin。由這四個組成我們的"盒子"。
我們日??赡軙龅讲煌臑g覽器,元素的高寬不一致。除了可能是瀏覽器內置的margin跟padding不同之外,也可能是IE跟w3c的盒子模型組成不同。
以下是兩種不同盒子的分類:
W3C盒子模型:可通過box-sizing: content-box來設置,他包含content+padding+border+margin。
IE盒子模型:可通過box-sizing: border-box來設置,content+margin。其中content包含border,padding。
2.BFC
簡單的個人理解,block formatting context,塊級格式化上下文。產生了BFC的,形成了獨立容器,他的特性就是不會再布局中影響到外邊的元素。
他的特性:
1)BFC邊距會重疊。
2)BFC的內外元素互相不影響
3)BFC不會與浮動元素發生重疊
4)BFC元素的高度計算會包括元素內的浮動元素的高度
觸發的條件是:
1)body 根元素
2)浮動元素:float 除 none 以外的值
3)絕對定位元素:position (absolute、fixed)
4)display 為 inline-block、table-cells、flex,table-caption
5)overflow 除了 visible 以外的值 (hidden、auto、scroll)
此外,除了BFC,還有IFC、GFC、FFC的概念。我們簡單了解一下。
GFC:可簡單理解為grid布局
FFC:可簡單理解為flex布局。
IFC:內聯格式化上下文,簡單理解為:inline-block。
水平方向上的 margin,border 和 padding在框之間得到保留。框在垂直方向上可以以不同的方式對齊:它們的頂部或底部對齊,或根據其中文字的基線對齊。包含那些框的長方形區域,會形成一行,叫做行框。inline-block的元素的內部是一個BFC,但是它本身可以和其它inline元素一起形成IFC。
3.flex布局
flex,即彈性布局。一個由css3引入,為我們的盒子屬性帶來靈活性的一種布局方式。一旦父級采用了flex布局,里邊的子控件將收flex布局限制,部分原本的樣式(如float:left)也會失效。
基本api不做講解,不熟悉可以看看:www.ruanyifeng.com/blog/2015/0…
特別注意:flex:0 0 30%的意義:等于flex-grow=0(默認不放大)+flex-shrink=0(不縮小)+flex-basis=30%( 項目占據主軸的空間)
4.css3新特性
背景,支持RGBA透明度,一次多背景圖
支持媒體查詢
支持陰影,漸變,陰影
支持邊框圖片,border-image: url(border.png) 30 30 round
支持transform位移系列
支持過渡效果transition
支持自定義字體
引入flex/grid布局
引入多種選擇器
其他不做詳細說明,有興趣搜一下css3新特性
5.圖片格式
前端的圖片分類格式,其實是性能優化的很大部分。選擇好圖片的類型,對前端的性能影響非常大。
而前端對圖片的精髓,一方面是對圖片大小的評估,一方面是對圖片的類型選擇。
他的大小可以這樣判斷:
比如一張200*200的圖片大小,這時候,他的像素點有40000個。每個像素有 4 個通道, 所以一共有160000個字節,所以,我們評估該圖片的大小大概為:160000/1024 ?約等于 156(KB), 如果大很多,說明圖片大小有優化控件。如果小很多,說明此時是模糊的。
| png | 適合顏色簡單,但是對圖片質量比較高。日常用的png8,此外還有png32, | 適合logo體積太大一般不用 |
| jpeg | 不影響圖片質量的情況有損壓縮,banner圖。適合大圖。 | 壓縮后大小可省略很多,一般大圖使用 |
| svg | 對性能有損耗,體積小,壓縮性搶。可在質量不下降的過程被放大 | 部分瀏覽器兼容性不太好 |
| webp | 只針對谷歌,兼容性不好。圖片大小能壓縮30~40%。 | 谷歌瀏覽器用,如有非常注重性能的產品,可判斷瀏覽器加載不同類型圖片 |
| base64 | 壓縮成字符流,實際大小是變大了,但是好處就是減少了http請求 | 一般也針對小圖標 |
6.移動端適配
列舉一下筆者所知道的適配方式:
1)媒體查詢。該方案的話,個人覺得是最佳的方案,也是常用UI庫非常喜歡的用處理方式之一。唯一不好的是:多套媒體方案,也意味多份的工作量。
2)vw/vh利用單位vw/vh進行布局。該方案的話,對整體的布局還是相對穩定,但是對部分細節等處理還是不優化。且遇到手機屏幕差異較大的話,會出現嚴重的視差。
3)rem相對穩定的方法。根據屏幕大小計算出font-size;但是只能求良好,很難求精。如果UI對一像素非常的敏感,這個方案可能是個非常糟糕的選擇。
4)類似小程序rpx。相信原生小程序開發者都用過rpx。這里其實原理有點類似rem。但是,卻不是按屏幕大小去計算,而是不同的屏幕定義了自己的標準。
7.常見兼容性
這個問題本次只列舉了幾個常見的,非全部列出。如需具體,可另查資料。
1)間距差異是否大,導致文本換行,或者間隔太大。原因:每個瀏覽器的margin和padding的默認值不同。解決方案:全局文件設置統一默認margin和padding。
2)圖片默認有間距原因:因為img標簽是行內屬性標簽,所以只要不超出容器寬度,img標簽都會排在一行里,但是部分瀏覽器的img標簽之間會有個間距。去掉這個間距使用float是正道。(我的一個學生使用負margin,雖然能解決,但負margin本身就是容易引起瀏覽器兼容問題的用法,所以我禁止他們使用)解決方案:使用float屬性為img布局
3)較小的高度(小于10px),時,ie可能會超出高度原因:IE有一個默認的行高的高度解決方案:給超出高度的標簽設置overflow:hidden;或者設置行高line-height 小于你設置的高度。
為min-height本身就是一個不兼容的CSS屬性
4)透明度兼容設置原因:不同瀏覽器各自透明度關鍵字不統一。解決方案:filter:alpha(opacity=50); -moz-opacity:0.5; ?-khtml-opacity: 0.5; opacity: 0.5;
5)IE的hover圖片會閃爍原因:IE6的每次觸發 hover 的時候都會重新加載解決方案:提前緩存文件。document.execCommand("BackgroundImageCache", false, true);
8.垂直居中
該回復只給與思路,沒有具體寫法。因為我覺得大家都應該懂。
已知寬高:1.margin 自己算高寬 2.定位 + margin-top + margin-left3.定位 + margin:auto
未知寬高:1.transform 但有IE兼容的問題2.flex 布局3.display: table-cell
9.實現1px
首先你可能需要了解一下物理像素跟獨立像素的區別。
物理像素:一個物理像素是顯示器(手機屏幕)上最小的物理顯示單元,如:iPhone6上就有7501334個物理像素顆粒。獨立像素:邏輯像素,程序使用的虛擬像素。如:iPhone6上就有375677個獨立像素。
那么如何實現1px呢:1.利用 transfrom 的 scale 縮放來實現2.利用 background 的 line-gradient 線性漸變來實現3.meta viewport修改成1比0.5。這樣整個屏幕的大小縮小了0.5。4.利用box-shadow
10.三列布局
該回復只給思路
1.CSS浮動第一個float:left,第二個float:right,第三個設置margin-left和margin-right
2.絕對定位法第一個定位到left,第二個定位到right,第三個設置margin-left和margin-right
3.flex布局
11.樣式優化
初步聊聊個人的樣式優化方案如下:
1.避免css層級太深。有興趣了解一下css tree如何跟html tree融合成dom tree。2.首屏(特別是緩沖效果圖)可適當使用內聯元素。這樣有利于更快的顯示。3.異步加載CSS。非首次重要引入的css文件,不放在head里邊。這樣會引起阻塞。4.減少 回流 的屬性。如display:none可以考慮使用visibility5.適當使用GPU渲染。如tranfrom等。6.css動畫的性能,是遠遠的大于js動畫性能。7.利用工具壓縮,去重。
12.偽類和偽元素
偽類和偽元素的根本區別在于:它們是否創造了新的元素
偽類,指可以通過元素選擇器,就可以實現的效果,如frist-child,active等。而偽元素,是指需要通過創元素,才可以實現的效果,如first-letter,before,after等。
具體元素跟寫法有興趣,可參考:blog.csdn.net/qq_27674439…
3)javaScript篇
javaScript篇,由于擴展性十分全。對于大神來說,每一個點,都可以做一篇簡介參考。本文只能是概念上的簡介,或者是個人對應的理解。如理解有誤,歡迎吐槽。
1.內置對象
內置對象,也叫原始類型。
原始類型有5個,null,undefined,boolean,number,string。es6引入了symbol,可以用來做獨立標識用。es10引入了bigint, 主要用的大數據。number最大值2的53次方,超過只能使用bigint。截至目前為止,一共是7個。
原始類型存儲的都是值,他的原型匯總,是沒有任何函數的。如果你看到類型有函數,比如toString,那說明類型給轉換為了對象類型,此時才有toString方法。
原始類型存儲的是值,對象類型存儲的是地址。
2.閉包
簡單的理解是,一個綁定了執行環境的函數,可以訪問到外部環境的變量。
他的好處就是:變量常駐內存,對于實現某些業務很有幫助,比如計數器之類的。架起了一座橋梁,讓函數外部訪問函數內部變量成為可能。私有化,一定程序上解決命名沖突問題,可以實現私有變量。
缺陷是:他的變量常駐在內存中,其占用內存無法被GC回收,導致內存溢出。
注意,閉包的原理是作用域鏈,所以閉包訪問的上級作用域中的變量是個對象,其值為其運算結束后的最后一個值。
3.執行上下文
代碼運行時,產生一個對應的執行環境,這個叫做執行上下文。
通常執行上下文,有三個環境: 1.全局環境:代碼首先進入的環境 2.函數環境:函數被調用時執行的環境 3.eval函數:www.cnblogs.com/chaoguo1234…
執行上下文,可分為三個階段,分別為創建,執行,銷毀階段。我們簡單的分析一下,各個階段分別處理了什么。
創建階段: (1).生成變量對象 (2).建立作用域鏈 (3).確定 this 指向
執行階段:(1).變量賦值(2).函數引用(3).執行其他代碼
銷毀階段:執行完畢出棧,等待回收被銷毀
4.原型/原型鏈
指構造函數的內置屬性,即prototype屬性。每個構造函數都自帶prototype屬性,指向一個對象,常用實例共享屬性和方法的。
Prototype.constructor會指向原構造函數
對象的原型,也是個對象。只要對象的原型有值,不為null,他就還有原型。所以構成了原型鏈。
5.作用鏈域
作用域鏈的原理和原型鏈很類似,如果這個變量在自己的作用域中沒有,那么它會尋找父級的,直到最頂層。注意:JS沒有塊級作用域,若要形成塊級作用域,可通過(function(){})();立即執行的形式實現。
6.繼承
繼承的幾種方式:
1.原型鏈繼承本質是重寫了對象。缺點:1)對象實例共享所有繼承的屬性和方法
2)不能傳遞參數2.構造函數繼承在子類構造函數的內部調用超類型構造函數。使用aapply()和call() 方法缺點:1)函數復用性不高
2)只能繼承實例上的屬性,原型上的方法不可見3.組合繼承本質:原型鏈 + 構造函數Parent.call(this) new Parent()避免了上述的缺點,常用。優點:可傳參,不會與父類引用屬性共享缺點:繼承父類函數的時候調用了父類構造函數,導致子類的原型上多了不需要的父類屬性,存在內存上的浪費。
4.原型式繼承實現本質:object()函數對傳入其中的對象執行了一次淺復制
5.寄生式繼承借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法
6.寄生組合高效率只調用了一次構造函數,集寄生式繼承和組合繼承的優點于一身,是實現基于類型繼承的最有效方式。就是將父類的原型賦值給了子類,并且將構造函數設置為子類,這樣既解決了無用的父類屬性問題Parent.call + Object.create()
7.class繼承
7.this關鍵字
1) this總是指向函數的直接調用者(而非間接調用者)2) 如果有new關鍵字,this指向new出來的那個對象3) 在事件中,this指向目標元素,特殊的是IE的attachEvent中的this總是指向全局對象window。
this大概有以下五種場景:1.綁定事件指向事件本身2.普通函數的,指向方法體。3.new函數的指向當前類4.箭頭函數,指向上級上下文5.call/apply/bind
8.new關鍵字
看以下代碼,這就是new的整體過程。
function createThis( proto ){var obj = new Object;obj.__proto__ = proto.prototype;let [ constructor, ...args] = [ ...arguments ];let result = constructor.apply( obj, args );return typeof result === 'object' ? result : obj; }可以從代碼中看到new的執行過程,新建一個對象,設置原型鏈,改變this指向,根據對象返回結果。
9.類型的判斷
談到js類型的判斷,我們能想起 typeof,instanceof,constructor,Object.prototype.toString.call()。(沒了吧?還有的話提醒我一下)
那么我們對比一下他們的作用與區別。
typeof?對于原始類型來說,除了 null 都可以顯示正確的類型。但是對于對象來說,除了函數都會顯示 object,所以他的作用,僅僅只能判斷原始類型,判斷不了對象。
instanceof,用于判斷一個變量是否某個對象的實例,內部機制是通過原型鏈來判斷的。他的確能判斷是否類型的是否正確。但一點值得注意,instanceof 檢測的是原型,原型鏈上,每一個類型,都會返回true。所以,只能用來判斷兩個對象是否屬于實例關系, 而不能判斷一個對象實例具體屬于哪種類型。
constructor, 是原型prototype的一個屬性,當函數被定義時候,js引擎會為函數添加原型prototype,并且這個prototype中constructor屬性指向函數引用, 因此重寫prototype會丟失原來的constructor。
但是他也有明顯的缺陷:
1:null 和 undefined 無constructor,這種方法判斷不了。2:還有,如果自定義對象,開發者重寫prototype之后,原有的constructor會丟失,因此,為了規范開發,在重寫對象原型時一般都需要重新給 constructor 賦值,以保證對象實例的類型不被篡改。
toString是幾個方案中,相對比較不錯的方案。建議使用。toString() 是 Object 的原型方法,調用該方法,默認返回當前對象的 [[Class]] 。這是一個內部屬性,其格式為 [object Xxx] ,其中 Xxx 就是對象的類型。
10.類型的轉換
js類型的轉換,可以分為三種情況:
轉換為布爾值
轉換為數字
轉換為字符串
其中,轉化為boolean,除了 undefined, null, false, NaN, '', 0, -0,其他所有值都轉為 true。我們日常可以用它來判斷對象是否未賦值。
11.比較運算符
比較運算符,是我們常用到的。如果都為number類型,比較值的大小,那么當然簡單咯。如果是非number值的時候如何處理呢?
順序如下:將值轉換為原始值(ToPrimitive方法)轉換為數字(valueOf方法)轉換為字符串(toString方法)
12.四則運算符
這里筆者的記憶是這樣的,分為兩類:
加法類:只要有運算有字符串,那么將全部轉為字符串。如果不是字符串(且數字),那就把它轉換為(字符串)或數字。
那么如何判斷先轉為數字還是轉為字符串呢?這涉及到加法運算會觸發三種類型轉換。參考“比較運算符”,ToPrimitive方法。
非加法類:只要其中一方是數字,那么另一方就轉為數字。
13.拷貝
拷貝,任何語言都有自己的深拷貝以及淺拷貝。深拷貝有利于數據的完全獨立,但是全是深拷貝的話,內存又不會不斷的往上漲,于是又有了淺拷貝。
淺拷貝指拷貝引用對象,仍指向同一個地址,修改時原對象也會受到影響.。
深拷貝完全拷貝一個新對象,修改時原對象不再受到任何影響
基于內存的節省,我們日常用到的函數,很多都屬于淺拷貝,比如我們的擴展運算符,還有Object.assign,contact,slice等。都屬于淺拷貝。
而深拷貝:
* 可以使用JSON.parse(JSON.stringify(obj))。性能最快。其弊端也必將明顯,首先無法拷貝函數、undefined、或symbol等值。其二對象要是有自身循環調用,會報錯。 * 利用遞歸來實現每一層都重新創建對象并賦值 * 如何用jquery,可以考慮,$.extend( [deep ], target, object1 [, objectN ] ),這也是深拷貝的一種。 * 也可以利用lodash.js,cloneDeep方法進行深拷貝。14.函數調用
js的函數調用,有四種方式:
1.方法調用模式(this指向他本身)
2.函數調用模式(this指向windows)
3.構造器調用模式(利用原型構造,JS摒棄這個方法)
4.apply調用模式(利用apply改變this對象。)
函數調用,自身攜帶的,記住有 this 和 arguments
15.高階函數
接收函數作為參數或者返回函數的函數,都可成為高階函數。所以常見的方法有:map,filter,bind,apply等。
需要了解一下,高階函數實現AOP。
16.柯里化函數
柯里化,實現上,就是返回一個高階函數,通過閉包把傳入的參數保存起來。當傳入的參數數量不足時,遞歸調用 bind 方法;數量足夠時則立即執行函數。學習一下 javascript 的高階用法還是有意義的。
柯里化是一種將使用多個參數的一個函數轉換成一系列使用一個參數的函數的技術。
17.數組
數組的方法可以寫的實在是太多了。借助一下這位小伙伴的博客:juejin.im/post/684490…
18.偽數組
偽數組,說明它不是真正意義上的數組,他的輸出是個對象,但他的原型并不指向Array。
常見的偽數組包括:arguments、getElementsByTagName等獲取的NodeList對象
它的特性是:
1)具有length屬性;
2)按索引方式存儲數據;
3)沒有內置方法,不具有數組的push()、pop()等方法
偽數組也可以轉換為數組,可以通過:
var args = Array.prototype.slice.call(arguments);
Array.from(arguments)
擴展運算符
19.重定向this
call,apply,bind,三者都是用來改變函數的this對象的指向的。且第一個參數都是this要指向的對象,也就是想指定的上下文。
但傳參的值也不同,apply后續只能傳遞數組,而call與bind可以傳遞多個參數。
bind 是返回對應函數,便于稍后調用;apply 、call 則是立即調用。
20.嚴格模式
use strict是否很熟悉?了解一下他的大概作用:1) 消除js不合理,不嚴謹地方,減少怪異行為2) 消除代碼運行的不安全之處,3) 提高編譯器的效率,增加運行速度4) 為未來的js新版本做鋪墊。
21.for循環
首先效率問題:for > forEach > map
如何選擇對應的循環呢:
如果需要將數組按照某種規則映射為另一個數組 ? ?map
如果需要進行簡單的遍歷 ??forEach 或者 for of
如果需要對迭代器進行遍歷 ? ??for of
如果需要過濾出符合條件的項 ? ?filter
此外,我們要明白傳統for
這個 for-of 循環首先調用了 values 數組的 Symbol.iterator 方法,獲取了一個迭代器(對 Symbol.iterator 的調用發生在 JS 引擎后臺)。接下來 iterator.next() 被調用,迭代器結果對象的 value 屬性被讀出并放入了第一個結果變量。如果你只是簡單地迭代數組或集合的值,那么使用 for-of 循環而不是 for 循環就是個好主意。for-of 循環一般不易出錯,因為需要留意的條件更少;傳統的 for 循環被保留用于處理更復雜的控制條件。在不可迭代對象、 null 或 undefined 上使用 for-of 語句,會拋出錯誤。
二.前端基礎進階
1)ES6篇
1.模塊化
在以前,js一直沒有模塊化的體系。這就會產生一個問題,當項目到達大型時,很大可能性出現方法重疊,以及安全性問題,成為大型項目的一個痛點與障礙。而es6模塊化正式為此誕生。
這里簡述前端模塊化的區別:
1)AMD, commonJS, 與es6,都屬于預加載類型。而后期引入的CDM是懶加載。何為預加載, 也就是說,在程序調用,所有的模塊都加載完成。而懶加載,是用到什么的時候,才去加載什么。
2)AMD跟cmd專注于前端的規范。而commonjs跟es6 moudle可用于前后端。
3)AMD的代表做為requirejs,cmd的代表作為seajs。commonjs 與 es6,則無需引入,只需要引入編譯器(如babel)即可。seajs為淘寶引入的規范,我們都知道淘寶相對很大,不采用懶加載,首屏的時間將會很長,不過現在已經停止維護。
4)es6 跟 commonJS做了如下改變:
1.ES6只能新增值,無法重新賦值就會報錯2.CommonJS 輸出是值的拷貝,即原來模塊中的值改變不會影響已經加載的該值, ES6靜態分析,動態引用,輸出的是值的引用,值改變,引用也改變,即原來模塊中的值改變則該加載的值也改變。3.CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。4.CommonJS 加載的是整個模塊,即將所有的接口全部加載進來, ES6 可以單獨加載其中的某個接口(方法)。5.CommonJS this 指向當前模塊,ES6 this指向undefined
2.變量聲明
變量聲明(var)會有變量提升。變量會提前初始化,也可以提前訪問。當項目變量復雜的時候,很容易產生bug。es6就在這個時候,引入了let跟const。
當然,引入let與const不僅僅解決了變量提升的問題,他們的不同如下:
1)局部作用域新引入的let,const聲明,再不會再產生變量提升。避免了變量提前訪問的場景,間接的提高了嚴謹性。我們可以在程序運行時就知道了報錯,而非后期的調試中。
2)禁止重復聲明如果一個標識符已經在代碼塊內部被定義,那么在此代碼塊內使用同一個標識符進行 let 聲明就會導致拋出錯誤
3)區分常量與變量這是let與const的區別。const 聲明會阻止對于變量綁定與變量自身值的修改,避免了我們日常開發中,了不小心改到常量的問題。
4)暫時性死區下述案例,用let跟var定義的結果,就明白什么叫暫時性死區
for( let i = 0; i<10; i++ ){setTimeOut( function(){alert(i );}, 1000); }3.Symbol
Symbol是JS新引入的基本類型。我們都知道在ES5之前,JS 已有的基本類型(字符串、數值、布爾類型、 null 與 undefined )之外, ES6 引入 了一種新的基本類型。
符號起初被設計用于創建對象私有成員,而這也是 JS 開發者期待已久的特性。在符號誕生之前,將字符串作為屬性名稱導致屬性可以被輕易訪問,無論命名規則如何。而“私有名稱”意味著開發者可以創建非字符串類型的屬性名稱,由此可以防止使用常規手段來探查這些名稱。
我們常用于:1.作為內置屬性名稱??梢员苊馔瑓得母采w。2.使用Symbol來替代常量。Symbol來創建一些常量。比如訂單狀態等,可以也可以避免重復。
4.數組的擴展
需要明白Array.of跟Array.form的意義。
首先上述提到,數組有了偽數組的概念,而轉化為數組,可以通過 Array.prototype.slice.call(arguments)。但是這個方法并不直觀,所以引入了更為直觀的Array.form。
只要是部署了iterator(下邊會提及)接口的數據結構,Array.from都能將其轉為數組。
而Array.of是為了解決new Array()的嚴謹性的問題。new Array( )后邊的值,可能代表長度,可能代表數值。
Array.of基本上可以用來替代Array()或newArray(),并且不存在由于參數不同而導致的重載,而且他們的行為非常統一。
5.函數的擴展
es6對函數的擴展,主要針對兩個,一個是箭頭函數,一個是解構函數。
箭頭函數跟普通函數的區別:
(1)用了箭頭函數,this就不是指向window,而是父級(指向是可變的)。
(2)不能使用arguments對象。
(3)不能用作構造函數,這就是說不能夠使用new命令,否則會拋出一個錯誤。
(4)不可以使用yield命令,因此箭頭函數不能用作Generator函數
這里,簡單提及解構函數,解構數組,以及字符串模版等概念。
6.Map,Set,WeakMap與WeakSet
數組在 JS 中的使用正如其他語言的數組一樣,但缺少更多類型的集合導致數組也經常被當作隊列與棧來使用。數組只使用了數值型的索引,而如果非數值型的索引是必要的,開發者便會使用非數組的對 象。
MapMap與Object,其最本質的區別,鍵值對的集合(Hash 結構),但是傳統上只能用字符串當作鍵。
對于Map來說,undefined和null是兩個不同的鍵,布爾值true和字符串true是兩個不同的鍵,而NaN之間視為同一個鍵 ,0和-0也是一個鍵,
const map = new Map(); map.set(['a'], 1); map.get(['a'])會輸出underfined。
WeakMap
WeakMap跟Map結構類似,也是用于生成鍵值對的集合,但是他只能用對象,來作為鍵值。其次,WeakMap的鍵名所指向的對象,不計入垃圾回收機制。
WeakMap 與 Map 在 API 上的區別主要是兩個,一是沒有遍歷操作(即沒有keys()、values()和entries()方法),也沒有size屬性。因為沒有辦法列出所有鍵名,某個鍵名是否存在完全不可預測,跟垃圾回收機制是否運行相關。這一刻可以取到鍵名,下一刻垃圾回收機制突然運行了,這個鍵名就沒了,為了防止出現不確定性,就統一規定不能取到鍵名。二是無法清空,即不支持clear方法。因此,WeakMap只有四個方法可用:get()、set()、has()、delete()。
WeakMap的實例比較少,個人從來沒有在實踐中使用。但有這么一個實例相對適合:比如我們要統計一個頁面統計該頁面所有節點的點擊次數。
其一,首先我們獲取到的dom是一個對象,符合作為鍵值。
其二,當對應的節點消失的時候,垃圾回收機制,回自動回收對應的在WeakMap節點,同時達到釋放內存的目的
SetSet可能相對更好理解,他可以簡單理解為是一個“無重復值”的“有序”列表,且運行值方便快速訪問以及判斷。
我們可以利用他去重。包括數組,字符串等。
也可以利用他去接受一些具有 iterable 接口的其他數據結構,例如我們統計頁面有幾個div?new Set(document.querySelectorAll('div'));
WeakSet跟WeakMap類似,還是兩個關鍵字:“對象”,“內存”。
7.iterator
迭代器iterator, 可以理解成一個為不同的數據結構,統一訪問的機制(Symbol.iterator屬性)。只要對應的數據結構有Symbol.iterator屬性,就可以完成遍歷操作。
function createIterator(items) {var i = 0;return {next: function() {var done = (i >= items.length);var value = !done ? items[i++] : undefined;return {done: done,value: value};}}; } var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"我們的字符串,數組、類數組的對象、Set和Map,都具備Iterator接口。所以他們都是可迭代對象。
可迭代的作用有三個:
1.為各種數據結構,提供一個統一的、簡便的訪問接口;
2.是使得數據結構的成員能夠按某種次序排列;
3.是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。
常用到iterator的場景包括:
1.for...of循環
2.擴展運算符
3.解構賦值
4.yield_yield后面跟的是一個可遍歷的結構
5.數組的遍歷會調用遍歷器接口
8.Generator
嚴格來說generator(生成器)屬于ES5,并不是ES6。但由于涉及迭代器等,所以并入es6模塊。
生成器( generator )是能返回一個迭代器的函數。生成器函數由放在 function 關鍵字之 后的一個星號( * )來表示,并能使用新的 yield 關鍵字。將星號緊跟在 function 關鍵 字之后,或是在中間留出空格,都是沒問題的.
形式上,Generator 函數是一個普通函數,但是有兩個特征。一是,function關鍵字與函數名之間有一個星號;二是,函數體內部使用yield表達式,定義不同的內部狀態(yield在英語里的意思就是“產出”)。
Generator有著"停止","開始"的狀態,那我們可以用他來控制異步編程,所以,他也是異步的解決方案之一。
Generator要next一步一步往下執行。如果想一步執行,可以借助Thunk 函數(當然他的原理也是遍歷幫我們執行了next。)
9.Promise
Promise 被設計用于改善 JS 中的異步編程,與事件及回調函數對比,在異步操作方面為你提供了更多的控制權與組合性。Promise 調度被添加到 JS 引擎作業隊列,以便稍后執行。不過此處有另一個作業隊列追蹤著 Promise 的完成與拒絕處理函數,以確保適當的執行。
Promise 具有三種狀態:掛起、已完成、已拒絕。一個 Promise 起始于掛起態,并在成功時轉為完成態,或在失敗時轉為拒絕態。在這兩種情況下,處理函數都能被添加以表明Promise 何時被解決。
Promise的缺陷:
1)無法取消Promise,一旦新建它就會立即執行,無法中途取消。
2)如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。
3)當處于pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
4)then的寫法相比await,明顯在程序代碼抒寫上,更加繁瑣。
10.proxy 跟 Reflect
proxy:代理對目標對象進行了虛擬,因此該代理與該目標對象表面上可以被當作同一個對象來對待。代理允許你攔截在目標對象上的底層操作,而這原本是 JS 引擎的內部能力。攔截行為使用了一個能夠響應特定操作的函數(被稱為陷阱)。
Reflect:是給底層操作提供默認行為的方法的集合,這些操作是能夠被代理重寫的。每個代理陷阱都有一個對應的反射方法,每個方法都與對應的陷阱函數同名,并且接收的參數也與之一致。
JS 運行環境包含一些不可枚舉、不可寫入的對象屬性,然而在 ES5 之前開發者無法定義他們自己的不可枚舉屬性或不可寫入屬性。ES5引入了 Object.defineProperty() 方法以便開發者在這方面能夠像 JS 引擎那樣做。
ES6 讓開發者能進一步接近 JS 引擎的能力,這些能力原先只存在于內置對象上。語言通過代理( proxy )暴露了在對象上的內部工作,代理是一種封裝,能夠攔截并改變 JS 引擎的底層操作。
11.Class寫法
Class寫法,可以簡單理解成ES6的一個語法糖。我們日常用他所實現的功能,其實用ES5都可以做到,但是class的寫法,讓對象原型的寫法更加清晰。但不僅僅是糖語法。
1.首先Class的寫法會有特殊內部屬性標記[[FunctionKind]]:"classConstructor",這個標記了,如果沒有new,則無法調用類構造函數
2.類方法是不可枚舉的
3.Class是使用嚴格模式的。
此外,我們需要了解一下Class寫法中關鍵super、static、constructor、new.target。本文不做詳細介紹。
2)瀏覽器篇
1.瀏覽器的儲存
cookie,localStorage,sessionStorage.IndexedDB
比較一下差異:
1)傳遞方式:cookie在瀏覽器和服務器間來回傳遞;sessionStorage和localStorage不會自動把數據發給服務器,僅在本地保存;
2)存儲大小:localStorage<=5M;sessionStorage<=5M;cookie<4K;(ie內核瀏覽器占主流地位,且ie6仍占有相當大的市場份額,所以在程序中應當使用少于20個cookie,且不大于4k)
3)有效性:localStorage:始終有效,窗口或瀏覽器關閉也一直保存,因此用作持久數據;sessionStorage:僅在當前瀏覽器窗口關閉前有效,不能持久保持;cookie:只在設置的cookie過期時間之前一直有效,即使窗口或瀏覽器關閉也不會消失;
4)共享機制:localStorage :在所有同源窗口中都是共享的;sessionStorage:同時“獨立”打開的不同窗口,即使是同一頁面,sessionStorage對象也是不同的;cookie:在所有同源窗口中都是共享的
5)瀏覽器支持:sessionStorage的瀏覽器最小版本:IE8、Chrome 5。
6)使用場景cookie:保存回話信息localStorage:持久保存的數據sessionStorage:擁有獨立特性的數據
2.瀏覽器的緩存
1.Service Worker 是運行在瀏覽器背后的獨立線程。必須HTTPS。
三個步奏:注冊(下載:sw.js),監聽(等其他worker失效),查看緩存1)sw線程能夠用來和服務器溝通數據(service worker的上下文內置了fetch和Push API)2)能夠用來進行大量復雜的運算而不影響UI響應。3)它能攔截所有的請求
2.Memory Cache將資源緩存在了內存中。事實上,所有的網絡請求都會被瀏覽器緩存到內存中,當然,內存容量有限,緩存不能無限存放在內存中,因此,注定是個短期緩存。內存緩存的控制權在瀏覽器,前后端都不能干涉。
3.Disk Cache存儲在硬盤中的緩存強緩存和協商緩存, HTTP Header 來實現的。Cache-Control > Expires(http1.0產物, 受本地時間影響) > ETag(http1.1出現) > Last-Modified(Last-Modified 打開文件的時候會變,以秒計算的)
4.Push Cache
服務器推送,http2
3.瀏覽器的渲染
生成dom樹:字節數據-->字符串-->標記(token)-->node-->dom
生成css樹:字節數據-->字符串-->標記(token)-->node-->cssdom
整體的渲染過程:
1)處理 HTML 并構建 DOM 樹。
2)處理 CSS 構建 CSSOM 樹。
3)將 DOM 與 CSSOM 合并成一個渲染樹。
4)根據渲染樹來布局,計算每個節點的位置。
5)調用 GPU 繪制,合成圖層,顯示在屏幕上。
兩個重要的概念,重繪與回流:
重繪:當節點需要更改外觀而不會影響布局的,比如改變 color 就叫稱為重繪
回流:布局或者幾何屬性需要改變就稱為回流。回流必定會發生重繪,重繪不一定會引發回流?;亓魉璧某杀颈戎乩L高的多,改變深層次的節點很可能導致父節點的一系列回流。當 Event loop 執行完 Microtasks 后,會判斷 document 是否需要更新。因為瀏覽器是 60Hz 的刷新率,每 16ms 才會更新一次。
導致性能問題:
1)改變 window 大小
2)改變字體
3)添加或刪除樣式
4)文字改變
5)定位或者浮動
6)盒模型
減少重繪和回流的細節:
1)使用 translate 替代 top
2)使用 visibility 替換 display: none ,因為前者只會引起重繪,后者會引發回流(改變了布局)
3)盡量算出結果再去重繪把 DOM 離線后修改,比如:先把 DOM 給 display:none (有一次 Reflow),然后你修改 100 次,然后再把它顯示出來
4)動畫實現的速度的選擇,動畫速度越快,回流次數越多,也可以選擇使用 requestAnimationFrame ?Load 和 DOMContentLoaded 區別。Load 事件觸發代表頁面中的 DOM,CSS,JS,圖片已經全部加載完畢。DOMContentLoaded 事件觸發代表初始的 HTML 被完全加載和解析,不需要等待 CSS,JS,圖片加載。
4.瀏覽器的安全
1.xss跨站腳本攻擊原理:(1)構造URL (2)發布內容式 (3)蠕蟲式
2.CSRF跨站請求偽造1)驗證碼。2)HTTP Referer是header的一部分 3)token
3.sql腳本注入拼接腳本
4.上傳漏洞
(1)檢查服務器是否判斷了上傳文件類型及后綴。(2) 定義上傳文件類型白名單,即只允許白名單里面類型的文件上傳。(3) 文件上傳目錄禁止執行腳本解析,避免攻擊者進行二次攻擊。
5.瀏覽器的跨域
首先什么是跨域,違反瀏覽器同源策略的就是跨域。跨域本身就是就是為了保護瀏覽器的安全, 主要是用來防止 CSRF 攻擊的
那什么是同源策略?所謂的同源,指的是協議,域名,端口相同。瀏覽器處于安全方面的考慮,只允許本域名下的接口交互,不同源的客戶端腳本,在沒有明確授權的情況下,不能讀寫對方的資源。
解決同源策略的方案:
1)sonp
2)iframe
3)postMessage
4)CORS
5)webscoket
6)反向代理服務器
6.瀏覽器的內存
瀏覽器(通常指)的內存分配,64位系統下大約為1.4GB,在32位系統下大約為0.7G。
我們通常定義變量時候就完成了分配內存,使用時候是對內存的讀寫操作,內存的釋放依賴于瀏覽器的垃圾回收機制。
造成內存泄露
1.意外的全局變量引起的內存泄漏。
2.閉包引起的內存泄漏
3.沒有清理的DOM元素引用
4.被遺忘的定時器或者回調
5.監聽事件
7.瀏覽器的垃圾回收
64位下新生代的空間為64M,老生代為1400M32位下新生代的空間為16M,老生代為700M.
javaScript使用垃圾回收機制來自動管理內存,垃圾回收是一把雙刃劍
優勢:可以大幅度簡化程序的內存管理代碼,降低程序的負擔,減少因時常運轉而帶來的內存泄露問題。
劣勢:意味著程序員將無法掌控內存。js沒有暴露任何關于內存的API。我們無法強迫其進行垃圾回收,也無法干預內存管理。
1、V8最初是為了瀏覽器設計的,不太可能遇到大內存的場景2、js垃圾回收的時候程序會暫停線程執行,會占用一定時間。
它有兩種情況會回收,一種是定時回收,一種內存不夠了回收。
1.新生代算法Scavenge GC(GC 復制算法)分為兩個空間:form 跟 to。
2.老生代算法標記清除算法標記壓縮算法
對垃圾回收算法有興趣的朋友:www.jianshu.com/p/a8a04fd00…
8.瀏覽器的執行機制
javascript是一門單線程語言, Event Loop是javascript的執行機制libuv
需明白什么叫事件循環事件,微任務,宏任務。以及如何運行。
可了解:juejin.im/post/684490…
不知覺中,文章已來到了2W字。貌似掘金的限制是2W字?筆者決定分開多篇文章來匯總,避免有人關心下一章節的內容,所以本文先給出大概菜單,預計時間一周以內,有興趣敬請關注!
三.鞏固前端基建
1)加深鞏固篇
1.前端去重的方法有哪些?
2.前端異步的方案有哪些?
3.前端網絡請求有哪些?
4.前端定時器有哪些?
5.前端創建對象有哪幾種方式?
6.前端代碼的復用有哪幾種方式?
2)工具拓展篇
1.wepback
2.nignx
3.csr與ssr
4.web Worker
3)網絡協議篇
TCP三次握手,DNS解析...
4)設計模式篇
單例模式,觀察者模式....
5)前端算法篇
排序算法等...
四.手寫原生代碼
apply, call, bind, new...
五.前端框架
1)Vue
mvvn,數據劫持,router,vuex...
2)React
redux,hooks...
3)微信小程序
自定義組件,生命周期...
4)hybrid app
六.前端性能
1)如何跟蹤
perfomance lighthouse...
2)如何優化
從網絡出發,從渲染出發...
七.前端素養
職業生涯規劃,對前端的看法...
最后
看完點個贊,分享一下吧,讓更多的朋友能夠看到。如果你喜歡前端開發博客的分享,就給公號標個星吧,這樣就不會錯過我的文章了。
好文和朋友一起看~
總結
以上是生活随笔為你收集整理的落魄前端,整理给自己的前端知识体系复习大纲(上篇,2w字)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用C# .net开发微信公众号之服务器
- 下一篇: 众昂矿业:萤石因奇特的“荧光效应”而得名