作者:逐漸前行
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。由這四個組成我們的"盒子"。
我們日常可能會遇到不同的浏覽器,元素的高寬不一緻。除了可能是浏覽器内置的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.作為内置屬性名稱。可以避免同參數名的覆寫。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 就叫稱為重繪
- 回流:布局或者幾何屬性需要改變就稱為回流。回流必定會發生重繪,重繪不一定會引發回流。回流所需的成本比重繪高的多,改變深層次的節點很可能導緻父節點的一系列回流。當 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)如何優化
從網絡出發,從渲染出發...
七.前端素養
職業生涯規劃,對前端的看法...
最後
看完點個贊,分享一下吧,讓更多的朋友能夠看到。如果你喜歡前端開發部落格的分享,就給公号标個星吧,這樣就不會錯過我的文章了。



好文和朋友一起看~