- debounce 和 thrrow
原理
1.throttle 比如電梯睬愤。保證如果電梯第一個(gè)人進(jìn)來(lái)后蚂且,15秒后準(zhǔn)時(shí)運(yùn)送一次,不等待。如果沒(méi)有人炭庙,則待機(jī)饲窿。
2.debounce 如果電梯里有人進(jìn)來(lái),等待15秒焕蹄。如果又人進(jìn)來(lái)逾雄,15秒等待重新計(jì)時(shí),直到15秒超時(shí)腻脏,開始運(yùn)送鸦泳。
// input 輸入的時(shí)候
function debounce (fun,time){
let timer = null
return function(...arg){
// 下次來(lái)的清理到上次的
clearTimeout(timer)
timer = setTimeout(()=>{fun.apply(this,arg)},time)
}
}
const input = document.getElementById("input")
function inputFun(){
console.log(1111)
}
input.addEventListener("input",thrrow2(inputFun,2000))
function thrrow(fun,dely){
let canrun = true
return function(...arg){
if(canrun){
canrun = false
fun.apply(this,arg)
// 利用 setTimeout 做開關(guān)
setTimeout(()=>{
canrun = true
},dely)
}
}
}
// 利用時(shí)間差計(jì)算
function thrrow2(fun,dely){
let time = new Date()
return function(...arg){
let newDate = new Date()
if(newDate-time>dely){
time = new Date()
fun.apply(this,arg)
}
}
}
- Set Map weakSet weakMap
Set
Set 更像數(shù)組,但是值是唯一的永品,且無(wú)序的
Set(iterator) 構(gòu)造函數(shù)入?yún)⒅恢С?iterator 可迭代的對(duì)象,
let set = new Set([1,2,3,4])
let set2 = new Set("1234")
set 對(duì)象如果建立set
利用iterator 實(shí)現(xiàn)
let obj = {name:"小明",age:"11"}
obj[Symbol.iterator] = function(){
let list = []
Object.keys(obj).forEach((item)=>{
list.push([item,obj[item]])
}
)
return {
list,
index:0,
len:list.length,
next(){
if(this.index<this.len){
return{
value:this.list[this.index++],
done:false
}
}
return {
done:true,
value:undefined
}
}
}
}
for(var i of obj){
console.log(i)
}
let m = new Set(obj)
實(shí)例方法
// has
// get
// delete
// forEach
// keys
// values
// entries
// keys 和 values 一樣
// entries 返回的{key,value}一致
Map
共同點(diǎn):集合做鹰、字典 可以儲(chǔ)存不重復(fù)的值
不同點(diǎn):集合 是以 [value, value]的形式儲(chǔ)存元素,字典 是以 [key, value] 的形式儲(chǔ)存
let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // 覆蓋引用
// john 被存儲(chǔ)在了 map 中腐碱,
// 我們可以使用 map.keys() 來(lái)獲取它
WeakMap 在這方面有著根本上的不同誊垢。它不會(huì)阻止垃圾回收機(jī)制對(duì)作為鍵的對(duì)象(key object)的回收。
讓我們通過(guò)例子來(lái)看看這指的到底是什么症见。
WeakMap
和 Map
的第一個(gè)不同點(diǎn)就是喂走,WeakMap
的鍵必須是對(duì)象,不能是原始值:
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // 覆蓋引用
// john 被從內(nèi)存中刪除了
new 的實(shí)現(xiàn)
function _new (...s){
let obj=Object.create({})
let fun = s.shift()
let res = fun.apply(obj,s)
obj=__proto__ = fun.prototype
if(Object.prototype.toString.call(res)=="[object Object]"){
return res
}
return obj
}
瀏覽器解析url的過(guò)程
- 1 用戶輸入url :
sche/host121.doman111:popt11/path1/files 1.scheme1:// 自用沒(méi)有任何價(jià)值連接
協(xié)議谋作。如http芋肠,host.主機(jī)名稱www .doman url port 默認(rèn)80端口 path 服務(wù)器地址。files服務(wù)器地址下的文件
2 通過(guò)url解析出主機(jī)名稱
3 通過(guò)主機(jī)名獲得ip地址遵蚜,這個(gè)過(guò)程如果本地緩存列表有帖池,則會(huì)的在本地獲取,如果沒(méi)有則需要在dns上獲取到ip地址吭净,然后緩存(在dns本地緩存的順序是:一.瀏覽器緩存 二 操作系統(tǒng)緩存 三 路由緩存 四 ips 和dns 服務(wù)器緩存 五 根服務(wù)器緩存睡汹,遵循 遞歸查找)
4 瀏覽器解析出 端口號(hào)
-
5 客戶端與服務(wù)端建立tcp 的三次握手
第一次握手:建立連接時(shí),客戶端發(fā)送 SYN 包(seq=x)到服務(wù)器寂殉,并進(jìn)入 SYN_SENT 狀態(tài)囚巴, 等待服務(wù)器確認(rèn)。(SYN:同步序列編號(hào)友扰,Synchronize Sequence Numbers)彤叉;
第二次握手:服務(wù)器收到 SYN 包,必須確認(rèn)客戶的 SYN(ack=x+1)村怪,同時(shí)自己也發(fā)送一個(gè) SYN 包(seq=y)秽浇,即 SYN + ACK 包,此時(shí)服務(wù)器進(jìn)入 SYN_RECEVED 狀態(tài)甚负;
第三次握手:客戶端收到服務(wù)器的 SYN + ACK 包柬焕,向服務(wù)器發(fā)送確認(rèn)包 ACK(ack=y+1)审残,此包發(fā)送完畢,客戶端和服務(wù)器端進(jìn)入 ESTABLISHED(TCP 連接成功)狀態(tài)击喂,完成三次握手维苔。
CP 有6中標(biāo)志位:
- SYN(synchronous 建立連接)
- ACK(acknowledgement 確認(rèn))
- PSH(push 傳送)
- FIN(finish 結(jié)束)
- RST(reset 重置)
- URG(urgent 緊急)
另外數(shù)據(jù)包:
Sequence number(順序號(hào)碼)
Acknowledge number(確認(rèn)號(hào)碼)
第一次握手中 SYN = 1,代表客戶端 Client 要建立連接懂昂,同時(shí)隨機(jī)產(chǎn)生 seq(Sequence number)= x 的數(shù)據(jù)包介时,然后傳輸?shù)?Server;
Server 收到 Client 的信息凌彬,通過(guò) SYN = 1 知道是要建立連接沸柔,然后向 Client 發(fā)送信息,ACK = 1 代表確認(rèn)铲敛,ack(Acknowledge number)= x + 1褐澎,SYN = 1 代表建立連接,隨機(jī)產(chǎn)生 seq = y 的數(shù)據(jù)包伐蒋;
Clien 收到 Server 的信息工三,通過(guò) SYN = 1 知道是要建立連接,然后檢查 ack 是否正確先鱼,即第一次發(fā)送的 seq + 1俭正,同時(shí)檢查 ACK 是否為 1,若正確焙畔,Client 再發(fā)送 ack = y + 1掸读,ACK = 1 到 Server,Server 收到確認(rèn) seq 值和 ACK = 1 后則連接建立成功宏多。
6 客戶端發(fā)出請(qǐng)求報(bào)文儿惫,請(qǐng)求報(bào)文包括:一 請(qǐng)行:http:liuliukun.com,二 請(qǐng)求頭:get伸但、post請(qǐng)求方式肾请、cash、cookie更胖、path筐喳、origin 請(qǐng)求報(bào)文:請(qǐng)求加入的接口數(shù)據(jù)
7 服務(wù)端返回報(bào)文,一 包括相應(yīng)碼函喉,報(bào)文頭:請(qǐng)求的跨越、時(shí)間荣月、content-type管呵,報(bào)文主題:返回請(qǐng)求后的數(shù)據(jù)
1.. 代表是
準(zhǔn)備請(qǐng)求http
2..請(qǐng)求成功的狀態(tài)
200 請(qǐng)求成功
204 請(qǐng)求成功并放回空
206 請(qǐng)求大文件時(shí)候,如下載功能哺窄,部分文件成功
3.. 重定向
301 永久重定向
302 臨時(shí)重定向捐下,后續(xù)還是訪問(wèn)老的地址 304:訪問(wèn)文件未作修改账锹,客戶端在緩存中獲取
4.. 客戶端出錯(cuò)
400:語(yǔ)法出錯(cuò)、
401表示http協(xié)議有問(wèn)題
403表示:服務(wù)器拒絕
404:未找到資源
5.. 服務(wù)器
500 :服務(wù)器運(yùn)行出錯(cuò)
503:服務(wù)器碟機(jī)
- 8 獲取文檔并解析坷襟,
- 9 如果文檔中有其他請(qǐng)求則奸柬,在執(zhí)行678
結(jié)束時(shí),執(zhí)行四次揮手婴程,1 瀏覽器說(shuō)廓奕,我的報(bào)文發(fā)送完成了,準(zhǔn)備關(guān)閉了 2服務(wù)器發(fā)發(fā)請(qǐng)求档叔,表示桌粉,這邊消息,已經(jīng)接受完畢衙四,3 服務(wù)發(fā)送消息铃肯,表示,消息已經(jīng)發(fā)送完成 4 瀏覽器告訴服務(wù)端传蹈。我消息已經(jīng)接受完準(zhǔn)備關(guān)閉
瀏覽器解析
html-->html parse -->domtree layout
| |
(html 和 css 的一個(gè)attch過(guò)程)attach-->padding tree--paining
|
css-->css parse-->css rule
css tree
css rules
selectors declaration
div margin 3px
計(jì)算的權(quán)重規(guī)則
* {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
layout
負(fù)責(zé)計(jì)算頁(yè)面的高度押逼,位置信息的 --改變了css的樣式,如文字大小等高度惦界,位置挑格,都需要重新布局(會(huì)影響其他元素,損耗巨大)
painting
繪制表锻,從新改了頁(yè)面的顏色恕齐,背景圖(損耗小)
cookie瞬逊、session显歧、sessionStorage和localSorage
cookie
- 1 是放在客戶端的
- 體積較小4k,每個(gè)瀏覽器運(yùn)行最多20個(gè),
- 有失效時(shí)間确镊,靠設(shè)置的士骤。如果為負(fù)數(shù)這關(guān)閉頁(yè)面失效。如果為正數(shù)則看看設(shè)置時(shí)間蕾域,如果為0則認(rèn)為是清除 cookie
- cookie是會(huì)通過(guò)http 放在請(qǐng)求報(bào)文頭部傳給服務(wù)端的
- 生成的原因是拷肌,用于保持用戶登錄數(shù)據(jù),讓服務(wù)端接收到請(qǐng)求時(shí)旨巷,先判斷是否sessionId 如果沒(méi)有則生產(chǎn)sessionid 給到cookie,然后瀏覽器保持起來(lái)放在硬盤中
- cookie 是字符串
- cookie 不安全巨缘,容易被竊取
session
- session 是放在服務(wù)端的,用來(lái)處理用戶身份的
- session 是一個(gè)對(duì)象采呐,并且沒(méi)有訪問(wèn)限制
sessionStorage
- 不支持跨域 當(dāng)頁(yè)面消失時(shí),數(shù)據(jù)也會(huì)丟失若锁,不同頁(yè)面也會(huì)消失
- 不會(huì)攜帶http請(qǐng)求
- 最大支持 5m
- 有成熟的api
localStorage
- 不支持跨域 當(dāng)頁(yè)面消失時(shí),數(shù)據(jù)不會(huì)丟失,不同頁(yè)面也會(huì)消失
- 不會(huì)攜帶http請(qǐng)求
- 最大支持 5m
- 有成熟的api
- 可以在相同的域名斧吐,端口號(hào)下 信息共享又固,而session只能在相同的頁(yè)面下
URL 說(shuō)明 是否允許通信
http://www.a.com/a.js
http://www.a.com/b.js 同一域名下 允許
http://www.a.com/lab/a.js
http://www.a.com/script/b.js 同一域名下不同文件夾 允許
http://www.a.com:8000/a.js
http://www.a.com/b.js 同一域名仲器,不同端口 不允許
http://www.a.com/a.js
https://www.a.com/b.js 同一域名,不同協(xié)議 不允許
http://www.a.com/a.js
http://70.32.92.74/b.js 域名和域名對(duì)應(yīng)ip 不允許
http://www.a.com/a.js
http://script.a.com/b.js 主域相同仰冠,子域不同 不允許
http://www.a.com/a.js
http://file.a.com/b.js 同一域名乏冀,不同二級(jí)域名(同上) 不允許(cookie這種情況下也不允許訪問(wèn))
http://www.cnblogs.com/a.js
http://www.a.com/b.js 不同域名 不允許
防止cookie 竊取
- 設(shè)置httpOnly 為true,無(wú)法通過(guò)js腳本獲取cookie
- 合理的時(shí)間內(nèi)修改seeionid
- 使用token (服務(wù)端返回的加密字符、token使用jwt -json web token--) 放在header 中
- 校驗(yàn)頭部信息User-Agent驗(yàn) 等
如果用戶關(guān)閉了cookie怎么辦
通過(guò)url帶cookieid 但很不安全洋只,淘寶關(guān)閉了cookie 直接跳轉(zhuǎn)到了登錄頁(yè)辆沦。
GC 算法
- GC是一種機(jī)制,垃圾回收器完成的具體工作
- 具體工作就是木张,查找垃圾众辨,釋放空間(obj=null),回收空間
- 算法是舷礼,工作時(shí)鹃彻,時(shí)時(shí)查找和回收所遵循的原則
具體算法有 - 引用計(jì)數(shù)
- 標(biāo)記清除
- 標(biāo)記整理
- 分代回收
引用計(jì)數(shù)
- 設(shè)置引用數(shù),判斷當(dāng)前引用數(shù)是否為0
- 引用計(jì)數(shù)器
- 引用關(guān)系修改時(shí)妻献,修改引用數(shù)
let obj1 ={
name:1
}
let obj2 ={
name:2
}
let obj3 ={
name:3
}
let arrlist = [obj1.name,obj2.name,obj3.name]
function fn(){
let name1=1
let name2 =2
}
fn()
// 其中 name1 和name2 在 fn執(zhí)行后蛛株,引用為o被清楚
// 而obj被arrlist 引用1不會(huì)被清楚
優(yōu)點(diǎn)
- 發(fā)現(xiàn)垃圾立即處理
- 最大程度減少程序暫停 // 應(yīng)為是時(shí)時(shí)處理
缺點(diǎn)
- 無(wú)法清楚相互引用的狀態(tài)
- 時(shí)間開銷大,因?yàn)槿绻婕按罅繑?shù)據(jù)刪除時(shí)候
標(biāo)記清除
let a:{b:cffdfpnt} //標(biāo)記
function (){
name.next =name2 // 沒(méi)有被標(biāo)記
name2.pre = name1 gf
}
- 核心思想:分標(biāo)記和清除兩個(gè)階段
- 遍歷所有可達(dá)對(duì)象 a.b.c.d 標(biāo)記
- 清楚所有沒(méi)有標(biāo)記的列表(并且清除標(biāo)記的部分育拨,把所有回收空間直接放在空閑列表上)
優(yōu)點(diǎn)
可以解決上面函數(shù)內(nèi)部相互引用的 引用計(jì)數(shù)無(wú)法清楚的問(wèn)題
缺點(diǎn)
會(huì)照成空間碎片的問(wèn)題
global
|
V
a頭域1域2 b頭 域1 c頭域1
| |
|—————空閑列表----|
只有 b被標(biāo)記谨履,ac會(huì)被清除,但是由于清除的a,有兩個(gè)字節(jié)
c有一個(gè)字節(jié)熬丧。而且abc是連續(xù)地址笋粟,假設(shè)空閑列表只有這么多了,在生成1.5個(gè)字節(jié)時(shí)析蝴,會(huì)照成一個(gè)多害捕,一個(gè)不夠用
標(biāo)記整理
于標(biāo)記清除唯一不同的是,在標(biāo)記清除的空間回收前闷畸,會(huì)做一個(gè)整理階段尝盼,會(huì)把清除,和使用的內(nèi)存空間整理開來(lái)佑菩,防止出現(xiàn)空間碎片
V8
什么是v8
- v8是javascript 主流的執(zhí)行引擎
- v8 采用及時(shí)編譯
- v8 有內(nèi)存上線盾沫,64g電腦 1.5g,32g電腦800m,為了配合垃圾回收機(jī)制殿漠,過(guò)大的內(nèi)存赴精,清理垃圾更加耗時(shí),影響瀏覽器速度
分代回收
v 8內(nèi)存分配
新生代 老生代
from to 老生代存檔區(qū)
新生代
- 小空間存儲(chǔ)新生代(32|16)
- 新生代是存儲(chǔ)時(shí)間交端的內(nèi)存绞幌,如在函數(shù)里面的變量
新生代處理邏輯
- 使用標(biāo)記整理和復(fù)制算法
- 新生代將內(nèi)存分配為2個(gè)大小相等的空間蕾哟,
- 使用空間為from,限制空間為to
- 在使用的時(shí)候,把內(nèi)存分到 from
- 當(dāng)使用的空間達(dá)到一定程度時(shí),使用標(biāo)記整理渐苏,使得內(nèi)存空間連續(xù)化
- 整理完成后,講form 拷貝到to
- 在把from的內(nèi)容清除掉
內(nèi)存晉升
在拷貝的時(shí)候發(fā)現(xiàn)菇夸,當(dāng)前清除的空間發(fā)現(xiàn)琼富,在老生代也存在的時(shí)候
場(chǎng)景
- 當(dāng)經(jīng)歷了一輪gc 操作發(fā)現(xiàn)空間還在的時(shí)候
- 如果to 使用到了 25%的時(shí)候,將內(nèi)存分到老生代里面為了 下次to空間的存儲(chǔ)
老生代
- 在內(nèi)存中存活較長(zhǎng)的數(shù)據(jù)庄新,比如全局作用下的數(shù)據(jù)鞠眉,和閉包數(shù)據(jù)
- 老生代內(nèi)存大小為1.4g/700m
原理實(shí)現(xiàn) - 主要采用,標(biāo)記清楚择诈,標(biāo)記整理械蹋,增量標(biāo)記算法
- 當(dāng)程序運(yùn)行的時(shí)候是標(biāo)簽清楚
- 當(dāng)新生代的晉升數(shù)據(jù)時(shí),并且老生代空間不足的情況
- 執(zhí)行的過(guò)程會(huì)出現(xiàn)切片情況羞芍,停止js運(yùn)行哗戈,分段的去垃圾清除
新老對(duì)比
- 由于新生代是用空間換時(shí)間,基于內(nèi)存小荷科,老生代一份為2 比較浪費(fèi)唯咬, 不適合復(fù)制算法
- 老生代內(nèi)容大,采用分量標(biāo)記畏浆,處理
內(nèi)存問(wèn)題的外在表現(xiàn)
- 加載延遲
- 操作不順胆胰,內(nèi)存過(guò)大導(dǎo)致 設(shè)備開啟更多大的空間
- 性能伴隨時(shí)間越長(zhǎng),越差
監(jiān)控內(nèi)存的方式
存儲(chǔ)問(wèn)題的總監(jiān)
- 內(nèi)存泄露 --> 內(nèi)存持續(xù)走高沒(méi)有下降
- 內(nèi)存膨脹 --> 設(shè)備為了性能開辟了更大的空間
- 平凡的垃圾回收 --->內(nèi)存變化圖
快照查找分離dom
分離dom 占用了空間
let tempDom
function fn () {
let ul = document.createElement("ul")
for(let i=0 ;i<10;i++){
let li = document.createElement("li")
ul.appendChild(li)
}
tempDom = ul
}
let add = document.getElementById("add")
add.addEventListener("click",fn)
在 chrom 瀏覽器刻获,中的memery中查找deta蜀涨,會(huì)發(fā)現(xiàn)有分離dom,雖然沒(méi)有使用蝎毡,但沒(méi)js引用
優(yōu)化方式
1 慎用全局變量
- 會(huì)導(dǎo)致垃圾回收到結(jié)束時(shí)才使用
- 是作用域的頂端厚柳,查找使用時(shí),
- 會(huì)照成污染
2 全局緩存系統(tǒng)的方法
// 例子1
document.getElementById("ll")
document.getElementById("ll")
document.getElementById("ll") document.getElementById("ll")
例子2
let doc = document
doc.getElementById("ll")
doc.getElementById("ll") doc.getElementById("ll")
在jsperf 上性能會(huì)提升很多
3通過(guò)原型鏈的方法替代構(gòu)造函數(shù)本身的方法
function Person(){
this.mame = funciton (){}
}
unction Person1(){
}
Person1.prototype.name=funciton (){}
4 解決閉包的內(nèi)存泄漏
funciton fn (){
let m = document.getElementByid("lk")
m.onclick=funciotn(){
console.log(m.id) //調(diào)用了外部的值形成了閉包
}
m = null
}
fn()
5 避免訪問(wèn)方法的使用
function Person(){
this.name = "lk"
this.getName=function (){
return this.name
}
}
let person = new Person()
// person.name 的方式速度優(yōu)于 person.getName()
6 最快的循環(huán)方式
forEach >for>for in
7 減少作用域的查找
let name = 1
funtion f1(){
return mame
}
funtion f2(){
let name =2
return mame
}
// f2 會(huì)減少內(nèi)存查找顶掉,但會(huì)開辟跟多的空間
8 數(shù)據(jù)的創(chuàng)建方式
- 引用類型的時(shí)候草娜,字面量和new的速度差不多
- 基本數(shù)據(jù)類型的時(shí)候,字面量快痒筒,因?yàn)閚ew的是一個(gè)對(duì)象宰闰,而a 是個(gè)字符串,但a還是調(diào)用字符串的方法簿透,是因?yàn)殡[式轉(zhuǎn)換的
let a = "111"
let b = new String("1111")
9 減少在for循環(huán)里面的數(shù)據(jù)操作移袍,while 會(huì)快于for
正則
[1234] 表示其中一個(gè)
(1234) 表示一個(gè)整體
- 除了
+ 1 到任何個(gè)
d 數(shù)字
D 除了數(shù)字
s 空格換行符
S 除了空格換行符
w 字母數(shù)字下劃線
W 除了字母數(shù)字下劃線
. 除了換行符
http問(wèn)題
http 緩存
- 強(qiáng)緩存
- 協(xié)商緩存
開始
|
V 符合時(shí)間內(nèi),和hash相同
|----------->---瀏覽器——————304————————————————|
| | 服務(wù)器請(qǐng)求
| V ---------|-------
^ 發(fā)起請(qǐng)求 | |
| | 請(qǐng)求頭:(hash)if-no-match if-modify-since(時(shí)間)
| V | |
------<-----是否有緩存(本地)---->返回頭Etags---no--頭last-modify
強(qiáng)緩存
不需要去服務(wù)器獲取數(shù)據(jù)老充,從本地獲取葡盗,serviceWorker 、memery cashe啡浊、dask cache觅够。
這些緩存得設(shè)置由 Expires胶背、 cache control、pragma三個(gè)決定pragma的級(jí)別最高
expires
expires 設(shè)置時(shí)間喘先,如果系統(tǒng)時(shí)間大于了 expires的時(shí)間钳吟,緩存失效,由于時(shí)間放在前端窘拯,導(dǎo)致了時(shí)間的不準(zhǔn)確红且,這個(gè)優(yōu)先級(jí)最低
cache-control
- max-age 超過(guò)時(shí)間緩存失效
- no-cache 沒(méi)有強(qiáng)緩存,會(huì)遵循協(xié)商緩存
- no-store 沒(méi)有緩存包括協(xié)商緩存
- private 專用于前端緩存涤姊,不包括中間cdn暇番,中間代理
- public 緩存過(guò)程中使用,后期必須服務(wù)區(qū)校驗(yàn)
pragma
唯一一個(gè) no-cache 優(yōu)先級(jí)最高
協(xié)商緩存
用于和服務(wù)器校驗(yàn)的
- Etag if-no-match,用于判斷文件的hash 值是否改變思喊,如果沒(méi)改變壁酬。返回304
- last-modify if-modify-since 用于判斷修改時(shí)間,服務(wù)器會(huì)根據(jù)最后一次修改時(shí)間左判斷搔涝,如果沒(méi)有修改返回304
http1.0和http1.x的區(qū)別
- http默認(rèn)打開了keep-alive
- 增加了 Etags等If-Unmodified-Since, If-Match, If-None-Match控制緩存的屬性
- 支持了斷點(diǎn)傳出厨喂,返回206
- 增加了host 頭處理--應(yīng)對(duì)一個(gè)服務(wù)器對(duì)接多個(gè)虛擬機(jī)的情況
http2.0和http1.x的區(qū)別
- 原來(lái)的tcp 傳輸只能支持6-8個(gè)。單個(gè)tcp 會(huì)出現(xiàn)堵塞庄呈,二http 可以實(shí)現(xiàn)多路復(fù)用蜕煌,原因是练般,http2 使用了二進(jìn)制分流的概念蛉顽,而http使用的報(bào)文傳輸,無(wú)法區(qū)分不同的流
- 對(duì)header 經(jīng)行了壓縮
- 服務(wù)器可發(fā)起推送
http3.0與http2.0
解決單個(gè) tcp 堵塞問(wèn)題幅虑,使用udp+加密+控流+丟包重新穿的技術(shù)
https 和http 的差別
http應(yīng)用層 https應(yīng)用層
| |
V V
Tcp傳輸層 ssl/tls 安全層
| |
V V
ip網(wǎng)咯層 tcp-->ip
瀏覽器 發(fā)送隨機(jī)數(shù) random1 + 支持的加密算法列表 到服務(wù)器
服務(wù)器 發(fā)送隨機(jī)數(shù) random2 + 選擇的加密算法 + 數(shù)字證書(公鑰在證書里) + 確認(rèn)信息
瀏覽器 驗(yàn)證證書有效性
瀏覽器 生成一個(gè)隨機(jī)數(shù) pre-master文兑,采用非對(duì)稱加密通過(guò)服務(wù)器的公鑰加密發(fā)送給服務(wù)器盒刚,同時(shí)根據(jù)加密算法將 random1 + random2 + pre-master 生成 master-secret 用于后續(xù)數(shù)據(jù)傳輸
服務(wù)器 用私鑰解密得到 pre-master,根據(jù)算法將 random1 + random2 + pre-master 生成 master-secret用于數(shù)據(jù)傳輸
對(duì)稱加密绿贞,瀏覽器和服務(wù)器之間使用 master-secret 加密的數(shù)據(jù)進(jìn)行通信
對(duì)稱加密:指的就是加因块、解密使用的同是一串密鑰,所以被稱做對(duì)稱加密籍铁。對(duì)稱加密只有一個(gè)密鑰作為私鑰涡上。
非對(duì)稱加密:指的是加、解密使用不同的密鑰拒名,一把作為公開的公鑰吩愧,另一把作為私鑰。公鑰加密的信息增显,只有私鑰才能解密雁佳。反之,私鑰加密的信息,只有公鑰才能解密糖权。
get 和 post的區(qū)別
- get 會(huì)默認(rèn)把請(qǐng)求的數(shù)據(jù)緩存下來(lái)堵腹,post 不會(huì)
- get 會(huì)對(duì)把請(qǐng)求的參數(shù)放在url中,post放在header 中
- get 會(huì)把headr 和body 內(nèi)容一次性的tcp 星澳,post會(huì)兩次
tcp 和udp
tcp 有序可靠秸滴,udp 會(huì)出現(xiàn)丟包情況
總結(jié)緩存
本地緩存有
- serviceWorder
//注冊(cè)
navigator.serviceWorker.registor("ws.js",{scope:"/"}).then
//ws.js
//install的時(shí)候存儲(chǔ)所需文件
self.addEventLister("install",()=>{
waitUntil(()=>{
cache.open("key").then(()=>{
cache.addAll('')
})
})
})
// actrated 銷毀緩存
self.addEventLister("actrated",()=>{
watiUnitl(()=>{
cache.delect
})
})
// fetch時(shí)給到cche
self.addEventLister("fetch",()=>{
cache.match()
})
- memery cache 如get 的一些img ,cdn
- dick cache 存儲(chǔ)了 url 的ip地址,cookie募判。
- Etag/if-no-match 、Last modify/if-modify-since
跨域
- Access-control-Allow-origin *
- Access-control-Allow-Gredentials = true 攜帶cookies-服務(wù)器
- Access-control-export-headers 該字段可選咒唆。CORS請(qǐng)求時(shí)届垫,XMLHttpRequest對(duì)象的getResponseHeader()方法只能拿到6個(gè)基本字段:Cache-Control、Content-Language全释、Content-Type装处、Expires、Last-Modified浸船、Pragma妄迁。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定李命。上面的例子指定
- withCredentials 客戶端發(fā)送cookie的開關(guān)
- document.domain + iframe跨域 location.hash + iframe
css部分
偽類和偽元素
- 偽類 1狀態(tài): hover active visit focus 2 結(jié)構(gòu)話:nth-child first child 3表單:checked default disable readOnly
- 偽元素 before after selection
rem 和 em
rem 是根據(jù)根元素的計(jì)算登淘,em 是根據(jù)當(dāng)前父元素計(jì)算 下面是利用rem 的移動(dòng)端布局
var dpr = 1
if (isIPhone) {
// iOS下,對(duì)于2和3的屏封字,用2倍的方案黔州,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
}else if(isAndroid && devicePixelRatio > 2){
dpr = 3;
}else {
// 其他設(shè)備下,仍舊使用1倍的方案
dpr = 1;
}
var width = documentElement.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = parseInt(rem) + 'px';
利用babel postcss-pxtorem 的把當(dāng)前文件px 轉(zhuǎn)換成 rem
flex
flex 是 disable position float 的語(yǔ)法糖
flex 1 的含義
flex 1 === flex.grow===1 flex.shrink===1 flex.basis ==auto
- grow 是判斷當(dāng)前子元素的擴(kuò)展屬性阔籽,場(chǎng)景:只有當(dāng)前子元素的寬度中和小于總寬度才會(huì)有效,默認(rèn)值0流妻,即使小于于總寬度,也不放大
- shrink 是判斷當(dāng)前子元素的總寬度是否大于總寬度笆制,如果大于則縮小绅这,默認(rèn)值為1,默認(rèn)縮小父盒子的寬度
- basis在辆,在縮放前证薇,判斷父盒子的總寬度。默認(rèn)auto
BFC(塊格式化上下文)
css 執(zhí)行上下文开缎,規(guī)范內(nèi)部元素的渲染位置棕叫,BFC 之間不相互影響。
例如:display:flex,float,positon..
IFC(內(nèi)聯(lián)上下文)
內(nèi)聯(lián)上下文的高度由 子元素的最高算奕删,
運(yùn)用于
開啟BFC :
- body元素
- 設(shè)置disable inline-block flex
- 設(shè)置 float 除了設(shè)置為none 外
- 設(shè)置 overflow 除了visiable
XSS CFRS
XSS 跨站腳本攻擊俺泣,比如在input 輸入一些帶有script的標(biāo)簽,這些標(biāo)簽通過(guò)document.cookie獲取cookie 信息。
解決辦法:
- 1 通過(guò)設(shè)置 httpOnly=true,無(wú)法從本地獲取cookie 2 通過(guò)校驗(yàn)輸入框中的內(nèi)容來(lái)判斷'<'閉合標(biāo)簽的伏钠,做轉(zhuǎn)換横漏。
CSRF 跨站請(qǐng)求偽造:都過(guò)劫持當(dāng)期登錄的網(wǎng)站cookie <img src="facebook.com" style="visibility:hidden;"> 這個(gè)圖片,如果你點(diǎn)擊熟掂,就會(huì)把你的當(dāng)前cookie 發(fā)給facebook缎浇,然后第三方網(wǎng)站會(huì)利用你的cookie 去請(qǐng)求 當(dāng)前網(wǎng)站的接口偽造信息。
解決辦法
- 1 cookie 有個(gè)叫做SameSite 的屬性赴肚,strict:最為嚴(yán)格素跺,告訴的是 禁止第三方的cookie。lax:稍微寬泛誉券,指的是大多數(shù)情況允許指厌,只有g(shù)et的導(dǎo)航地址時(shí)會(huì)帶上。none:不管
- 2可以利用token踊跟,token是服務(wù)端根據(jù)用戶信息合成的數(shù)據(jù)踩验,傳給客戶端,客戶端在下次請(qǐng)求的時(shí)候商玫,手動(dòng)帶上箕憾。和cookie 不同的是,在csrf 的時(shí)候拳昌,cookie 會(huì)自動(dòng)帶上袭异,而token不會(huì)
websocket
- 長(zhǎng)連接通訊,輪詢?cè)L問(wèn)
- 雙向通信協(xié)議
- 需要先建立連接后在不斷的發(fā)送數(shù)據(jù)
柯里化
function add(){
const _arg = [...arguments]
function addFun(){
_arg.push(...arguments)
return addFun
}
addFun.toString=function(){
return _arg.reduce((a,b)=>a+b)
}
return addFun
}
console.log(add(1)(2)(4,1))
利用高級(jí)函數(shù)做回調(diào)調(diào)用
compose
function compose(f,g){
return (x){
return(f(g(x))
}
}
實(shí)現(xiàn)了數(shù)據(jù)流有右往左的過(guò)程
模塊化
- commonJs 是同步加載模塊炬藤,用于nodeJs 扁远,因?yàn)閚ode 文件加載都在本地進(jìn)行
- AMD 是requireJs的推廣的模塊化思想,異步加載數(shù)據(jù)
- CMD 是sea.js推廣過(guò)程的模塊化思想
- UMD 是AMD he CommonJS 的合并產(chǎn)物
// AMD
define(key,[a.js,b.js],funcion()=>{
console.log(111)
})
require(['foo', 'bar'], function(foo, bar) {
foo.func();
bar.func();
} );
// CMD
define(()=>{
cont a = require("./a")
cont a = require("./a")
})
// comonJs
module.export
requre()
ES6
export
import
AMD 和CMD 的區(qū)別
- amd有全局的api 支持刻像,而cmd 沒(méi)有畅买,只能通過(guò) sea.use
- amd 是提前執(zhí)行,cmd 是延遲執(zhí)行,但到后面amd 也執(zhí)行延遲執(zhí)行
commonJs 和 es6 module
- commonJs 是同步獲取文件细睡,適合服務(wù)端谷羞。
- commonJs 是講字符串,對(duì)象溜徙,函數(shù)復(fù)制了一份給到另外一個(gè)變量湃缎,而es6module是一個(gè)解構(gòu)的過(guò)程
- reruire 可以放在任何位置 而import 只能放在開頭
- require 會(huì)先將導(dǎo)入文件執(zhí)行一次,然后放入緩存蠢壹,后續(xù)在引入的時(shí)候只會(huì)走緩存嗓违,es6 import會(huì)在JavaScript引擎靜態(tài)分析,在編譯時(shí)就引入模塊代碼
for in for of
- for in 適合遍歷對(duì)象图贸,也可以數(shù)組蹂季,但是遍歷的是 key ,而 of 遍歷的是value
- for of 本質(zhì)是 迭代器冕广,map set 實(shí)現(xiàn)了 迭代接口的,每次next 執(zhí)行到下個(gè)值### oss cnd 的區(qū)別
- oss 是儲(chǔ)存功能偿洁,只要存儲(chǔ)靜態(tài)資源
- cnd 是分發(fā)作用撒汉,把oss 的文件分發(fā) 并緩存到cdn 節(jié)點(diǎn)
js 函數(shù)思想
函數(shù)
函數(shù)是一等公民
- 函數(shù)可以存儲(chǔ)在變量
- 函數(shù)可以作為入?yún)?/li>
- 函數(shù)可做返回值
純函數(shù)
每次輸入輸出的值都是一樣的 例子:
let arr = [1,2,3,4,5]
console.log(arr.slice(1,3))
console.log(arr.slice(1,3))
console.log(arr.slice(1,3))
// 每次輸出是一樣的結(jié)果
console.log(arr.slice(1,3))
console.log(arr.slice(1,3))
console.log(arr.slice(1,3))
// 每次輸出是的結(jié)果不一樣
純函數(shù)的好處
1 緩存值,應(yīng)為相同的輸出涕滋〔欠可以做緩存
function sum (a,b){
console.log(1111,a,b)
return a+b
}
function memoize(fn){
let cache = {}
return function(){
let key = JSON.stringify(arguments)
cache[key] = cache[key]? cache[key]:fn.apply(fn,[...arguments])
return cache[key]
}
}
let _m = memoize(sum)
console.log(_m(1,3))
console.log(_m(1,3))
console.log(_m(1,3))
console.log(_m(1,4))
2 可以做單元測(cè)試,由于純函數(shù)的輸出是可靠的 3 在多線程時(shí)宾肺,操作公共數(shù)據(jù)溯饵,會(huì)導(dǎo)致公共數(shù)據(jù)不可靠,但是純函數(shù)不會(huì)锨用,相同的輸出會(huì)有相同的輸出
副作用
// 副作用使我的的函數(shù)變得不純潔
let num = 8
function getVal(val){
return val>num
}
// 相同的輸入會(huì)得到相同的輸出瓣喊,但是由于nums的存在,導(dǎo)致了變數(shù)黔酥,以至于函數(shù)的不純,所以稱為副作用
副作用的來(lái)源
- 配置文件
- 數(shù)據(jù)庫(kù)
- 用戶輸入
- 洪橘。跪者。。熄求。
柯里化
function sum (a,b,c){
return a+b+c
}
function curry(fn){
return function num1(...arg){
if(fn.length>arg.length){
return function(){
let arr = [...arguments].concat(arg)
//1 注意return 值 2 arr是數(shù)組傳進(jìn) num1時(shí)候要換成單個(gè)形參
return num1(...arr)
}
}else{
return fn(...arg)
}
}
}
let sumFn = curry(sum)
console.log(sumFn(1)(1,3))
console.log(sumFn(1,2)(3))
console.log(sumFn(1,2,3))
解決重復(fù)問(wèn)題的渣玲,生成過(guò)程 假如 要計(jì)算 1+3+x的問(wèn)題,那就let all=sumFn(1,2)弟晚,all(1),all(2),不用在關(guān)注1忘衍,3的問(wèn)題 柯里化意義:
- 可以緩存函數(shù)的參數(shù)
- 可以讓我傳遞較少的參數(shù)得到返回記憶的,新函數(shù)
- 讓函數(shù)變得更加靈活卿城,顆粒度更小
- 把多元函數(shù)換成一元函數(shù)枚钓,使其功能更強(qiáng)大
compose 實(shí)現(xiàn)
// 洋蔥代碼
fes1(fes2(fes3(2)))
去除洋蔥代碼,利用純函數(shù)瑟押,讓數(shù)據(jù)從右往左傳遞
function reduce(arr,fn,acc){
for(var i = 0;i<arr.length;i++){
let val = arr[i]
acc=fn(acc,val)
}
return acc
}
function compose(...arg){
return function(val){
return reduce(arg.reverse(),(acc,fn)=>fn(acc),val)
}
}
function getRevece(arr){
return arr.reverse()
}
function getFirst(arr){
return arr[0]
}
function toUpert(val){
return val.toLocaleUpperCase()
}
let getHF = compose(toUpert,getFirst,getRevece)
console.log(getHF(["l","lk","love"]))
// let getHF = compose(toUpert,getFirst,getRevece)
// let getHF = //compose(toUpert,compose(getFirst,getRevece))
//let getHF = compose(compose(toUpert,getFirst),getRevece)
// 任意組合順序得到的結(jié)果都是相同的
compose 函數(shù)調(diào)試
function log(val){
console.log(val)
return val
}
let getHF = compose(toUpert,getFirst,log,getRevece)
point free
- 不需要關(guān)系數(shù)據(jù)
- 只需要組合函數(shù)
- 定義使用的基本函數(shù)
compose(toUpert,getFirst,log,getRevece)
函子
// maybe 函子 如果當(dāng)前函子的值為nuLl,導(dǎo)致下一步報(bào)錯(cuò)時(shí)候
class Maybe {
constructor(val){
this._val = val
}
static of(val){
return new Maybe(val)
}
map(fn){
console.log(this.isNoting(),"dd",this._val)
return this.isNoting()?new Maybe(null):new Maybe(fn(this._val))
}
isNoting(){
return this._val===null||this._val===undefined
}
}
console.log(Maybe.of(1).map(x=>undefined).map(x=>undefined).map(x=>undefined))
// maybe 函子雖然知道是解決了 null的不報(bào)錯(cuò)問(wèn)題搀捷,但導(dǎo)致后續(xù)流程走不通 Either 函子
class Left {
static of(val){
return new Left(val)
}
constructor (val){
this._val = val
}
map(){
return this
}
}
class Right {
static of(val){
return new Left(val)
}
constructor (val){
this._val = val
}
map(fn){
return new Right(fn(this._val))
}
}
try {
Right.of("1111")
} catch (error) {
left.of("1111")
}
// io 函子,讓副作用 到執(zhí)行的時(shí)候去操作變量
class IO {
static of(val){
return new IO(function(){
return val
})
}
constructor(fn){
this._val = fn
}
map(fn){
return new IO(compose(fn,this._val))
}
}
let res = IO.of(process).map(p=>p.execPath)
console.log(res._val())
pointed 函子
上面的函子使用了 of 方法多望,是的變量_val 包含于在一個(gè)盒子里嫩舟,也是就是new后的一個(gè)變量,point 函子是一個(gè)概念
monad
// io 函子怀偷,讓副作用 到執(zhí)行的時(shí)候去操作變量
class IO {
static of(val){
return new IO(function(){
return val
})
}
constructor(fn){
this._val = fn
}
map(fn){
return new IO(compose(fn,this._val))
}
}
let res = IO.of(process).map(p=>p.execPath)
// console.log(res._val())
//
// console.log(fs,"fs")
function readFiles(str){
return new IO(()=>{
return fs.readFileSync(str,"utf-8")
})
}
function log(x){
return new IO(()=>{
console.log(x,"xx")
return x
})
}
let r = compose(log,readFiles)("index.js")
// console.log(r._val()._val())
// 鏈?zhǔn)秸{(diào)用太過(guò)繁瑣
class Monad {
constructor(val){
this._val = val
}
map(fn){
return new Monad(compose(fn,this._val))
}
join(){
return this._val()
}
firstMap(fn){
return this.map(fn).join()
}
}
function read2(sr){
return new Monad(()=>{
return fs.readFileSync(sr,"utf-8")
})
}
function log2(x){
return new Monad(()=>{
console.log(x)
return x
})
}
// 等于永jion 方法執(zhí)行了兩次替代了_val
console.log(read2("index.js").firstMap(log2).join())
es6
字符串標(biāo)簽
const name =1
sdf`sd${name}`
proxy
const {log:log1} = console
const Objtext = {
name:"lk",
age:18,
isMerry:false
}
let ObjectProxy = new Proxy(Objtext,{
get(target,name,receiver){
// console.log(...arg,"arge111")
console.log(target,"target111")
console.log(name,"name111")
console.log(receiver,"who111")
},
set(target,name,val){
log1(111111)
target[name] =val
log1(target,name,val)
}
})
console.log(ObjectProxy.name="lp")
log1(Objtext,"origin val")
proxy 和 object.defineProtity的區(qū)別
- proxy 還可以監(jiān)聽 數(shù)組家厌,
- proxy 不是侵入式的,不需要改寫原型方法
- proxy 更加強(qiáng)大可以監(jiān)聽數(shù)據(jù)刪除椎工,遍歷等
es2017
Object.entries
let obj2 = {name:"lk",age:18}
console.log(Object.entries(obj2))
console.log(new Map(Object.entries(obj2)))
// log:[ [ 'name', 'lk' ], [ 'age', 18 ] ]
// Map { 'name' => 'lk', 'age' => 18 }
可以將對(duì)象的鍵值對(duì)完美的變成二維數(shù)組形式饭于,而這種形式剛好是map想要的形式
Object.getOwnPropertyDescriptors
let p1 = {
firstName:"liu",
lastname:"kun",
get fulllName(){
return this.firstName+"**"+this.lastname
}
}
// let p2 = Object.assign({},p1)
// p2.firstName = "lp"
// console.log(p2,"p2")
/*
Object.defineProperty(p1, 'property1', {
value: 42,
writable: false
});
Object.defineProperties(obj, {
'property1': { value: true, writable: true },
'property2': { value: 'Hello', writable: false }
});
// 只是改不對(duì)象本身蜀踏,和一些getter 和setter 方法,不是修改property的屬性镰绎,不牽扯原型
*/
let getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors(p1)
let p2 = Object.defineProperties(p1,getOwnPropertyDescriptors)
console.log(p2,"p2")
p2.firstName = "li"
console.log(p2.fulllName)
瀏覽器解析
html-->html parse -->domtree layout
| |
(html 和 css 的一個(gè)attch過(guò)程)attach-->padding tree--paining
|
css-->css parse-->css rule
css tree
css rules
selectors declaration
div margin 3px
計(jì)算的權(quán)重規(guī)則
* {} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li {} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li {} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li {} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red {} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level {} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y {} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
layout
負(fù)責(zé)計(jì)算頁(yè)面的高度脓斩,位置信息的 --改變了css的樣式,如文字大小等高度畴栖,位置随静,都需要重新布局(會(huì)影響其他元素,損耗巨大)
painting
繪制吗讶,從新改了頁(yè)面的顏色燎猛,背景圖(損耗小)
總結(jié)緩存
本地緩存有
- serviceWorder
//注冊(cè)
navigator.serviceWorker.registor("ws.js",{scope:"/"}).then
//ws.js
//install的時(shí)候存儲(chǔ)所需文件
self.addEventLister("install",()=>{
waitUntil(()=>{
cache.open("key").then(()=>{
cache.addAll('')
})
})
})
// actrated 銷毀緩存
self.addEventLister("actrated",()=>{
watiUnitl(()=>{
cache.delect
})
})
// fetch時(shí)給到cche
self.addEventLister("fetch",()=>{
cache.match()
})
- memery cache 如get 的一些img ,cdn
- dick cache 存儲(chǔ)了 url 的ip地址照皆,cookie重绷。
- Etag/if-no-match 、Last modify/if-modify-since
websocket
- 長(zhǎng)連接通訊膜毁,輪詢?cè)L問(wèn)
- 雙向通信協(xié)議
- 需要先建立連接后在不斷的發(fā)送數(shù)據(jù)
實(shí)現(xiàn)了數(shù)據(jù)流有右往左的過(guò)程
性能優(yōu)化
- 雪碧圖
- 代碼壓縮昭卓,從個(gè)人代碼壓縮:1使用table 代替div 、2精簡(jiǎn)代碼瘟滨、3 網(wǎng)頁(yè)Gzip 壓縮
// gizip 壓縮候醒,看是否瀏覽器支持,如果responseHeader 中有 content-Encoding="gizp"時(shí)
// node 使用expression 中間件
express.use(compression())
// webpack compressionWebpackPlugin
const CompressionWebpackPlugin = require('compression-webpack-plugin');
new CompressionWebbapckPlugin({
asset:"[path].gz[qurey]"
algorithm:"gzip",
text:/\.(js|css)/,
mixRatio:8 //壓縮比最小為8時(shí)候壓縮
threShold:1024 // 大于1024時(shí)壓縮
})
- 代碼封裝庫(kù)杂瘸,提高js 效率倒淫。
- 使用cdn 加速
// 動(dòng)靜分離
動(dòng)態(tài)資源:多次訪問(wèn)會(huì)發(fā)生變化的
靜態(tài)資源:多次訪問(wèn)不會(huì)發(fā)生變化的,如 html,js css
把靜態(tài)資源放在 nginx 代理服務(wù)器上
動(dòng)態(tài)資源放在 tomcat 上
// cdn 回溯
是 cdn 跳過(guò)了 緩存(本地败玉,nginx) 訪問(wèn)的原始服務(wù)器上了
- 使用json作為前后端 交換
- 控制cookie大小敌土,
ssr
服務(wù)端 使用react 的
- renderToString 將react組件轉(zhuǎn)換為字符串隨瀏覽器返回
- 如果遇到 o'nclick 事件,客戶端需要獨(dú)立打包重新注釋服務(wù)端
- 路由控制 服務(wù)端使用react-router-dom/StaticRouter 來(lái)實(shí)現(xiàn)路由跳轉(zhuǎn)
app.get('*',(req,res) => {
const content=renderToString(
<StaticRouter context={{}} location={req.path}>
{routes}
</StaticRouter>
);
res.send(`
<!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></title>
</head>
<body>
<div id="root">${content}</div>
<script src="/index.js"></script>
</body>
</html>
`);
});
app.listen(9090);
deepclone
function deepClone(obj){
const type = [Map,Date,Set,RegExp,WeakMap,WeakSet]
if(type.includes(obj.constructor))return new obj.constructor(obj)
const clone = Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj))
hash.set(obj,clone)
for(let k of Reflect.ownKeys(obj)){
clone[k] = typeof obj[k]==="object"&&typeof obj[k]!=null?deepClone(obj[k]):obj[k]
}
return clone
}
// 示例
// 第二個(gè)參數(shù)為null時(shí)运翼,拋出TypeError
// const throwErr = Object.myCreate({a: 'aa'}, null) // Uncaught TypeError
// 構(gòu)建一個(gè)以
const obj1 = Object.myCreate({a: 'aa'})
console.log(obj1) // {}, obj1的構(gòu)造函數(shù)的原型對(duì)象是{a: 'aa'}
const obj2 = Object.myCreate({a: 'aa'}, {
b: {
value: 'bb',
enumerable: true
}
})
console.log(obj2) // {b: 'bb'}, obj2的構(gòu)造函數(shù)的原型對(duì)象是{a: 'aa'}
原型
1 構(gòu)造函數(shù)
構(gòu)造函數(shù)模式就是創(chuàng)建一個(gè)自定義的類返干,并且可以創(chuàng)造類的實(shí)例
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
this.sayName = function () {
alert(this.name);
}
}
var per = new Person("孫悟空", 18, "男");
function Dog(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
var dog = new Dog("旺財(cái)", 4, "雄")
console.log(per);//當(dāng)我們直接在頁(yè)面中打印一個(gè)對(duì)象時(shí),事件上是輸出的對(duì)象的toString()方法的返回值
console.log(dog);
上面的 new 方法每調(diào)用一次都生產(chǎn)新的name 屬性和 sayName 方法血淌,這樣的工作是重復(fù)的犬金,如何解決了?---prototype
2原型鏈
對(duì)象之間純?cè)诶^承關(guān)系六剥,可用通過(guò)prototype 訪問(wèn)父類的對(duì)象晚顷。直到Object 為止
Person -->--prototype---->Person.protype---->
| ^ | ^
| |--<--constructor--<---------| | |
v ^ |
new | |
| ^ |
person------>----__proto__->------—| __proto__
|
|
v
|
Object()----->proto----------->Object.prototype
|
__proto__
null
other: __proto__ === Object.getprototypeof b=Object.create(a),b.__proto__=a
事件冒泡、事件捕獲和事件委托
// 事件監(jiān)聽疗疟,第三個(gè)參數(shù)是布爾值该默,默認(rèn)false,false是事件冒泡策彤,true是事件捕獲
document.getElementById('box3').addEventListener('click', sayBox3, false);
document.getElementById('box2').addEventListener('click', sayBox2, false);
document.getElementById('box1').addEventListener('click', sayBox1, false);
1 先捕獲后冒泡 直到document位置
document-box3->box2->box1->box2->box3-doucment
2阻止冒泡事件
if(window.event.stopPropagation){widnow.event.stopPropagation()}else{window.event.cancelBubble=true}
3關(guān)于IE
事件注冊(cè) window.attachEvent(name,fun)
事件刪除 window.detachEvent(name,fun)
chorme:window.addEventListener window.removeEventListener
獲取事件源id chorme:e.target.id ie:e.srcElement.id
關(guān)于js的瀏覽器兼容問(wèn)題栓袖,一般用能力檢測(cè)來(lái)解決匣摘,if(){}else{}
js事件循環(huán)eventLoop
js 是單線程,但js純?cè)谕饺蝿?wù)和異步任務(wù)裹刮,js處理同步和異步任務(wù)的機(jī)制叫做eventLoop
- 同步任務(wù) :所有立即執(zhí)行的任務(wù)都是同步任務(wù)如申明變量音榜,函數(shù)執(zhí)行(無(wú)依賴)
- 異步任務(wù) :時(shí)間類的setTimeOut 、setInterval捧弃,ajax請(qǐng)求赠叼、promise等
異步任務(wù)
- 宏觀任務(wù)(Macro Task):時(shí)間類,ajax违霞、dom
- 微觀任務(wù)(Micro Task)promise
執(zhí)行順序
- 1 先執(zhí)按順序執(zhí)行同步隊(duì)列嘴办,遇到異步任務(wù)統(tǒng)一放到異步等待隊(duì)列中,至到從上到下代碼執(zhí)行完成买鸽。
- 2 執(zhí)行異步隊(duì)列涧郊,遇到宏觀任務(wù)先按順序放入等待隊(duì)列中,直到所有宏觀任務(wù)放入隊(duì)列眼五,在執(zhí)行微觀任務(wù)妆艘,如遇在微觀任務(wù)中遇到到宏觀任務(wù),排到上面宏觀隊(duì)列的最后面
- 執(zhí)行所有的宏觀任務(wù)
console.log('開始111');
setTimeout(function () {
console.log('timeout111');
});
new Promise(resolve => {
console.log('promise111');
resolve();
setTimeout(() => console.log('timeout222'));
}).then(function () {
console.log('promise222');setTimeout(() => console.log('timeout333'));
})
console.log('開始222');
結(jié)果
開始111
promise111
開始222
promise222
undefined
timeout111
timeout222
console.log('開始111');
setTimeout(function () {
console.log('timeout111');
});
new Promise(resolve => {
console.log('promise111');
resolve();
setTimeout(() => console.log('timeout222'));
}).then(function () {
console.log('promise222');setTimeout((function(){console.log("time33333")})());
})
console.log('開始222');
結(jié)果
開始111
promise111
開始222
promise222
time33333
undefined
timeout111
()()會(huì)立即執(zhí)行跳出setTimteOut
js中call看幼、apply批旺、bind方法的作用和區(qū)別
function a (){console.log(this)}
a() // window
a.cell({},111) // {}
a.apply({},[1]) //{}
a.apply({},[1]) // function
a.apply({},[1])() // {}
閉包
var fun1 = addCount();
fun1(); //1
fun1(); //2
var fun2 = addCount();
fun2(); //1
fun2(); //2
特殊的for 循環(huán)
for (var i = 0; i < 4; i++) {
setTimeout(function() {
console.log(i);
}, 300);
}
結(jié)果: 4個(gè)4 ,等同步任務(wù)完成后拿到的i 為4
- 解決方法1
//方法一:
for (var i = 0; i < 4; i++) {
setTimeout(
(function(i) {
return function() {
console.log(i);
};
})(i),
300
);
}
// 或者
for (var i = 0; i < 4; i++) {
setTimeout(
(function() {
var temp = i;
return function() {
console.log(temp);
};
})(),
300
);
}
//這個(gè)是通過(guò)自執(zhí)行函數(shù)返回一個(gè)函數(shù),然后在調(diào)用返回的函數(shù)去獲取自執(zhí)行函數(shù)內(nèi)部的變量,此為閉包
//方法發(fā)二:
for (var i = 0; i < 4; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 300);
})(i);
}
- 解決方法2
for(let i= 0;i<4;i++){
setTimeout(()=>{console.log(i)},300)
}
//錯(cuò)位方式
let i= 0;
for(;i<4;i++){
setTimeout(()=>{console.log(i)},300)
}
這種原因是由于 let 和 var,let 有作用域桌吃,每次在for 循環(huán)的時(shí)候都會(huì)i=3生成新的作用域,所以導(dǎo)致了苞轿,在var 的時(shí)候最后一次覆蓋了前面茅诱,而let作用域的存在每次都是在自己的作用域下生成了新的 i,i =0 ...i=3
Promise
例子
new Promise((resolve)=>{ setTimeout(()=>{resolve()},300)})
.then(res=>{
return new Promise((resolve)=>setTimeout(()=>{resolve(222)},300))})
.then((res)=>{
console.log(res)}
).then(res=>{
console.log("上個(gè)為null")
}).then((res)=>{
throw new Eror()}).
then(null,(res)=>{
console.log("error1")
})
.then((res)=>{
console.log(res+"eror hou") ;
return 111
}).then(res=>console.log(res+"last"))
返回
222
上個(gè)為null
error1
undefinederor hou
1 111last
Promise.all 和Promise.race
//切菜
function cutUp(){
console.log('開始切菜。');
var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){
console.log('切菜完畢搬卒!');
resolve('切好的菜');
}, 1000);
});
return p;
}
//燒水
function boil(){
console.log('開始燒水瑟俭。');
var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){
console.log('燒水完畢!');
resolve('燒好的水');
}, 1000);
});
return p;
}
Promise.all([cutUp(), boil()])
.then((result) => {
console.log('準(zhǔn)備工作完畢');
console.log(result);
})
返回結(jié)果:
開始切菜契邀。
開始燒水摆寄。
切菜完畢!
燒水完畢坯门!
準(zhǔn)備工作完畢
['切好的菜','燒好的水']
let p1 = new Promise(resolve => {
setTimeout(() => {
resolve('I\`m p1 ')
}, 1000)
});
let p2 = new Promise(resolve => {
setTimeout(() => {
resolve('I\`m p2 ')
}, 2000)
});
Promise.race([p1, p2])
.then(value => {
console.log(value)
})
返回結(jié)果:
I`m p1
- then 返回的如果是promise 后面的.then 等待狀態(tài)完成后在執(zhí)行微饥,如果返回普通值這不用等待,可直接在then中獲取古戴。不返回也可調(diào)用后面.then()
- Promise.all() 和Promise.race()都是批量處理異步欠橘。但是all是有完成才能成功,有一個(gè)失敗則返回第一個(gè)失敗现恼。Promise.race()成功只返回第一個(gè)成功肃续。
迭代器
let a = [1,2,3,4]
for(var i =0;i<a.length;i++){
console.log(a[i])
}
迭代器要解決的問(wèn)題就是黍檩,像上面在循環(huán)的時(shí)候,不考慮標(biāo)記位i的數(shù)據(jù)始锚,只關(guān)注遍歷數(shù)據(jù)的value值
手寫promise
class MyPromise {
static PENDING = "pending"
static FULFILLED = "fulfilled"
static REJECTED = "rejected"
constructor(executer){
this.state = MyPromise.PENDING
this.value = null
this.callbacks = []
// 使用try catch 保證函數(shù)體內(nèi)得報(bào)錯(cuò)
try {
// bind this 保證this 永遠(yuǎn)指向MyPromise
executer(this.resolve.bind(this),this.reject.bind(this))
} catch (err) {
this.reject(err)
}
}
resolve(value){
// 只有在 peding 狀態(tài)時(shí)才讓改變 state刽酱。防止?fàn)顟B(tài)倒流
if(this.state===MyPromise.PENDING){
this.state = MyPromise.FULFILLED;
this.value = value
setTimeout(()=>{
this.callbacks.forEach(({onFulfilled})=>{
try {
onFulfilled(this.value)
} catch (error) {
this.reject(error)
}
})
})
}
}
reject(reason){
if(this.state===MyPromise.PENDING){
this.state = MyPromise.REJECTED
this.value = reason
setTimeout(()=>{
this.callbacks.forEach(({onReject})=>{
onReject(this.value)
})
})
}
}
/*
let p = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve("解決")
},1000)
// reject("失敗")
})
像這種異步調(diào)用,因?yàn)閞esolve 被滯后調(diào)用瞧捌,但是then 方法是同步執(zhí)行得棵里,所有雖然 reslove被1秒后調(diào)用成功,
但是then 已經(jīng)提前調(diào)用察郁,并且沒(méi)有獲取到狀態(tài)和值
*/
then(onFulfilled,onReject){
// onFulfilled,onReject
// 他倆存在是為了和value 產(chǎn)生一個(gè) result ,這個(gè)result 為了后續(xù)的 新 的
// pmromise resolve,和reject 傳值并改變狀態(tài)
// onFulfilled 執(zhí)行是為了完成當(dāng)前的第一個(gè)then 的函數(shù)體的值
if(typeof onFulfilled!="function"){
// then() 解決這種空得then時(shí)候得穿透問(wèn)題
onFulfilled = ()=> {this.value;this.state=MyPromise.FULFILLED}
}
if(typeof onReject!="function"){
onReject = ()=>{this.value;this.state=MyPromise.REJECTED}
}
// 返回一個(gè)新的 promise 重復(fù)上面操作
let promise = new MyPromise ((resolve,reject)=>{
if(this.state===MyPromise.PENDING){
this.callbacks.push({
onFulfilled:(valve)=>{
this.parse(promise,onFulfilled(valve),resolve,reject)
},
onReject:(valve)=>{
this.parse(promise,onFulfilled(valve),resolve,reject)
}
})
}
if(this.state===MyPromise.FULFILLED){
// 保證到異步隊(duì)列
setTimeout(()=>{
this.parse(promise,onFulfilled(this.value),resolve,reject)
})
}
if(this.state===MyPromise.REJECTED){
setTimeout(()=>{
this.parse(promise,onReject(this.value),resolve,reject,true)
})
}
})
return promise
}
parse(promise,result,reject,resolve,mark){
// 不允許返回自己
if(promise===result){
throw new TypeError("Chaning cycle detected for promise")
}
try {
if(result instanceof MyPromise ){
result.then(resolve,reject)
}else{
if(mark){
reject(result)
}else resolve(result)
}
} catch (error) {
reject(error)
}
}
static resolve (value){
return new MyPromise((resolve,reject)=>{
if(value instanceof MyPromise){
value.then(resolve,reject)
}else{
resolve(value)
}
})
}
static reject (value){
return new MyPromise((resolve,reject)=>{
if(value instanceof MyPromise){
value.then(resolve,reject)
}else{
reject(value)
}
})
}
static all(promises){
const value = []
return new MyPromise((resolve,reject)=>{
promises.forEach((promise)=>{
promise.then(res=>{
value.push(res)
if(value.length===promises.length){
resolve(value)
}
},(reson)=>{
reject(reson)
})
})
})
}
static race(promises){
return new MyPromise((resove,reject)=>{
promises.forEach(promise=>{
promise.then(value=>{
resove(value,1111)
},reason=>{
reject(reason)
})
})
})
}
}
自定義迭代器
function iterator(arr){
var index=0 ;
return {
next(){
if(index>=arr.length){
return{done:true,value:undefined }
};
return {done:false,value:arr[index++]}
}
}
}
generator 生成器
function *genarator (arr){
for(var i=0 ;i<arr.length;i++){
yield arr[i]
}
}
const gen = genarator([1,2,3,4])
gen.next()
- yield 可中斷函數(shù)執(zhí)行衍慎,到下個(gè).next()時(shí)候才能繼續(xù)執(zhí)行下面函數(shù)體
- yield 不可在*函數(shù)外調(diào)用
可迭代對(duì)象
- 數(shù)組,set,map
- Symbol.iterator 函數(shù)可以返回一個(gè)迭代器
a = [1,2,4][Symbol.iterator]
a.next()
qu = new Set([1,2,3])
mm = qu.[Symbol.iterator]()
mm.next
uu = new Map()
uu.set(1,1)
uu.set(2,2)
yy = uu[Symbol.iterator]()
yy.next()
{
done: false
value: [1, 1]
}
for of 可以替代迭代器
a = [1,2,3,4]
for(var i of a){console.log(i)}
1 2 3 4
for of 為何可以遍歷數(shù)據(jù)皮钠,遍歷數(shù)據(jù)具備的屬性是什么稳捆?
let col = {
list:[],
*[Symbol.iterator](){
for(var i =0;i<this.list.length;i++){ yield this.list[i]
}
}
}
col.list.push(1)
col.list.push(2)
col.list.push(3)
col.list.push(4)
for(let item of col){console.log(item)}
是因?yàn)閷?duì)象內(nèi)部有一個(gè)Symbol.iterator屬性,所生成的迭代器麦轰。
內(nèi)建迭代器
- entries() 返回[key,value ] set數(shù)據(jù)key和value值相同
- values() 只返回 value的值
- keys() 只返回 key的值
var arr1 = [1,2,3,4]
var set1 = new Set([1,2,3,4,5])
var map1 = new Map()
map1.set(11,11)
map1.set(22,22)
for (let i of arr1.entries()){console.log(i)}
// [0,1] [1,2] [2,3] [3,4] [4,5]
arr1.entries().next()
// {value:[0,1],done:fase}
for (let m of set1.entries()){console.log(m)}
// [1,1] [2,2] [3,3] [4,4] [5,5]
set1.entries().next()
// {value:[1,1],done:false}
for(let q of map1.entries()){console.log(q)}
// [11,11] [22,22]
map1.entries().next()
// {value:[11,11],done:false}
// value() 和keys() 同理乔夯,都是可以返回可迭代的數(shù)據(jù)
可以看出 entries 和values keys 都是可以使用.next與Symbol.iterator有相同功能
高級(jí)迭代器
function *createIterator() {
let first = yield 1;
let second = yield first + 2; // 4 + 2
yield first + 3; // 5 + 3
}
next() next(2) next()
// next 結(jié)果{value: 1, done: false} {value: 4, done: false} {value: 5, done: false}
第一次什么都沒(méi)傳值,則為1 款侵。第二次傳了2.yield+2 為4末荐,此時(shí)first 被賦值為2。所以在第三次的時(shí)候會(huì)返回5新锈,如果第二次不穿2的話甲脏,無(wú)法獲取到后面的值。所以在函數(shù)體內(nèi)無(wú)法獲取前面的first 值妹笆,只能通過(guò)next 后的返回值做為下個(gè)next 的入?yún)@取值
迭代器拋錯(cuò)
function *createIterator() {
let first = yield 1;
let second;
try {
second = yield first + 2; // yield 4 + 2 块请,然后拋出錯(cuò)誤
} catch (ex) {
second = 6; // 當(dāng)出錯(cuò)時(shí),給變量另外賦值
}
yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // "{ value: 9, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
注意這個(gè)錯(cuò)誤處理得放上一次
return 可阻斷generator
function *cf(){ yield 1 ;return 2 ; yield 2}
mm = cf()
mm.next()
mm.next()
mm.next()
// {value: 1, done: false} {value: 2, done: true} {value: undefined, done: true}
異步函數(shù)回調(diào)拳缠,并獲取其值
const async11 = (data)=>{
return (cb)=>{
setTimeout(()=>{
cb&&cb(11111)
},2000)
}
}
const async22 = (data)=>{
return(cb)=>{
setTimeout(()=>{
// cb&&cb("async22")
// console.log("chuanzhi")
// 下一次想做什么 墩新?1 ff.next(data) ,按照這個(gè)步驟在走一次
console.log(data)
cb&&cb(data +"--2222")
},2000)
}
}
const async33=(data)=>{
return (cb)=>{
cb&&cb(data)
console.log(data)
setTimeout(()=>{
cb&&cb()
},2000)
}
}
function *generator(){
let data1 = yield async11()
let data2 = yield async22(data1)
yield async33(data2)
}
let i = 0
function run (generantor){
const gen = generantor()
let result = gen.next()
// const { value,done } = result
console.log(result,"result")
function step() {
i++
if(!result.done){
result.value((data)=>{ // 需要一個(gè)中間函數(shù)的原因是,除了要執(zhí)行 gen.next(data)窟坐,執(zhí)行回調(diào)完成后要在 step 后面繼續(xù)調(diào)用函數(shù)
result = gen.next(data)
if(i>10){
console.log("死循環(huán)了")
}else{
step()
}
})
}
}
step()
}
run(generator);
// 11111 11111--2222
async await
var snyca = () => {
return new Promise((res) => {
setTimeout(() => {
res(11111)
}, 2000)
})
};
var snyca2 = (data) => {
setTimeout(() => {
console.log("console.log(222)");
console.log(data, "data");
return data + "2222"
}, 3000)
};
async function genr() {
let data1 = await snyca();
let data2 = await snyca2(data1)
};
genr()
- 如果返回的是promise海渊,會(huì)自動(dòng)執(zhí)行 resolve函數(shù),并返回resolve入?yún)ⅰ?/li>
- 如果沒(méi)有await的話哲鸳,在synca2函數(shù)中得到的是一個(gè)promise對(duì)象
- 沒(méi)有await的話臣疑,右邊的函數(shù)體不會(huì)做等待,執(zhí)行下面函數(shù)
繼承 很多繼承
- 原型鏈繼承
function Person (){
this.name = "kk"
};// 父類
function Women (){
// 子類
};
Women.prototype = new Person();
Women.prototype.qq = 111;
Women.prototype.constructor = Women ; // 上面prototype 被污染了徙菠,賦值成整個(gè)Person的類朝捆,導(dǎo)致了Women.prototype.constructor 的對(duì)象指針指錯(cuò)
let son = new Women();
Person.prototype.mm = 'fff'
son.mm ==="fff"
優(yōu)點(diǎn),簡(jiǎn)單懒豹。父級(jí)添加元素芙盘,子級(jí)同步
缺點(diǎn):1 調(diào)用父類函數(shù)的時(shí)候不能傳值驯用。2不能批量給子類函數(shù)繼承 3 new Person()時(shí)會(huì)導(dǎo)致產(chǎn)生實(shí)例
- 構(gòu)造函數(shù)繼承
function Person(){this.name="kk"}
Person.prototype.say = ()=>{console.log("say")}
function Women(){Person.call(this)}
var son = new Women()
son.name // kk
son.say //underfined
優(yōu)點(diǎn):能夠?qū)崿F(xiàn)多個(gè)類繼承,缺點(diǎn):由于沒(méi)有new 只是執(zhí)行了類函數(shù)儒老,所以不能繼承prototype的屬性
- 實(shí)例繼承
function Person (){this.name="kk"}
Person.prototype.say = ()=>{console.log("say")}
function Women (){var instants = new Person() ; return instants}
var son = Women()
son.name // kk
son.say() // say
function Person (){this.name="kk"}
Person.prototype.say = ()=>{console.log("say")}
function Person2 (){this.age = 13}
function Women(){var instans1 = new Person() ;var instans2 = new Person2();return{...instans1,...instans2}}
var son = new Women()
son.age // 13
son.say //underfined
結(jié)構(gòu)會(huì)導(dǎo)致 prototype 丟失
實(shí)例繼承優(yōu)點(diǎn)蝴乔,可以繼承父類原型,簡(jiǎn)單驮樊。缺點(diǎn):無(wú)法實(shí)現(xiàn)多類繼承
- 組合模式
function Person (){
this.name = 'kk'
} ;
Person.prototype.say = ()=>{
console.log("say")
};
function Person2 (){
this.age = 13
};
function Women(){
Person.call(this);
Person2.call(this)
};
Women.prototype = {...new Person,...new Person2};
Women.prototype.constructor = Women;
var son = new Women()
son.name// kk
son.say // underfined
由于...多個(gè)原型prototyp導(dǎo)致原型鏈丟失
function Person() {
this.name = 'kk'
};
Person.prototype.say = () => {
console.log("say")
};
function Person2() {
this.age = 13
};
function Women() {
Person.call(this);
Person2.call(this)
};
Women.prototype = new Person;
Women.prototype.constructor = Women;
var son = new Women()
console.log(son.name) // kk
console.log(son.say()) // say
優(yōu)點(diǎn):組件模式可以實(shí)現(xiàn)多個(gè)繼承薇正,(原型鏈自能繼承一個(gè),所有這種模式囚衔,必須把prototype屬性放在一個(gè)類的原型鏈上)缺點(diǎn):會(huì)有兩次原型調(diào)用挖腰,兩次Person() 執(zhí)行,兩次實(shí)例
- 寄生組合模式
function Person() {
this.name = 'kk'
};
Person.prototype.say = () => {
console.log("say")
};
function Women() {
Person.call(this)
};
function Super(){}
Super.prototype = Person.prototype
Women.prototype = new Super();
Women.prototype.constructor = Women;
var son = new Women()
console.log(son.name)
console.log(son.say())
Super.prototype = Person.prototype 练湿,使用了一個(gè)空的類作為中轉(zhuǎn)猴仑,減少實(shí)例的次數(shù)
- es6 estends繼承
constructor
一個(gè)構(gòu)造函數(shù)必須有一個(gè) constrcutor 方法,在使用new時(shí)候會(huì)自動(dòng)調(diào)用肥哎,如果沒(méi)有會(huì)默認(rèn)添加一個(gè)新的空的constructor
class me{
name =111
}
class ff extends me{
// name = "fff"
constructor(){
// super()
// this.name="fff"
}
}
let qq =new ff()
console.log(qq.name)
然后就報(bào)錯(cuò)了:Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived
說(shuō)明寫了 constructor 必須要些super()辽俗,super 指的是父級(jí)的constrctor 和父級(jí)的類對(duì)象,所以想要在子類中使用this 必須先super篡诽,在繼承過(guò)程中es6的模式和es5完全不同崖飘,es6的子類,是沒(méi)有this的杈女,是通過(guò)super 獲取父級(jí)的this 然后在改造而來(lái)的
proto
class A {
}
class B extends A{}
console.log(B.__proto__==A)
console.log(B.prototype.__proto__==A.prototype)
// true true
因?yàn)椋?/p>
Object.setPrototypeOf(B,A)
Object.setPrototypeOf(B.prototype,A.prototype)
/*
原理:setPrototypeOf(obj1,obj2){
obj1.__proto__==obj2
return obj1
}
*/
特俗的3中繼承方式
class A extends Object{}
console.log(A.__proto__===Object)
console.log(A.prototype.__proto__===Object.prototype)
class B {}
console.log(B.__proto__==Function.prototype)
console.log(B.prototype.__proto__==Object.prototype)
class C extends null{}
console.log(C.__proto__===Function.prototype)
console.log(C.prototype.__proto__===undefined)
// 繼承了null 之后導(dǎo)致了 C.prototype 的__proto__ 斷裂朱浴,不在執(zhí)行Object
super
調(diào)用子集的時(shí)候super(),會(huì)指向當(dāng)前子類
class C {constructor(){this.name=1}}
console.log(C.__proto__===Function.prototype)
console.log(C.prototype.__proto__.__proto__)
super還可以作為對(duì)象使用,作為調(diào)用父級(jí)的方法
class A {
name=1
p(){
console.log(this.name)
}
}
class B extends A{
constructor(){
super()
this.name = 'ffff'
}
say(){
super.p()
}
}
const b = new B()
b.say() // ffff
// 此時(shí)調(diào)用super的時(shí)候就等于 A.prototype.p.call(this)
但是支能調(diào)用父級(jí)的prototype 的方法达椰,父級(jí)的this方法無(wú)法使用super.獲取
class A {
constructor(){
this.qqq="12345234"
}
p(){
console.log(this.name)
}
}
A.prototype.xx =111
class B extends A{
say(){
console.log(super.qqq)
console.log(super.p)
console.log(super.xx)
}
}
const b = new B()
b.say() // undefined funtion 111
super 的綁定場(chǎng)景
class A {
constructor(){
this.qqq="12345234"
}
}
class B extends A{
constructor(){
super()
super.qqq =9999999
console.log(this.qqq)
console.log(super.qqq)
}
}
new B() // 9999999 undefined
// 原因翰蠢,在調(diào)用new 的時(shí)候會(huì)把super綁定this,上,所有super.qqq =this.qqq
//砰碴。但在找super.qqq的時(shí)候躏筏,會(huì)去A 的 prototype上找
修改原生構(gòu)造函數(shù)
function Myclass (){
Array.call(this)
}
Myclass.prototype = Object.create(Array.prototype)
const arr1=new Myclass()
arr1.push(1)
console.log(arr1.length)
arr1[1] =33
console.log(arr1.length)
console.log(new Array())
這種或多或少會(huì)有問(wèn)題板丽,無(wú)法因?yàn)槌释鳎@種Array.call(this)這種Array的內(nèi)部是無(wú)法拿到this的
class MyArray extends Array{
constructor(){
super()
}
getNamelk(){
console.log("lkkkk")
}
}
let lk = new MyArray();
console.log(lk)
lk.getNamelk()
lk.push(111)
console.log(lk)
這種使用了es6的方法可以很好的定義自己的數(shù)組的方法,完美的繼承了數(shù)組的屬性
get set
class A {
get value(){
return this.name
}
set value(value){
this.name = value
}
}
let a = new A();
a.value=1111
console.log(a.value)
class的 generator
class A{
constructor(...arr){
this.arr=arr
}
*[Symbol.iterator](){
for(let i of this.arr){
yield i
}
}
}
let a = new A(1,2,3,4,5,6)
for(let m of a){
console.log(m)
}
混合模式
function mix (...max){
class mix {}
for(let m of max){
copeProtos(mix,m)
copeProtos(mix.prototype,m.prototype)
}
return mix
}
function copeProtos(a,b){
for(let i of Reflect.ownKeys(b)){
if(i!="prototype"&&i!="constructor"&&i!="name"){
let dec = Object.getOwnPropertyDescriptor(b,i);
Object.defineProperty(a,i,dec)
}
}
}
class A{
name=11
}
class B{
name=2323;
say(){
console.log("mmm")
}
}
const MaxAll = mix(A,B)
aa = new MaxAll()
console.log(aa.say())
differ算法
differ算法就是找到j(luò)sx和current Fiber的差異
規(guī)則有三
- 1整個(gè)樹時(shí)埃碱,前后跨越層級(jí)的時(shí)候去的重建
- 2 相同組件有相識(shí)的結(jié)構(gòu)猖辫,如果發(fā)生了div 變成p這種情況重建,
- 使用key 暗示react元素的不變
根據(jù)newchildren 的類型砚殿,有object(elementTpye)啃憎、number、string為單節(jié)點(diǎn)似炎,當(dāng)為array 時(shí)候?yàn)槎喙?jié)點(diǎn)辛萍。規(guī)則有兩種悯姊,
單個(gè)節(jié)點(diǎn)
<div key="1"><div>
<div key="2"><div>
// key 不同重建
<div key="1"></div>
<p key="1"></div>
// type 不同重建
<div key="1">1</div>
<div key="1">2</div>
// 復(fù)用
2 一對(duì)多時(shí)
// 老的
ul>li*3
// 新的
ul>p
會(huì)先判斷 key,如果key 不同,就刪除掉后面的li,表示p我們已經(jīng)找到了對(duì)應(yīng)的li
贩毕。如果key不同悯许,type相對(duì),給當(dāng)前元素標(biāo)記刪除辉阶。
源碼
function reconcileSingleElement(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
element: ReactElement
): Fiber {
const key = element.key;
let child = currentFirstChild;
// 首先判斷是否存在對(duì)應(yīng)DOM節(jié)點(diǎn)
while (child !== null) {
// 上一次更新存在DOM節(jié)點(diǎn)先壕,接下來(lái)判斷是否可復(fù)用
// 首先比較key是否相同
if (child.key === key) {
// key相同,接下來(lái)比較type是否相同
switch (child.tag) {
// ...省略case
default: {
if (child.elementType === element.type) {
// type相同則表示可以復(fù)用
// 返回復(fù)用的fiber
return existing;
}
// type不同則跳出switch
break;
}
}
// 代碼執(zhí)行到這里代表:key相同但是type不同
// 將該fiber及其兄弟fiber標(biāo)記為刪除
deleteRemainingChildren(returnFiber, child);
break;
} else {
// key不同谆甜,將該fiber標(biāo)記為刪除
deleteChild(returnFiber, child);
}
child = child.sibling;
}
// 創(chuàng)建新Fiber垃僚,并返回 ...省略
}
多個(gè)節(jié)點(diǎn)的differ
1 存在在多種情況,更新规辱,刪除和增加谆棺,由于react官方認(rèn)為,更新的頻率更高按摘,所以選擇跟新優(yōu)先級(jí)更高
####### 遍歷方式包券,由于返回的jsx的是這樣的
{
$$typeof: Symbol(react.element),
key: null,
props: {
children: [
{$$typeof: Symbol(react.element), type: "li", key: "0", ref: null, props: {…}, …}
{$$typeof: Symbol(react.element), type: "li", key: "1", ref: null, props: {…}, …}
{$$typeof: Symbol(react.element), type: "li", key: "2", ref: null, props: {…}, …}
{$$typeof: Symbol(react.element), type: "li", key: "3", ref: null, props: {…}, …}
]
},
ref: null,
type: "ul"
}
而,fiber炫贤,是單鏈表sibling獲取溅固,無(wú)法雙指針遍歷
遍歷第一次
// 之前
<li key="0">0</li>
<li key="1">1</li>
<li key="2">2</li>
// 之后
<li key="0">0</li>
<li key="2">1</li>
<li key="1">2</li>
- 遍歷children[0],olderfiber 判斷是否可用。
- 如果可用繼續(xù)遍歷兰珍。i++
- 如果不可用侍郭,判斷不用的場(chǎng)景,如果是key 不同直接跳出遍歷掠河。如果type不同在標(biāo)記delect
- 直到所有遍歷完成
第二次遍歷
情況如下
- oldfiber遍歷完了亮元,childer沒(méi)遍歷完,把剩下的newchiled生成的workinprogress.fiber 以此標(biāo)記為placement
- oldfiber 沒(méi)遍歷完唠摹,chiderb遍歷完了卤恳,這是標(biāo)記為deletion
- 兩者都沒(méi)遍歷完成
處理移動(dòng)的節(jié)點(diǎn)
會(huì)初始化一個(gè)lastindex的值,如果oldefiber.index<=lastindex 就往后移動(dòng)(往后添加元素的開銷最小)翠忠,然后跟新當(dāng)前oldefiber.index 和lastindex ,lastindex為當(dāng)前兩者最大值形入。以此類推,繼續(xù)往下比
// 之前
abcd
// 之后
acdb
===第一輪遍歷開始===
a(之后)vs a(之前)
key不變藕赞,可復(fù)用
此時(shí) a 對(duì)應(yīng)的oldFiber(之前的a)在之前的數(shù)組(abcd)中索引為0
所以 lastPlacedIndex = 0;
繼續(xù)第一輪遍歷...
c(之后)vs b(之前)
key改變成肘,不能復(fù)用,跳出第一輪遍歷
此時(shí) lastPlacedIndex === 0;
===第一輪遍歷結(jié)束===
===第二輪遍歷開始===
newChildren === cdb斧蜕,沒(méi)用完双霍,不需要執(zhí)行刪除舊節(jié)點(diǎn)
oldFiber === bcd,沒(méi)用完,不需要執(zhí)行插入新節(jié)點(diǎn)
將剩余oldFiber(bcd)保存為map
// 當(dāng)前oldFiber:bcd
// 當(dāng)前newChildren:cdb
繼續(xù)遍歷剩余newChildren
key === c 在 oldFiber中存在
const oldIndex = c(之前).index;
此時(shí) oldIndex === 2; // 之前節(jié)點(diǎn)為 abcd洒闸,所以c.index === 2
比較 oldIndex 與 lastPlacedIndex;
如果 oldIndex >= lastPlacedIndex 代表該可復(fù)用節(jié)點(diǎn)不需要移動(dòng)
并將 lastPlacedIndex = oldIndex;
如果 oldIndex < lastplacedIndex 該可復(fù)用節(jié)點(diǎn)之前插入的位置索引小于這次更新需要插入的位置索引染坯,代表該節(jié)點(diǎn)需要向右移動(dòng)
在例子中,oldIndex 2 > lastPlacedIndex 0丘逸,
則 lastPlacedIndex = 2;
c節(jié)點(diǎn)位置不變
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:bd
// 當(dāng)前newChildren:db
key === d 在 oldFiber中存在
const oldIndex = d(之前).index;
oldIndex 3 > lastPlacedIndex 2 // 之前節(jié)點(diǎn)為 abcd酒请,所以d.index === 3
則 lastPlacedIndex = 3;
d節(jié)點(diǎn)位置不變
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:b
// 當(dāng)前newChildren:b
key === b 在 oldFiber中存在
const oldIndex = b(之前).index;
oldIndex 1 < lastPlacedIndex 3 // 之前節(jié)點(diǎn)為 abcd,所以b.index === 1
則 b節(jié)點(diǎn)需要向右移動(dòng)
===第二輪遍歷結(jié)束===
最終acd 3個(gè)節(jié)點(diǎn)都沒(méi)有移動(dòng)鸣个,b節(jié)點(diǎn)被標(biāo)記為移動(dòng)
// 之前
abcd
// 之后
dabc
===第一輪遍歷開始===
d(之后)vs a(之前)
key改變羞反,不能復(fù)用,跳出遍歷
===第一輪遍歷結(jié)束===
===第二輪遍歷開始===
newChildren === dabc囤萤,沒(méi)用完昼窗,不需要執(zhí)行刪除舊節(jié)點(diǎn)
oldFiber === abcd,沒(méi)用完涛舍,不需要執(zhí)行插入新節(jié)點(diǎn)
將剩余oldFiber(abcd)保存為map
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:abcd
// 當(dāng)前newChildren dabc
key === d 在 oldFiber中存在
const oldIndex = d(之前).index;
此時(shí) oldIndex === 3; // 之前節(jié)點(diǎn)為 abcd澄惊,所以d.index === 3
比較 oldIndex 與 lastPlacedIndex;
oldIndex 3 > lastPlacedIndex 0
則 lastPlacedIndex = 3;
d節(jié)點(diǎn)位置不變
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:abc
// 當(dāng)前newChildren abc
key === a 在 oldFiber中存在
const oldIndex = a(之前).index; // 之前節(jié)點(diǎn)為 abcd,所以a.index === 0
此時(shí) oldIndex === 0;
比較 oldIndex 與 lastPlacedIndex;
oldIndex 0 < lastPlacedIndex 3
則 a節(jié)點(diǎn)需要向右移動(dòng)
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:bc
// 當(dāng)前newChildren bc
key === b 在 oldFiber中存在
const oldIndex = b(之前).index; // 之前節(jié)點(diǎn)為 abcd富雅,所以b.index === 1
此時(shí) oldIndex === 1;
比較 oldIndex 與 lastPlacedIndex;
oldIndex 1 < lastPlacedIndex 3
則 b節(jié)點(diǎn)需要向右移動(dòng)
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:c
// 當(dāng)前newChildren c
key === c 在 oldFiber中存在
const oldIndex = c(之前).index; // 之前節(jié)點(diǎn)為 abcd掸驱,所以c.index === 2
此時(shí) oldIndex === 2;
比較 oldIndex 與 lastPlacedIndex;
oldIndex 2 < lastPlacedIndex 3
則 c節(jié)點(diǎn)需要向右移動(dòng)
===第二輪遍歷結(jié)束===