前端面試整理自用

  • 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")
image.png
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)
image.png
實(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)看看這指的到底是什么症见。

WeakMapMap 的第一個(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)击喂,完成三次握手维苔。


    三次握手.png

    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
關(guān)系鏈.png

事件冒泡、事件捕獲和事件委托

// 事件監(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é)束===
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市没佑,隨后出現(xiàn)的幾起案子毕贼,更是在濱河造成了極大的恐慌,老刑警劉巖蛤奢,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鬼癣,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡啤贩,警方通過(guò)查閱死者的電腦和手機(jī)待秃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)痹屹,“玉大人章郁,你說(shuō)我怎么就攤上這事≈狙埽” “怎么了暖庄?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)足画。 經(jīng)常有香客問(wèn)我雄驹,道長(zhǎng)佃牛,這世上最難降的妖魔是什么淹辞? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上象缀,老公的妹妹穿的比我還像新娘蔬将。我一直安慰自己,他們只是感情好央星,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布霞怀。 她就那樣靜靜地躺著,像睡著了一般莉给。 火紅的嫁衣襯著肌膚如雪毙石。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天颓遏,我揣著相機(jī)與錄音徐矩,去河邊找鬼。 笑死叁幢,一個(gè)胖子當(dāng)著我的面吹牛滤灯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播曼玩,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼鳞骤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了黍判?” 一聲冷哼從身側(cè)響起豫尽,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎顷帖,沒(méi)想到半個(gè)月后拂募,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窟她,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年陈症,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片震糖。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡录肯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吊说,到底是詐尸還是另有隱情论咏,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布颁井,位于F島的核電站厅贪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏雅宾。R本人自食惡果不足惜养涮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贯吓,春花似錦懈凹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至爬舰,卻和暖如春们陆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背情屹。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工棒掠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屁商。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓烟很,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蜡镶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雾袱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容