中高級(jí)前端面試知識(shí)點(diǎn)匯總

1.1亿絮、什么是HTML語(yǔ)義化?有什么好處麸拄?

根據(jù)內(nèi)容的結(jié)構(gòu)化(內(nèi)容語(yǔ)義化)派昧,選擇合適的標(biāo)簽(代碼語(yǔ)義化)便于開(kāi)發(fā)者閱讀和寫(xiě)出更優(yōu)雅的代碼的同時(shí)讓瀏覽器的爬蟲(chóng)和機(jī)器很好地解析。

為了在沒(méi)有 CSS 的情況下拢切,頁(yè)面也能呈現(xiàn)出很好地內(nèi)容結(jié)構(gòu)蒂萎、代碼結(jié)構(gòu):為了裸奔時(shí)好看;

用戶(hù)體驗(yàn):例如 title淮椰、alt 用于解釋名詞或解釋圖片信息五慈、label標(biāo)簽的活用帮毁;

有利于 SEO:和搜索引擎建立良好溝通,有助于爬蟲(chóng)抓取更多的有效信息:爬蟲(chóng)依賴(lài)于標(biāo)簽來(lái)確定上下文和各個(gè)關(guān)鍵字的權(quán)重豺撑;

方便其他設(shè)備解析(如屏幕閱讀器烈疚、盲人閱讀器、移動(dòng)設(shè)備)以語(yǔ)義的方式來(lái)渲染網(wǎng)頁(yè)聪轿;

便于團(tuán)隊(duì)開(kāi)發(fā)和維護(hù)爷肝,語(yǔ)義化更具可讀性,是下一步網(wǎng)頁(yè)的重要?jiǎng)酉蚵酱恚裱@個(gè)標(biāo)準(zhǔn)灯抛,可以減少差異化。

1.2音瓷、html5有哪些新特性对嚼?

HTML5 已經(jīng)不是 SGML 的子集

新增關(guān)于圖像,地理位置绳慎,緩存纵竖,多任務(wù)等多個(gè)功能

用于媒體回放的 Video 和 Audio

語(yǔ)義化更好的標(biāo)簽如 header、footer杏愤、section靡砌、aside、nav珊楼、canvas通殃、time等等,利于SEO優(yōu)化

新增的表單元素如 <datalist>厕宗、<keygen>画舌、<output>

新增的表單屬性如 autocomplete、autofocus已慢、placeholder曲聂、required、step等等蛇受。

HTML5 Web 存儲(chǔ) sessionStorage 和 localStorage

HTML5 WebSocket

HTML5 離線Web應(yīng)用(應(yīng)用程序緩存)

1.3句葵、你能解釋一下CSS的盒子模型么?

CSS盒模型是圍繞在HTML元素周?chē)亩x Border(邊界)兢仰,padding(內(nèi)邊距)和

margin(外邊距)的矩形空間

Border(邊界):定義了元素包含的最大區(qū)域

Padding(內(nèi)邊距):定義了邊界和內(nèi)部元素的間距

Margin:定義了邊界和任何相鄰元素的間距

1.4乍丈、CSS選擇器有哪些?哪些屬性可以繼承把将?

CSS選擇符:id選擇器(#myid)轻专、類(lèi)選擇器(.myclassname)、標(biāo)簽選擇器(div, h1, p)察蹲、相鄰選擇器(h1 + p)请垛、子選擇器(ul > li)催训、后代選擇器(li a)、通配符選擇器(*)宗收、屬性選擇器(a[rel=”external”])漫拭、偽類(lèi)選擇器(a:hover, li:nth-child)

可繼承的屬性:font-size, font-family, color

不可繼承的樣式:border, padding, margin, width, height

優(yōu)先級(jí)(就近原則):!important > [ id > class > tag ]

!important 比內(nèi)聯(lián)優(yōu)先級(jí)高

1.5、CSS3新增偽類(lèi)有那些?

E:nth-last-child(n)

E:nth-of-type(n)

E:nth-last-of-type(n)

E:last-child

E:first-of-type

E:only-child

E:only-of-type

E:empty

E:checked

E:enabled

E:disabled

E::selection

E:not(s)

E::not(.s)

tbody: nth-child(even), nth-child(odd)/*:此處他們分別代表了表格(tbody)下面的偶數(shù)行和奇數(shù)行(tr)*/等等......

1.6混稽、css3有那些新特性采驻?

RGBA 和透明度

background-image background-origin(content-box/padding-box/border-box)

background-size background-repeat

word-wrap(對(duì)長(zhǎng)的不可分割單詞換行)word-wrap:break-word

文字陰影:text-shadow: 5px 5px 5px #FF0000;(水平陰影,垂直陰影匈勋,模糊距離礼旅,陰影顏色)

font-face屬性:定義自己的字體

圓角(邊框半徑):border-radius 屬性用于創(chuàng)建圓角

邊框圖片:border-image: url(border.png) 30 30 round

盒陰影:box-shadow: 10px 10px 5px #888888

媒體查詢(xún):定義兩套css,當(dāng)瀏覽器的尺寸變化時(shí)會(huì)采用不同的屬性

1.7洽洁、對(duì)BFC規(guī)范(塊級(jí)格式化上下文:block formatting context)的理解痘系?

BFC規(guī)定了內(nèi)部的 Block Box 如何布局。

定位方案:

內(nèi)部的 Box 會(huì)在垂直方向上一個(gè)接一個(gè)放置饿自。

Box 垂直方向的距離由 margin 決定汰翠,屬于同一個(gè)BFC的兩個(gè)相鄰Box的margin會(huì)發(fā)生重疊。

每個(gè)元素的margin box 的左邊璃俗,與包含塊border box的左邊相接觸哎榴。

BFC的區(qū)域不會(huì)與float box重疊绒极。

BFC是頁(yè)面上的一個(gè)隔離的獨(dú)立容器辜伟,容器里面的子元素不會(huì)影響到外面的元素窄做。

計(jì)算BFC的高度時(shí)伴奥,浮動(dòng)元素也會(huì)參與計(jì)算洲炊。

滿(mǎn)足下列條件之一就可觸發(fā)BFC:

根元素,即html

float 的值不為 none(默認(rèn))

overflow 的值不為 visible(默認(rèn))

display 的值為 inline-block尼啡、table-cell暂衡、table-caption

position 的值為 absolute 或 fixed

1.8、 CSS優(yōu)化崖瞭、提高性能的方法有哪些狂巢?

避免過(guò)度約束

避免后代選擇符

避免鏈?zhǔn)竭x擇符

使用緊湊的語(yǔ)法

避免不必要的命名空間

避免不必要的重復(fù)

最好使用表示語(yǔ)義的名字。一個(gè)好的類(lèi)名應(yīng)該是描述他是什么而不是像什么

避免书聚!important唧领,可以選擇其他選擇器

盡可能的精簡(jiǎn)規(guī)則,你可以合并不同類(lèi)里的重復(fù)規(guī)則

1.9雌续、兩個(gè)div并排斩个,左邊div固定寬度,右邊寬度自適應(yīng)”驯杜,至少列出4種受啥。

方式一:BFC(塊級(jí)格式化上下文)

思路:左邊定寬 float:left,右邊采用 overflow: hidden; /* 觸發(fā)bfc */

方式二:采用flex布局

這種應(yīng)該是最簡(jiǎn)單的方式,右邊 flex:1

方式三:采用 display:table的方式來(lái)實(shí)現(xiàn)布局

父元素 display:table鸽心,兩個(gè)子元素采用 display:table-cell;

方式四:采用calc計(jì)算寬度的方式來(lái)實(shí)現(xiàn)

方式五:采用absolute+margin-left來(lái)實(shí)現(xiàn)

1.10腔呜、Sass和Less的異同之處。

1再悼、Less 環(huán)境較 Sass簡(jiǎn)單

Cass的安裝需要安裝Ruby環(huán)境,Less基于JavaScript膝但,是需要引入Less.js來(lái)處理代碼輸出css到瀏覽器冲九,也可以在開(kāi)發(fā)環(huán)節(jié)使用Less,然后編譯成css文件,直接放在項(xiàng)目中莺奸,有l(wèi)ess.app丑孩、SimpleLess、CodeKit.app這樣的工具灭贷,也有在線編輯地址温学。

2、Less 使用較 Sass簡(jiǎn)單

LESS 并沒(méi)有裁剪 CSS 原有的特性甚疟,而是在現(xiàn)有 CSS 語(yǔ)法的基礎(chǔ)上仗岖,為 CSS 加入程序式語(yǔ)言的特性。只要你了解 CSS 基礎(chǔ)就可以很容易上手览妖。

3轧拄、從功能出發(fā),Sass 較 Less 略強(qiáng)大一些

①sass有變量和作用域讽膏。

- $variable檩电,like php;

- #{$variable}like ruby府树;

- 變量有全局和局部之分俐末,并且有優(yōu)先級(jí)。

② sass 有函數(shù)的概念奄侠;

- @function和@return以及函數(shù)參數(shù)(還有不定參)可以讓你像js開(kāi)發(fā)那樣封裝你想要的邏輯卓箫。

-@mixin類(lèi)似function但缺少像function的編程邏輯,更多的是提高css代碼段的復(fù)用性和模塊化遭铺,這個(gè)用的人也是最多的丽柿。

-ruby提供了非常豐富的內(nèi)置原生api。

③進(jìn)程控制:

-條件:@if @else魂挂;

-循環(huán)遍歷:@for @each @while

-繼承:@extend

-引用:@import

④數(shù)據(jù)結(jié)構(gòu):

-$list類(lèi)型=數(shù)組甫题;

-$map類(lèi)型=object;

其余的也有string涂召、number坠非、function等類(lèi)型

4、Less 與Sass 處理機(jī)制不一樣

前者是通過(guò)客戶(hù)端處理的果正,后者是通過(guò)服務(wù)端處理炎码,相比較之下前者解析會(huì)比后者慢一點(diǎn)

5、關(guān)于變量在 Less 和 Sass 中的唯一區(qū)別就是 Less 用@秋泳,Sass 用$潦闲。

###二、Javascript 部分

2.1迫皱、介紹JavaScript的基本數(shù)據(jù)類(lèi)型

String歉闰、Number、Boolean、Null和敬、Undefined凹炸、Symbol、BigInt

2.2談?wù)則his的理解

this 總是指向函數(shù)的直接調(diào)用者(而非間接調(diào)用者)

如果有new關(guān)鍵字昼弟,this 指向new 出來(lái)的那個(gè)對(duì)象

在事件中啤它,this 指向目標(biāo)元素,特殊的是IE的attachEvent中的this總是指向全局對(duì)象window舱痘。

2.3变骡、null,undefined的區(qū)別衰粹?

null表示一個(gè)對(duì)象被定義了锣光,但存放了空指針,轉(zhuǎn)換為數(shù)值時(shí)為0铝耻。

undefined表示聲明的變量未初始化誊爹,轉(zhuǎn)換為數(shù)值時(shí)為NAN。

typeof(null) -- object;

typeof(undefined) -- undefined

2.4瓢捉、 什么是閉包(closure)频丘,為什么要用它?

閉包指的是一個(gè)函數(shù)可以訪問(wèn)另一個(gè)函數(shù)作用域中變量泡态。常見(jiàn)的構(gòu)造方法搂漠,是在一個(gè)函數(shù)內(nèi)部定義另外一個(gè)函數(shù)。內(nèi)部函數(shù)可以引用外層的變量某弦;外層變量不會(huì)被垃圾回收機(jī)制回收桐汤。

注意,閉包的原理是作用域鏈靶壮,所以閉包訪問(wèn)的上級(jí)作用域中的變量是個(gè)對(duì)象怔毛,其值為其運(yùn)算結(jié)束后的最后一個(gè)值。

優(yōu)點(diǎn):避免全局變量污染腾降。缺點(diǎn):容易造成內(nèi)存泄漏拣度。

特性:

a. JavaScript允許你使用在當(dāng)前函數(shù)以外定義的變量

b. 即使外部函數(shù)已經(jīng)返回,當(dāng)前函數(shù)仍然可以引用在外部函數(shù)所定義的變量

c. 閉包可以更新外部變量的值

d. 用閉包模擬私有方法

由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中螃壤,內(nèi)存消耗很大抗果,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁(yè)的性能問(wèn)題

例子:

function makeFunc() {

? ? var name = "Mozilla";

? ? function displayName() {

? ? ? ? console.log(name);

? ? }

? ? return displayName;

}

var myFunc = makeFunc();

myFunc();? //輸出Mozilla

2.5奸晴、如何判斷一個(gè)對(duì)象是否屬于某個(gè)類(lèi)冤馏?

使用instanceof 即if(a instanceof Person){alert('yes');}

2.6、new操作符具體干了什么呢?

創(chuàng)建一個(gè)空對(duì)象寄啼,并且 this 變量引用該對(duì)象宿接,同時(shí)還繼承了該函數(shù)的原型赘淮。

屬性和方法被加入到 this 引用的對(duì)象中。

新創(chuàng)建的對(duì)象由 this 所引用睦霎,并且最后隱式的返回 this 。

2.6走诞、JS延遲加載的方式有哪些副女?

JS的延遲加載有助與提高頁(yè)面的加載速度。

defer和async蚣旱、動(dòng)態(tài)創(chuàng)建DOM方式(用得最多)碑幅、按需異步載入JS

defer:延遲腳本。立即下載塞绿,但延遲執(zhí)行(延遲到整個(gè)頁(yè)面都解析完畢后再運(yùn)行)沟涨,按照腳本出現(xiàn)的先后順序執(zhí)行。

async:異步腳本异吻。下載完立即執(zhí)行裹赴,但不保證按照腳本出現(xiàn)的先后順序執(zhí)行。

2.7诀浪、call和apply的區(qū)別

call()方法和apply()方法的作用相同棋返,動(dòng)態(tài)改變某個(gè)類(lèi)的某個(gè)方法的運(yùn)行環(huán)境。他們的區(qū)別在于接收參數(shù)的方式不同雷猪。在使用call(obj,a1,a2...)方法時(shí)睛竣,傳遞給函數(shù)的參數(shù)必須逐個(gè)列舉出來(lái)。使用apply(obj,[a1,a2...])時(shí)求摇,傳遞給函數(shù)的是參數(shù)數(shù)組射沟。

2.8、數(shù)組對(duì)象有哪些原生方法与境,列舉一下

pop验夯、push、shift嚷辅、unshift簿姨、splice、reverse簸搞、sort扁位、concat、join趁俊、slice域仇、toString、indexOf寺擂、lastIndexOf暇务、reduce泼掠、reduceRight、forEach垦细、map择镇、filter、every括改、some

2.9腻豌、什么是跨域?如何解決嘱能?

要明白什么是跨域之前吝梅,首先要明白什么是同源策略?

同源策略就是用來(lái)限制從一個(gè)源加載的文檔或腳本與來(lái)自另一個(gè)源的資源進(jìn)行交互惹骂。那怎樣判斷是否是同源呢苏携?

如果協(xié)議,端口(如果指定了)和主機(jī)對(duì)于兩個(gè)頁(yè)面是相同的对粪,則兩個(gè)頁(yè)面具有相同的源右冻,也就是同源。也就是說(shuō)衩侥,要同時(shí)滿(mǎn)足以下3個(gè)條件国旷,才能叫同源:

協(xié)議相同

端口相同

主機(jī)相同

解決方案:

1.iframe

隨著近年來(lái)前端技術(shù)的飛躍發(fā)展以及移動(dòng)互聯(lián)網(wǎng)時(shí)代的洗禮,iframe的使用漸漸的不被建議茫死,雖然也是一種跨域請(qǐng)求的解決方案跪但,但這里就不再講述,請(qǐng)讀者自行查閱網(wǎng)上資料峦萎。

2.jsonp

jsonp是比較常用的方法屡久,我們假設(shè)a.com域名需要向b.com發(fā)起一個(gè)api請(qǐng)求(jsonp的一個(gè)缺點(diǎn)是,僅能接受GET方式)爱榔,則使用JSONP完成該過(guò)程的被环。

3. 通過(guò)請(qǐng)求同域下的api,間接獲取它域的數(shù)據(jù)

我們?nèi)砸杂蛎鸻.com/demo.html需獲取b.com下的數(shù)據(jù)為例详幽,這時(shí)候只要在a.com下創(chuàng)建一個(gè)demo.php筛欢,由demo.php通過(guò)curl的方式向b.com發(fā)起數(shù)據(jù)請(qǐng)求,并包裝請(qǐng)求結(jié)果返回給a.com/demo.html頁(yè)面唇聘。這里主要是通過(guò)與a.com/demo.html同域下的a.com/demo.php做了一層數(shù)據(jù)請(qǐng)求代理版姑,避免了前端跨域請(qǐng)求。

4.使用web服務(wù)器的反向代理設(shè)置

同樣是使用代理的思維迟郎,但與2不同的是剥险,我們這里使用web服務(wù)器的反向代理配置:

Nginx反向代理可以使用 proxy_pass

Apache2的反向代理的配置可以使用ProxyPass

5.設(shè)置header頭(CORS)

在你要跨域請(qǐng)求的api里,設(shè)置header頭Access-Control-Allow-Origin: "*";

2.10宪肖、如何實(shí)現(xiàn)js中的繼承

1表制、原型繼承的第一種方式:

function Cat(name){

? ? this.name=name;

}

//原型繼承

Cat.prototype.say=function(){

? ? alert("你好健爬,我是一只貓,我叫:"+this.name);

}

2么介、原型繼承第二種方式:

function Cat(name) {

? ? this.name = name;

}

function Animal() {}

Animal.prototype.run = function () {

? ? alert("動(dòng)物跑");

};

Cat.prototype = new Animal();

Cat.prototype.constructor=Cat;

3娜遵、借用構(gòu)造函數(shù)

function Cat(name,age) {

? ? Animal.call(this,name,age);

}

function Animal(name,age) {

? ? this.name = name;

? ? this.age=age;

}

4、經(jīng)典繼承

function create(obj) {

? ? if(Object.create) {

? ? return Object.create(obj);?

? ? } else {

? ? function F(){};

? ? F.prototype = obj;

? ? return new F();

? ? }

}

2.11夭拌、看下列代碼,輸出什么魔熏?解釋原因。

var undefined;//此時(shí)undefined這個(gè)變量的值是undefined

undefined == null; // true

1 == true;? // true

此時(shí)會(huì)把布爾類(lèi)型的值轉(zhuǎn)換為數(shù)字類(lèi)型 true=1 false=0

2 == true;? // false

0 == false;? // true

0 == '';? ? // true

NaN == NaN;? // false isNaN

[] == false; // true? 解釋?zhuān)簳?huì)把[]和false都通過(guò)Number()轉(zhuǎn)換為數(shù)字類(lèi)型

[] == ![];? // true? 解釋?zhuān)?[]:false

[]==[];//false

一個(gè)是number一個(gè)是string時(shí)鸽扁,會(huì)嘗試將string轉(zhuǎn)換為number

一個(gè)是number一個(gè)是boolean,將boolean轉(zhuǎn)換為number镶骗,結(jié)果:true:1 false:0

一個(gè)是object 另一個(gè)是string或number桶现,將Object轉(zhuǎn)換成number或string

所以,對(duì)于0鼎姊、空字符串的判斷骡和,建議使用 “===” ∠嗫埽“===”會(huì)先判斷兩邊的值類(lèi)型慰于,類(lèi)型不匹配時(shí)為false。

2.12唤衫、已知數(shù)組numberArray = [3,6,2,4,1,5]婆赠;

1) 實(shí)現(xiàn)對(duì)該數(shù)組的倒排,輸出[5,1,4,2,6,3]

function reverseArray(arr){

? ? var result=[];

? ? //方法1:

? ? /*for (var i = arr.length - 1; i >= 0; i--) {

? ? ? ? result.push(arr[i]);

? ? }*/

? ? //方法2:

? ? for (var i = 0, len = arr.length; i < len; i++) {

? ? ? ? result.unshift(arr[i]);

? ? }

? ? return result;

}

2) 實(shí)現(xiàn)對(duì)該數(shù)組的降序排列佳励,輸出[6,5,4,3,2,1]

冒泡排序過(guò)程演示

function sortDesc(arr) {

? ? for (var i = 0, len = arr.length; i < len; i++) {

? ? ? ? for (var j = i + 1, len2 = arr.length; j < len2; j++) {

? ? ? ? ? ? //>就是降序 <就是升序

? ? ? ? ? ? if (arr[j] > arr[i]) {

? ? ? ? ? ? ? ? var temp = arr[j];

? ? ? ? ? ? ? ? arr[j] = arr[i];

? ? ? ? ? ? ? ? arr[i] = temp;

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? return arr;

}

2.13休里、有這樣一個(gè)URL:http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e,請(qǐng)寫(xiě)一段JS程序提取URL中的各個(gè)GET參數(shù)(參數(shù)名和參數(shù)個(gè)數(shù)不確定)赃承,將其按key-value形式返回到一個(gè)json結(jié)構(gòu)中妙黍,如{a:’1′, b:’2′, c:”, d:’xxx’, e:undefined}。

答案:

function serlize(url){

? ? var result={};

? ? //1瞧剖、尋找拭嫁?后面的字符串

? ? url=url.substr(url.indexOf("?")+1);

? ? //2、將字符串用&分隔

? ? var args=url.split("&");//[“a=1”,”b=2”]

? ? for (var i = 0, len = args.length; i < len; i++) {

? ? ? ? var arg = args[i];

? ? var item = arg.split('=');

? ? ? ? //3抓于、對(duì)象的鍵=值

? ? ? ? result[item[0]]= item[1];

? ? }

? ? return result;

}

serlize(‘http://item.taobao.com/item.htm?a=1&b=2&c=&d=xxx&e‘);

2.14做粤、什么是 Javascript 高階函數(shù)?并寫(xiě)出例子

如果一個(gè)函數(shù)操作其他函數(shù)毡咏,即將其他函數(shù)作為參數(shù)或?qū)⒑瘮?shù)作為返回值驮宴,則稱(chēng)這個(gè)函數(shù)是一個(gè)高階函數(shù)。

我們來(lái)看看下面的例子:

function abc(temp){

console.log(temp);

}

function def(temp1,temp2){

temp1(temp2);

}

def(abc,"sos");

執(zhí)行之后呕缭,輸出:sos

首先我們定義了兩個(gè)函數(shù) abc() 和 def() ,

然后執(zhí)行 def(abc,"sos"),我們把a(bǔ)bc 這個(gè)函數(shù)作為了函數(shù)def() 的一個(gè)參數(shù)堵泽,

最后在函數(shù)def 中執(zhí)行了 abc() 這個(gè)函數(shù)修己;

2.15、JavaScript 什么是原型鏈?

原型鏈 : 實(shí)例對(duì)象與原型之間的鏈接,叫做原型鏈

Function.prototype.a = "a";

Object.prototype.b = "b";

function Person(){}

console.log(Person);? ? //function Person()

let p = new Person();

console.log(p);? ? ? ? //Person {} 對(duì)象

console.log(p.a);? ? ? //undefined

console.log(p.b);? ? ? //b

想一想p.a打印結(jié)果為undefined迎罗,p.b結(jié)果為b

? 解析:

? p是Person()的實(shí)例睬愤,是一個(gè)Person對(duì)象,它擁有一個(gè)屬性值__proto__纹安,并且__proto__是一個(gè)對(duì)象尤辱,包含兩個(gè)屬性值constructor和__proto__。

console.log(p.__proto__.constructor);? //function Person(){}

console.log(p.__proto__.__proto__);? ? //對(duì)象{}厢岂,擁有很多屬性值

我們會(huì)發(fā)現(xiàn)p.__proto__.constructor返回的結(jié)果為構(gòu)造函數(shù)本身光督,

p.__proto__.__proto__有很多參數(shù)

我們調(diào)用constructor屬性,p.___proto__.__proto__.constructor得到擁有多個(gè)參數(shù)的Object()函數(shù)塔粒,Person.prototype的隱式原型的constructor指向Object()结借,即Person.prototype.__proto__.constructor == Object()

從p.__proto__.constructor返回的結(jié)果為構(gòu)造函數(shù)本身得到Person.prototype.constructor == Person()所以p.___proto__.__proto__== Object.prototype

所以p.b打印結(jié)果為b,p沒(méi)有b屬性卒茬,會(huì)一直通過(guò)__proto__向上查找船老,最后當(dāng)查找到Object.prototype時(shí)找到,最后打印出b圃酵,向上查找過(guò)程中柳畔,得到的是Object.prototype,而不是Function.prototype郭赐,找不到a屬性薪韩,所以結(jié)果為undefined,這就是原型鏈堪置,通過(guò)__proto__向上進(jìn)行查找躬存,最終到null結(jié)束

console.log(p.__proto__.__proto__.__proto__);? //null

console.log(Object.prototype.__proto__);? ? ? ? //null

大家理解剛才的過(guò)程,相信下面這些應(yīng)該也都明白

//Function

function Function(){}

console.log(Function);? //Function()

console.log(Function.prototype.constructor);? ? //Function()

console.log(Function.prototype.__proto__);? ? ? //Object.prototype

console.log(Function.prototype.__proto__.__proto__);? ? //NULL

console.log(Function.prototype.__proto__.constructor);? //Object()

console.log(Function.prototype.__proto__ === Object.prototype); //true

總結(jié):

1.查找屬性舀锨,如果本身沒(méi)有岭洲,則會(huì)去__proto__中查找,也就是構(gòu)造函數(shù)的顯式原型中查找坎匿,如果構(gòu)造函數(shù)中也沒(méi)有該屬性盾剩,因?yàn)闃?gòu)造函數(shù)也是對(duì)象,也有__proto__替蔬,那么會(huì)去它的顯式原型中查找告私,一直到null,如果沒(méi)有則返回undefined

2.p.__proto__.constructor? == function Person(){}

3.p.___proto__.__proto__== Object.prototype

4.p.___proto__.__proto__.__proto__== Object.prototype.__proto__ == null? ? ? ?

5.通過(guò)__proto__形成原型鏈而非protrotype

最后附上一張圖承桥,大家閱讀完之后驻粟,看圖應(yīng)該可以很容易理解

###三、ES6凶异、ES7蜀撑、ES8 的相關(guān)知識(shí)點(diǎn)

ES6

塊級(jí)作用域 關(guān)鍵字let, 常量const

對(duì)象屬性賦值簡(jiǎn)寫(xiě)(property value shorthand)

解構(gòu)賦值

函數(shù)參數(shù) - 默認(rèn)值挤巡、參數(shù)打包、 數(shù)組展開(kāi)(Default 酷麦、Rest 矿卑、Spread)

箭頭函數(shù) (Arrow functions)

字符串模板 Template strings ${}

迭代器(Iterators)for (var n of ['a','b','c'])

生成器 (Generators)

class、constructor沃饶、extends母廷、super

Modules(模塊)

具有CommonJS的精簡(jiǎn)語(yǔ)法、唯一導(dǎo)出出口(single exports)和循環(huán)依賴(lài)(cyclic dependencies)的特點(diǎn)糊肤。

類(lèi)似AMD琴昆,支持異步加載和可配置的模塊加載。

Map + Set + WeakMap + WeakSet

WeakMap馆揉、WeakSet作為屬性鍵的對(duì)象如果沒(méi)有別的變量在引用它們椎咧,則會(huì)被回收釋放掉。

Math + Number + String + Array + Object APIs

Proxies:使用代理(Proxy)監(jiān)聽(tīng)對(duì)象的操作

.Symbols:調(diào)用symbol函數(shù)產(chǎn)生把介,它接收一個(gè)可選的名字參數(shù),該函數(shù)返回的symbol是唯一的蟋座。

^痔摺!向臀!Promises:處理異步操作的對(duì)象巢墅,用鏈?zhǔn)秸{(diào)用組織代碼

var promise = new Promise((resolve, reject) => {

? this.login(resolve)

}) //鏈?zhǔn)秸{(diào)用

.then(() => this.getInfo())

.catch(() => { console.log("Error") })

4.ES7

求冪運(yùn)算符(**)

3 ** 2? ? ? ? ? // 9

等價(jià)于:

Math.pow(3, 2)? // 9

Array.prototype.includes()方法:不能比較復(fù)雜類(lèi)型數(shù)據(jù),查找一個(gè)值在不在數(shù)組里券膀,若在君纫,則返回true,反之返回false芹彬。

['a', 'b', 'c'].includes('a')? ? // true

等價(jià)于

['a', 'b', 'c'].indexOf('a') > -1? ? ? //true

函數(shù)作用域中嚴(yán)格(strict)模式的變更蓄髓。

裝飾器(Decorator):修改類(lèi)的行為

參數(shù):target(所要修飾的目標(biāo)類(lèi)), name(所要修飾的屬性名), descriptor(該屬性的描述對(duì)象)

使用:npm install core-decorators –save

// 將某個(gè)屬性或方法標(biāo)記為不可寫(xiě)。

@readonly?

// 標(biāo)記一個(gè)屬性或方法舒帮,以便它不能被刪除; 也阻止了它通過(guò)Object.defineProperty被重新配置

@nonconfigurable?

// 立即將提供的函數(shù)和參數(shù)應(yīng)用于該方法会喝,允許您使用lodash提供的任意助手來(lái)包裝方法。 第一個(gè)參數(shù)是要應(yīng)用的函數(shù)玩郊,所有其他參數(shù)將傳遞給該裝飾函數(shù)肢执。

@decorate?

// 如果你沒(méi)有像Babel 6那樣的裝飾器語(yǔ)言支持,或者甚至沒(méi)有編譯器的vanilla ES5代碼译红,那么可以使用applyDecorators()助手预茄。

@extendDescriptor

// 將屬性標(biāo)記為不可枚舉。

@nonenumerable

// 防止屬性初始值設(shè)定項(xiàng)運(yùn)行侦厚,直到實(shí)際查找修飾的屬性耻陕。

@lazyInitialize

// 強(qiáng)制調(diào)用此函數(shù)始終將此引用到類(lèi)實(shí)例拙徽,即使該函數(shù)被傳遞或?qū)⑹テ渖舷挛摹?/p>

@autobind

// 使用棄用消息調(diào)用console.warn()。 提供自定義消息以覆蓋默認(rèn)消息淮蜈。

@deprecate

// 在調(diào)用裝飾函數(shù)時(shí)禁止任何JavaScript console.warn()調(diào)用斋攀。

@suppressWarnings

// 將屬性標(biāo)記為可枚舉。

@enumerable

// 檢查標(biāo)記的方法是否確實(shí)覆蓋了原型鏈上相同簽名的函數(shù)梧田。

@override?

// 使用console.time和console.timeEnd為函數(shù)計(jì)時(shí)提供唯一標(biāo)簽丧失,其默認(rèn)前綴為ClassName.method嗦董。

@time

// 使用console.profile和console.profileEnd提供函數(shù)分析,并使用默認(rèn)前綴為ClassName.method的唯一標(biāo)簽。

@profile

@noConcurrent 避免并發(fā)調(diào)用鳖枕,在上一次操作結(jié)果返回之前,不響應(yīng)重復(fù)操作

@makeMutex 多函數(shù)互斥抱婉,具有相同互斥標(biāo)識(shí)的函數(shù)不會(huì)并發(fā)執(zhí)行

@withErrToast 捕獲async函數(shù)中的異常寻拂,并進(jìn)行錯(cuò)誤提示

@mixinList 用于分頁(yè)加載,上拉加載時(shí)返回拼接數(shù)據(jù)及是否還有數(shù)據(jù)提示

@typeCheck 檢測(cè)函數(shù)參數(shù)類(lèi)型

import {noConcurrent} from './decorators';

methods: {

? @noConcurrent? ? //避免并發(fā)逢艘,點(diǎn)擊提交后旦袋,在接口返回之前無(wú)視后續(xù)點(diǎn)擊

? async onSubmit(){

? ? let submitRes = await this.$http({...});

? ? //...

? ? return;

? }

}

methods: {

? @mixinList({needToast: false})

? async loadGoods(params = {}){

? ? let goodsRes = await this.$http(params);

? ? return goodsRes.respData.infos;

? },

? async hasMore() {

? ? let result = await this.loadgoods(params);

? ? if(result.state === 'nomore') this.tipText = '沒(méi)有更多了';

? ? this.goods = result.list;

? }

}

// 上拉加載調(diào)用hasMore函數(shù),goods數(shù)組就會(huì)得到所有拼接數(shù)據(jù)

// loadGoods可傳三個(gè)參數(shù) params函數(shù)需要參數(shù) ,startNum開(kāi)始的頁(yè)碼,clearlist清空數(shù)組

// mixinList可傳一個(gè)參數(shù) needToast 沒(méi)有數(shù)據(jù)是否需要toast提示

5.ES8

異步函數(shù)(Async function)使用形式:

函數(shù)聲明: async function foo() {}

函數(shù)表達(dá)式: const foo = async function() {}

對(duì)象的方式: let obj = { async foo() {} }

箭頭函數(shù): const foo = async () => {}

this.$http.jsonp('/login', (res) => {

? this.$http.jsonp('/getInfo', (info) => {

? ? // do something

? })

})

異步編程機(jī)制:Generator? ? async/await

function * 函數(shù)名(){

? ? yieId 'hello';

? ? yieId 'world';

? ? return 'ending';

}

var hs = 函數(shù)名();

hs.next();

// { value: 'hello', done: false }

hs.next();

// { value: 'world', done: false }

hs.next();

// { value: 'ending', done: true }

hs.next();

// { value: undefined, done: true }

//自動(dòng)執(zhí)行Generator函數(shù)

async function asyncFunc(params) {

? const result1 = await this.login()

? const result2 = await this.getInfo()

}

調(diào)用 Generator 函數(shù)后它改,該函數(shù)并不執(zhí)行疤孕,返回的也不是函數(shù)運(yùn)行結(jié)果,而是一個(gè)指向內(nèi)部狀態(tài)的指針對(duì)象央拖,必須調(diào)用遍歷器對(duì)象的next方法祭阀,使得指針移向下一個(gè)狀態(tài)。每次調(diào)用next方法鲜戒,內(nèi)部指針就從函數(shù)頭部或上一次停下來(lái)的地方開(kāi)始執(zhí)行专控,直到遇到下一個(gè)yield表達(dá)式(或return語(yǔ)句)為止。

yieId:(產(chǎn)出),分段執(zhí)行,yield表達(dá)式是暫停執(zhí)行的標(biāo)記遏餐,而next方法可以恢復(fù)執(zhí)行

Object.entries()和Object.values()

(1)Object.entries():具有鍵值對(duì)的數(shù)據(jù)結(jié)構(gòu)伦腐,則每一個(gè)鍵值對(duì)都將會(huì)編譯成一個(gè)具有兩個(gè)元素的數(shù)組,這些數(shù)組最終會(huì)放到一個(gè)數(shù)組中境输,返回一個(gè)二維數(shù)組蔗牡,若目標(biāo)對(duì)象是數(shù)組時(shí),則會(huì)將數(shù)組的下標(biāo)作為鍵值返回嗅剖。鍵值對(duì)中辩越,如果鍵的值是Symbol,編譯時(shí)將會(huì)被忽略

Object.entries({ one: 1, two: 2 })? ? //[['one', 1], ['two', 2]]

Object.entries([1, 2])? ? ? ? ? ? ? ? //[['0', 1], ['1', 2]]

如果對(duì)象的key值是數(shù)字信粮,則返回值會(huì)對(duì)key值進(jìn)行排序黔攒,返回的是排序后的結(jié)果。

Object.entries({ 3: 'a', 4: 'b', 1: 'c' })? ? //[['1', 'c'], ['3', 'a'], ['4', 'b']]

//對(duì)象屬性的遍歷

let obj = { one: 1, two: 2 };

for (let [k,v] of Object.entries(obj)) {

? console.log(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);

}

//輸出結(jié)果如下:

'one': 1

'two': 2

(2)Object.values():只返回自己的鍵值對(duì)中屬性的值

Object.values({ one: 1, two: 2 })? ? ? ? ? ? //[1, 2]

Object.values({ 3: 'a', 4: 'b', 1: 'c' })? ? //['c', 'a', 'b']

字符串填充:padStart和padEnd

padStart函數(shù):通過(guò)填充字符串的首部來(lái)保證字符串達(dá)到固定的長(zhǎng)度,默認(rèn)情況下使用空格填充

padEnd:填充字符串的尾部來(lái)保證字符串的長(zhǎng)度的督惰。

'Vue'.padStart(10)? ? ? ? ? //'? ? ? Vue'

'Vue'.padStart(10, '_*')? ? ? ? ? //'_*_*_*_Vue'

'Vue'.padEnd(10, '_*')? ? ? ? ? //'Vue_*_*_*_'

Object.getOwnPropertyDescriptors():返回目標(biāo)對(duì)象中所有屬性的屬性描述符不傅,該屬性必須是對(duì)象自己定義的,不能是從原型鏈繼承來(lái)的赏胚。

該方法返回的描述符访娶,會(huì)有兩種類(lèi)型:數(shù)據(jù)描述符、存取器描述符

返回結(jié)果中包含的鍵可能的值有:configurable觉阅、enumerable崖疤、value、writable典勇、get劫哼、set。

let obj = {

? id: 1,

? name: 'test',

? get gender() {

? ? console.log('gender')

? },

? set grade(g) {

? ? console.log(g)

? }

}

Object.getOwnPropertyDescriptors(obj, 'id')

//輸出結(jié)果為:

{

? id: {

? ? configurable: true,

? ? enumerable: true,

? ? value: 1,

? ? writable: true

? }

}

和assign區(qū)別

Object.assign(obj)

//輸出結(jié)果為:

{

? gender: undefined

? id: 1,

? name: 'test'

}

共享內(nèi)存和原子(共享陣列緩沖區(qū)割笙,Shared memory and atomics)筆記待完善

新的構(gòu)造函數(shù)SharedArrayBuffer权烧、具有輔助函數(shù)的命名空間對(duì)象Atomics

多線程并發(fā)讀寫(xiě)數(shù)據(jù)

添加尾部逗號(hào)而不報(bào)錯(cuò)

###四、前端性能優(yōu)化

1伤溉、雪碧圖技術(shù)

這個(gè)很簡(jiǎn)單般码,把每個(gè)小圖標(biāo)都整合到一張大圖上面,極大的減輕http請(qǐng)求數(shù)乱顾,同時(shí)能夠讓圖片快速加載進(jìn)來(lái)侈询。

考慮到當(dāng)前的5g的發(fā)展前景,以后圖片不會(huì)造成加載延遲的現(xiàn)象糯耍。

2、瀏覽器渲染機(jī)制

輸入一個(gè)網(wǎng)址:我們得到服務(wù)端html文件囊嘉。

根據(jù)html文件温技,從頭到尾的一個(gè)個(gè)的依次渲染頁(yè)面渲染頁(yè)面。

但是遇到圖片——不會(huì)等待圖片的加載完畢扭粱,會(huì)直接渲染下面的標(biāo)簽舵鳞。

如果圖片加載出來(lái)——根據(jù)圖片選擇,由于圖片要占用空間琢蛤,決定是否重新加載頁(yè)面蜓堕,這個(gè)概念叫reflow。(優(yōu)化的方式——給圖片寬高)博其。

reflow和什么相關(guān):占位面積套才、定位方式、邊距慕淡。

對(duì)于樣式中的顏色變化背伴,叫做repaint、這個(gè)就只需要把顏色改變。所以性能上來(lái)說(shuō)傻寂,repaint稍微比reflow高點(diǎn)息尺。

repaint和什么相關(guān):和顏色變化相關(guān)

3、webpack疾掰、gulp等打包工具的使用

壓縮代碼搂誉,減少了代碼體積。

可以把多個(gè)css文件静檬,多個(gè)js文件炭懊,合并為一個(gè)css文件/js文件。

合并文件巴柿,讓我們減少了http請(qǐng)求數(shù)凛虽。

4、避免頁(yè)面跳轉(zhuǎn)广恢,也就是使用單頁(yè)面應(yīng)用的開(kāi)發(fā)凯旋。

每次頁(yè)面跳轉(zhuǎn),就是一次html文件的下載過(guò)程钉迷。而這個(gè)過(guò)程至非,我們首先從服務(wù)端下載網(wǎng)頁(yè),再進(jìn)行渲染糠聪,網(wǎng)頁(yè)性能體驗(yàn)會(huì)很差荒椭。而單頁(yè)面應(yīng)用,它從一開(kāi)始舰蟆,就把完整的網(wǎng)頁(yè)給加載到本地趣惠。

5、延遲加載身害、懶加載技術(shù)

什么是懶加載技術(shù):

原理:先將img標(biāo)簽中的src鏈接設(shè)為同一張圖片(空白圖片)味悄,將其真正的圖片地址存儲(chǔ)再img標(biāo)簽的自定義屬性中(比如data-src)。當(dāng)js監(jiān)聽(tīng)到該圖片元素進(jìn)入可視窗口時(shí)塌鸯,即將自定義屬性中的地址存儲(chǔ)到src屬性中侍瑟,達(dá)到懶加載的效果。

這樣做能防止頁(yè)面一次性向服務(wù)器響應(yīng)大量請(qǐng)求導(dǎo)致服務(wù)器響應(yīng)慢丙猬,頁(yè)面卡頓或崩潰等問(wèn)題涨颜。

6、將css放在HEAD中

如果將 CSS放在其他地方比如 BODY中茧球,則瀏覽器有可能還未下載和解析到 CSS就已經(jīng)開(kāi)始渲染頁(yè)面了庭瑰,這就導(dǎo)致頁(yè)面由無(wú) CSS狀態(tài)跳轉(zhuǎn)到 CSS狀態(tài),用戶(hù)體驗(yàn)比較糟糕抢埋。除此之外见擦,有些瀏覽器會(huì)在 CSS下載完成后才開(kāi)始渲染頁(yè)面钉汗,如果 CSS放在靠下的位置則會(huì)導(dǎo)致瀏覽器將渲染時(shí)間推遲。

7鲤屡、Vue項(xiàng)目的按需加載

vue中的懶加載是通過(guò)webpack的代碼分割來(lái)實(shí)現(xiàn)的损痰,下面是官網(wǎng)文檔:https://router.vuejs.org/zh-cn/advanced/lazy-loading.html

主要是在加載路由的時(shí)候,使用:

const Main = r => require.ensure([], () => r(require(‘../views/main/Main’)))

require.ensure就是webpack提供的異步加載的方式酒来。

8卢未、關(guān)于React的性能優(yōu)化

react中,進(jìn)行性能優(yōu)化的核心就是shouldComponentDidMount周期函數(shù)堰汉。

9辽社、設(shè)置合理的http緩存

http請(qǐng)求中,我們可以合理的設(shè)置headers翘鸭,就能達(dá)到緩存的目的滴铅。

有兩種常見(jiàn)的緩存,強(qiáng)制緩存和對(duì)比緩存:

第一種:強(qiáng)制緩存就乓。?

直接訪問(wèn)瀏覽器中的緩存數(shù)據(jù)汉匙,如果存在,則直接使用瀏覽器中的數(shù)據(jù)生蚁。如果不存在噩翠,則再向服務(wù)器發(fā)送請(qǐng)求,然后得到服務(wù)器的數(shù)據(jù)邦投,再把這些數(shù)據(jù)存在瀏覽器中伤锚。

第二種:對(duì)比緩存。?

首先志衣,獲取瀏覽器中的數(shù)據(jù)緩存標(biāo)識(shí)屯援,再獲取服務(wù)器上面的數(shù)據(jù)緩存標(biāo)識(shí),如果相匹配念脯,則直接從瀏覽器中獲取數(shù)據(jù)玄呛,如果不匹配,則從服務(wù)器上獲取數(shù)據(jù)和二。

關(guān)于緩存標(biāo)識(shí),有兩類(lèi)標(biāo)識(shí):

第一類(lèi):

第一次請(qǐng)求耳胎,服務(wù)器會(huì)返回一個(gè)Last-Modified惯吕。

下一次請(qǐng)求,瀏覽器會(huì)自動(dòng)在headers中添加一條If-Modified-Since屬性怕午,記錄的就是上一次數(shù)據(jù)發(fā)生修改的時(shí)間废登。

第二類(lèi):

第一次請(qǐng)求,服務(wù)端返回一個(gè)Etag資源唯一標(biāo)識(shí)符郁惜。

第二次請(qǐng)求堡距,瀏覽器會(huì)自動(dòng)攜帶一個(gè)If-None_Match標(biāo)識(shí)符甲锡。

應(yīng)用程序緩存

創(chuàng)建cache manifest文件,通過(guò)給html文件中的HTML標(biāo)簽添加一個(gè)manifest屬性來(lái)實(shí)現(xiàn)的羽戒。

<!DOCTYPE HTML>

<html manifest="demo.appcache">

<body>

文檔內(nèi)容......

</body>

</html>

###五缤沦、Vue、React易稠、Angular 的異同點(diǎn)

1缸废、Angular特性:

由自己實(shí)現(xiàn)一套模板編譯規(guī)則,數(shù)據(jù)變化依賴(lài)臟檢查驶社,

基本屬性包括:數(shù)據(jù)雙向綁定企量、基本模板指令、自定義指令亡电、表單驗(yàn)證届巩、路由操作、依賴(lài)注入份乒、過(guò)濾器恕汇、內(nèi)置服務(wù)、自定義服務(wù)冒嫡、組件拇勃、模塊。

運(yùn)行效率較低孝凌,數(shù)據(jù)變更檢測(cè)方式方咆。

學(xué)習(xí)angular會(huì)迫使你學(xué)習(xí)特有的預(yù)發(fā),上手成本很大蟀架,代碼看起來(lái)很干凈

依賴(lài)注入瓣赂,即一個(gè)對(duì)象將依賴(lài)項(xiàng)提供給另一個(gè)對(duì)象(客戶(hù)端)的模式。導(dǎo)致更多的靈活性和更干凈的代碼片拍。

Angular 最適合單頁(yè)應(yīng)用(SPA)煌集,因?yàn)樗赡芴纺[而不能用于微服務(wù)。

框架比較臃腫捌省,每次用啥功能要引入一大堆東西

Angular錯(cuò)誤提示不夠清晰明顯苫纤,對(duì)于初級(jí)開(kāi)發(fā)者,很難看懂Angular的錯(cuò)誤提示纲缓。(個(gè)人認(rèn)為這是最大的不好之處卷拘,當(dāng)初學(xué)習(xí)這個(gè)遇到很多坑啊)祝高,而且定位bug很難栗弟。

面向?qū)ο缶幊痰乃枷耄珹ngular由后端開(kāi)發(fā)人員設(shè)計(jì)的前端框架工闺。

詳細(xì)比較:React和Vue的區(qū)別

2乍赫、React特性:

單向綁定瓣蛀,先更新model,然后渲染UI元素,數(shù)據(jù)在一個(gè)方向流動(dòng)雷厂,使得調(diào)試更加容易惋增。代碼冗余,各種生命周期太麻煩罗侯,剛開(kāi)始接觸好難記器腋。

用了虛擬DOM。(對(duì)虛擬DOM的理解剛開(kāi)始我不是很理解概念钩杰,建議大家去看【深入REACT技術(shù)椚宜】這本書(shū)有很好的講解)

更適合大型應(yīng)用和更好的可測(cè)試性

Web端和移動(dòng)端原生APP通吃

更大的生態(tài)系統(tǒng),更多的支持和好用的工具

3讲弄、Vue特性

模板和渲染函數(shù)的彈性選擇

簡(jiǎn)單的語(yǔ)法和項(xiàng)目配置

更快的渲染速度和更小的體積四

4措左、Vue和React共同點(diǎn)

用虛擬DOM實(shí)現(xiàn)快速渲染

輕量級(jí)

響應(yīng)式組件

服務(wù)端渲染

集成路由工具,打包工具避除,狀態(tài)管理工具的難度低

5:不同點(diǎn)

vue? 控制器:無(wú)怎披;過(guò)濾器 :無(wú) ;指令:有瓶摆;渲染指令: 有 凉逛;數(shù)據(jù)綁定:雙向;

React? 控制器:無(wú)群井;過(guò)濾器 :無(wú) 状飞;指令:無(wú);渲染指令 : 無(wú) 书斜;數(shù)據(jù)綁定:?jiǎn)蜗颍?/p>

angular 控制器:有诬辈;過(guò)濾器 :有 ;指令:有荐吉;渲染指令 : 有 焙糟;數(shù)據(jù)綁定:雙向;

###六样屠、Webpack 如何對(duì)項(xiàng)目構(gòu)建優(yōu)化

請(qǐng)移駕我的另一篇 Chat 文章https://gitbook.cn/gitchat/activity/5bed34555748cb6bd2780d4b

###七穿撮、首屏優(yōu)化

1. DNS預(yù)解析

2.域名收斂

既然DNS解析比較耗時(shí),每個(gè)連接都要建立鏈路痪欲,那么我們就可以通過(guò)減少ajax到后臺(tái)的域名地址悦穿,通過(guò)反向代理去做。

3. 鏈路復(fù)用

因?yàn)槊恳淮捂溄訑?shù)都要建立3次TCP握手勤揩,通過(guò)keep-alive,可以保證建立的連接不會(huì)被關(guān)閉秘蛔, 下次請(qǐng)求可以直接發(fā)送數(shù)據(jù)陨亡,這樣可以減少將近200ms的時(shí)間傍衡,當(dāng)然會(huì)增加一部分Server的內(nèi)存消耗,要預(yù)先擴(kuò)容负蠕;http2.0已經(jīng)支持了這些特性蛙埂;

4. 資源內(nèi)聯(lián)

在首屏里,一些資源都是分散開(kāi)發(fā)的遮糖,在一些簡(jiǎn)單的頁(yè)面中绣的,有些內(nèi)容可以?xún)?nèi)聯(lián)進(jìn)去,不然像一些css頁(yè)面要等到html頁(yè)面解析完成后欲账,再去解析外聯(lián)的css屡江; 可以通過(guò)打包工具在發(fā)布的時(shí)候就能完成;

5.組件化開(kāi)發(fā)

首先一點(diǎn)是按需加載赛不,就是首屏顯示的東西惩嘉,哪些要加載,哪些無(wú)需加載踢故,可以區(qū)分開(kāi)來(lái)文黎,而且可以異步方式來(lái)加載。

我們通常是這么做的殿较,我們可以先加載一些公用的東西耸峭,然后通過(guò)路由,去加載首屏上可以看到選項(xiàng)卡的資源淋纲,哪些有用戶(hù)交互之后的東西劳闹,可以稍后在加載;

但是這樣會(huì)有一個(gè)問(wèn)題帚戳,有時(shí)候我們的js處理的比較快玷或,但是css文件處理的比較快,這樣就會(huì)使的頁(yè)面比較混亂片任,所以可以通過(guò)異步打包工具偏友,將css和js打包在一起

6. 服務(wù)端渲染

一個(gè)前后端分離的項(xiàng)目,通過(guò)ajax請(qǐng)求數(shù)據(jù)对供,這樣一個(gè)頁(yè)面的渲染路徑會(huì)拉的很長(zhǎng)位他,頁(yè)面先渲染html,然后在渲染js产场,js才發(fā)送ajax請(qǐng)求鹅髓,等到數(shù)據(jù)回來(lái)再進(jìn)行渲染,所以不管怎么樣都至少要3個(gè)RTT請(qǐng)求時(shí)間京景;這種模式窿冯,在網(wǎng)絡(luò)環(huán)境比較差的情況下,是很難接受的确徙,

所以我們又回歸到PHP時(shí)代醒串;通過(guò)服務(wù)器端渲染执桌,就可以控制用戶(hù),哪些東西是要打包的芜赌⊙稣酰可以一次性知道用戶(hù)需要哪些數(shù)據(jù);加上現(xiàn)在的NodeJs環(huán)境以及React缠沈,這就使得一些東西既可以在前端的瀏覽器進(jìn)行渲染膘壶,也可以在服務(wù)器中進(jìn)行字符串拼接,一起吐出來(lái)洲愤;

比如在無(wú)限長(zhǎng)的滾動(dòng)列表中颓芭,對(duì)圖片進(jìn)行懶加載,首先不給圖片賦值src屬性禽篱,通過(guò)插入dom計(jì)算可視區(qū)畜伐,在可視區(qū)內(nèi)則賦值src,在可視區(qū)之外躺率,交給scroll去處理玛界,將要進(jìn)入可視區(qū)時(shí)才賦值;

7.利用緩存

資源長(zhǎng)緩存:不是通過(guò)304去判斷緩存悼吱,而是通過(guò)max-age來(lái)做的慎框,通過(guò)md5命名文件,然后通過(guò)自動(dòng)化構(gòu)建工具去實(shí)現(xiàn)后添;主要聊一下數(shù)據(jù)層緩存與數(shù)據(jù)復(fù)用笨枯;

從客戶(hù)端的啟發(fā);客戶(hù)端會(huì)將上次緩存的數(shù)據(jù)重新渲染一遍遇西;所以數(shù)據(jù)緩存的設(shè)計(jì)可以設(shè)計(jì)為

可以在ajax請(qǐng)求的數(shù)據(jù)沒(méi)有回來(lái)之前馅精,用一些配置的假數(shù)據(jù),先把坑站好粱檀,然后等待線上的數(shù)據(jù)回來(lái)后洲敢,在把坑填好,這樣減少了白屏的時(shí)間茄蚯。這樣給用戶(hù)感覺(jué)比較快压彭,但不是真的快,交互性能確實(shí)變好了渗常;

在有些時(shí)候壮不,并不是所有的數(shù)據(jù)都是加載進(jìn)來(lái)的,像貼吧和知乎之類(lèi)的皱碘,展示的一些storage里的數(shù)據(jù)询一,都是帶有省略號(hào)的緩存數(shù)據(jù),等用戶(hù)真的點(diǎn)開(kāi)之后才去加載該帖子的全部數(shù)據(jù)。每次請(qǐng)求的數(shù)據(jù)又重新將原來(lái)的緩存覆蓋健蕊;

8.離線包

其實(shí)對(duì)于一些并發(fā)量比較大缓醋,向過(guò)年時(shí)候的搶紅包業(yè)務(wù),并發(fā)量是幾個(gè)億绊诲。這樣,對(duì)于一些經(jīng)常使用的用戶(hù)褪贵,可以幾天前就將離線包推送過(guò)去掂之,進(jìn)行下載,通過(guò)二進(jìn)制比較脆丁,進(jìn)行動(dòng)態(tài)增量更新世舰,而不用每次都下載,這么大的并發(fā)量槽卫,同時(shí)下載跟压,帶寬肯定爆,服務(wù)器肯定爆歼培。

9.減少請(qǐng)求次數(shù)

10.減少對(duì)dom的操作

###七震蒋、移動(dòng)端如何做適配

現(xiàn)在最常用的就是一稿設(shè)計(jì)多端適配方案—— rem + flexible

rem是什么?

rem(font size of the root element)是指相對(duì)于根元素的字體大小的單位躲庄。簡(jiǎn)單的說(shuō)它就是一個(gè)相對(duì)單位查剖。看到rem大家一定會(huì)想起em單位噪窘,em(font size of the element)是指相對(duì)于父元素的字體大小的單位笋庄。它們之間其實(shí)很相似,只不過(guò)一個(gè)計(jì)算的規(guī)則是依賴(lài)根元素一個(gè)是依賴(lài)父元素計(jì)算倔监。

REM自適應(yīng)JS

//designWidth:設(shè)計(jì)稿的實(shí)際寬度值直砂,需要根據(jù)實(shí)際設(shè)置

//maxWidth:制作稿的最大寬度值,需要根據(jù)實(shí)際設(shè)置

//這段js的最后面有兩個(gè)參數(shù)記得要設(shè)置浩习,一個(gè)為設(shè)計(jì)稿實(shí)際寬度静暂,一個(gè)為制作稿最大寬度,例如設(shè)計(jì)稿為750瘦锹,最大寬度為750籍嘹,則為(750,750)

;(function(designWidth, maxWidth) {

var doc = document,

win = window,

docEl = doc.documentElement,

remStyle = document.createElement("style"),

tid;

function refreshRem() {

var width = docEl.getBoundingClientRect().width;

maxWidth = maxWidth || 540;

width>maxWidth && (width=maxWidth);

var rem = width * 100 / designWidth;

remStyle.innerHTML = 'html{font-size:' + rem + 'px;}';

}

if (docEl.firstElementChild) {

docEl.firstElementChild.appendChild(remStyle);

} else {

var wrap = doc.createElement("div");

wrap.appendChild(remStyle);

doc.write(wrap.innerHTML);

wrap = null;

}

//要等 wiewport 設(shè)置好后才能執(zhí)行 refreshRem,不然 refreshRem 會(huì)執(zhí)行2次弯院;

refreshRem();

win.addEventListener("resize", function() {

clearTimeout(tid); //防止執(zhí)行兩次

tid = setTimeout(refreshRem, 300);

}, false);

win.addEventListener("pageshow", function(e) {

if (e.persisted) { // 瀏覽器后退的時(shí)候重新計(jì)算

clearTimeout(tid);

tid = setTimeout(refreshRem, 300);

}

}, false);

if (doc.readyState === "complete") {

doc.body.style.fontSize = "16px";

} else {

doc.addEventListener("DOMContentLoaded", function(e) {

doc.body.style.fontSize = "16px";

}, false);

}

})(750, 750);

第一個(gè)參數(shù)是設(shè)計(jì)稿的寬度辱士,一般設(shè)計(jì)稿有640,或者是750听绳,你可以根據(jù)實(shí)際調(diào)整

第二個(gè)參數(shù)則是設(shè)置制作稿的最大寬度颂碘,超過(guò)750,則以750為最大限制攒驰。

在小王待過(guò)的公司里面油讯,設(shè)計(jì)稿都是按750來(lái)設(shè)計(jì)的。不過(guò)管它用什么尺寸來(lái)設(shè)計(jì)涛目,適配理念都一樣峡竣,以不變應(yīng)萬(wàn)變靠抑。

使用1rem=100px轉(zhuǎn)換你的設(shè)計(jì)稿的像素,例如設(shè)計(jì)稿上某個(gè)塊是100px*300px,換算成rem則為1rem*3rem适掰。

這樣子不管什么類(lèi)型的手機(jī)颂碧,就都可以是適配了。像那種用多媒體查詢(xún)針對(duì)不同設(shè)備尺寸寫(xiě)樣式的方式类浪,就讓見(jiàn)鬼去吧载城。

###八、跨終端如何做適配

先來(lái)個(gè)圖壓壓驚费就,圖有點(diǎn)老诉瓦,現(xiàn)在都iPhonx了。

如果要適配這么多設(shè)備是不是很頭疼Aο浮2窃琛!

###九眠蚂、MVVM 框架的知識(shí)點(diǎn)猴贰。

####Vue 部分

Vue組件之間如何通信

一. 父子之間的通信

1. 父組件-》子組件(props down)

①通過(guò)屬性

步驟1:父組件在調(diào)用子組件時(shí)傳值

<son myName="michael" myPhone='123'></son> <son :myName="userList[0]"></son>

步驟2:子組件通過(guò)props得到父組件的傳過(guò)來(lái)的數(shù)據(jù)

Vue.component('son',{ props:['myName','myPhone'] })

②通過(guò)$parent

直接在子組件中通過(guò)this.$parent得到調(diào)用子組件的父組件

2、子組件-》父組件(events up)

①events up

步驟1:在父組件中 調(diào)用子組件的時(shí)候 綁定一個(gè)自定義事件 和 對(duì)應(yīng)的處理函數(shù)

methods:{ recvMsg:function(msg){ //msg就是傳遞來(lái)的數(shù)據(jù) } }河狐, template:' <son @customEvent="recvMsg"></son> '

步驟2:在子組件中 把要發(fā)送的數(shù)據(jù)通過(guò)觸發(fā)自定義事件傳遞給父組件

this.$emit('customEvent',123)

② $refs

步驟1:在調(diào)用子組件的時(shí)候 可以指定ref屬性

`<son ref='zhangsan'></son>`

步驟2:通過(guò)$refs

得到指定引用名稱(chēng)對(duì)應(yīng)的組件實(shí)例

this.$refs.zhangsan

二米绕、兄弟組件間的通信

步驟1:創(chuàng)建一個(gè)Vue的實(shí)例 作為事件綁定觸發(fā)的公共的對(duì)象

var bus = new Vue();

步驟2:在接收方的組件 綁定 自定義的事件

bus.$on('customEvent',function(msg){ console.log(msg); //msg是通過(guò)事件傳遞來(lái)的數(shù)據(jù) (傳遞來(lái)的123) });

步驟3:在發(fā)送方的組件 觸發(fā) 自定義的事件

bus.$emit('customEvent',123);

除了以上幾種方式外,還有 Vuex馋艺、路由傳參和緩存栅干。

二、Vue的雙向數(shù)據(jù)綁定原理是什么捐祠?

vue.js 是采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式碱鳞,通過(guò)Object.defineProperty()來(lái)劫持各個(gè)屬性的setter,getter踱蛀,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者窿给,觸發(fā)相應(yīng)的監(jiān)聽(tīng)回調(diào)。

第一步:需要observe的數(shù)據(jù)對(duì)象進(jìn)行遞歸遍歷率拒,包括子屬性對(duì)象的屬性崩泡,都加上 setter和getter

這樣的話,給這個(gè)對(duì)象的某個(gè)值賦值猬膨,就會(huì)觸發(fā)setter角撞,那么就能監(jiān)聽(tīng)到了數(shù)據(jù)變化

第二步:compile解析模板指令,將模板中的變量替換成數(shù)據(jù),然后初始化渲染頁(yè)面視圖谒所,并將每個(gè)指令對(duì)應(yīng)的節(jié)點(diǎn)綁定更新函數(shù)热康,添加監(jiān)聽(tīng)數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)有變動(dòng)劣领,收到通知姐军,更新視圖

第三步:Watcher訂閱者是Observer和Compile之間通信的橋梁,主要做的事情是:

1尖淘、在自身實(shí)例化時(shí)往屬性訂閱器(dep)里面添加自己

2庶弃、自身必須有一個(gè)update()方法

3、待屬性變動(dòng)dep.notice()通知時(shí)德澈,能調(diào)用自身的update()方法,并觸發(fā)Compile中綁定的回調(diào)固惯,則功成身退梆造。

第四步:MVVM作為數(shù)據(jù)綁定的入口,整合Observer葬毫、Compile和Watcher三者镇辉,通過(guò)Observer來(lái)監(jiān)聽(tīng)自己的model數(shù)據(jù)變化,通過(guò)Compile來(lái)解析編譯模板指令贴捡,最終利用Watcher搭起Observer和Compile之間的通信橋梁忽肛,達(dá)到數(shù)據(jù)變化 -> 視圖更新;視圖交互變化(input) -> 數(shù)據(jù)model變更的雙向綁定效果烂斋。

三屹逛、詳細(xì)說(shuō)下你對(duì)vue生命周期的理解?

總共分為8個(gè)階段創(chuàng)建前/后汛骂,載入前/后罕模,更新前/后,銷(xiāo)毀前/后

創(chuàng)建前/后: 在beforeCreated階段帘瞭,vue實(shí)例的掛載元素$el和數(shù)據(jù)對(duì)象data都為undefined淑掌,還未初始化。在created階段蝶念,vue實(shí)例的數(shù)據(jù)對(duì)象data有了抛腕,$el還沒(méi)有。

載入前/后:在beforeMount階段媒殉,vue實(shí)例的$el和data都初始化了担敌,但還是掛載之前為虛擬的dom節(jié)點(diǎn),data.message還未替換廷蓉。在mounted階段柄错,vue實(shí)例掛載完成,data.message成功渲染。

更新前/后:當(dāng)data變化時(shí)售貌,會(huì)觸發(fā)beforeUpdate和updated方法给猾。

銷(xiāo)毀前/后:在執(zhí)行destroy方法后,對(duì)data的改變不會(huì)再觸發(fā)周期函數(shù)颂跨,說(shuō)明此時(shí)vue實(shí)例已經(jīng)解除了事件監(jiān)聽(tīng)以及和dom的綁定敢伸,但是dom結(jié)構(gòu)依然存在

四、你是怎么理解vuex的恒削?

vuex可以理解為一種開(kāi)發(fā)模式或框架池颈。

通過(guò)狀態(tài)(數(shù)據(jù)源)集中管理驅(qū)動(dòng)組件的變化(好比spring的IOC容器對(duì)bean進(jìn)行集中管理)。

應(yīng)用級(jí)的狀態(tài)集中放在store中钓丰; 改變狀態(tài)的方式是提交mutations躯砰,這是個(gè)同步的事物; 異步邏輯應(yīng)該封裝在action中携丁。

五琢歇、聊聊你對(duì)Vue.js的template編譯的理解?

簡(jiǎn)而言之梦鉴,就是先轉(zhuǎn)化成AST樹(shù)李茫,再得到的render函數(shù)返回VNode(Vue的虛擬DOM節(jié)點(diǎn))

首先,通過(guò)compile編譯器把template編譯成AST語(yǔ)法樹(shù)(abstract syntax tree 即 源代碼的抽象語(yǔ)法結(jié)構(gòu)的樹(shù)狀表現(xiàn)形式)肥橙,compile是createCompiler的返回值魄宏,createCompiler是用以創(chuàng)建編譯器的。另外compile還負(fù)責(zé)合并option存筏。

然后宠互,AST會(huì)經(jīng)過(guò)generate(將AST語(yǔ)法樹(shù)轉(zhuǎn)化成render funtion字符串的過(guò)程)得到render函數(shù),render的返回值是VNode椭坚,VNode是Vue的虛擬DOM節(jié)點(diǎn)名秀,里面有(標(biāo)簽名、子節(jié)點(diǎn)藕溅、文本等等)

####React 部分

一匕得、react生命周期及相關(guān)用法

react生命周期分為初始化階段、運(yùn)行階段巾表、銷(xiāo)毀階段汁掠。

(1) 初始化階段:

getDefaultProps:獲取實(shí)例的默認(rèn)屬性

getInitialState:獲取每個(gè)實(shí)例的初始化狀態(tài)

componentWillMount:實(shí)例掛載之前

Render:渲染組件

componentDidMount:實(shí)例掛載完成。一般在這個(gè)函數(shù)中與后臺(tái)進(jìn)行初始化數(shù)據(jù)交互集币。

(2)運(yùn)行階段:

componentWillReceiveProps:父組件改變時(shí)調(diào)用考阱。

sholudComponentUpdate:主要是用來(lái)手動(dòng)阻止組件渲染,一般在這個(gè)函數(shù)中做組件的性能優(yōu)化鞠苟。

componentWillUpdate:組件數(shù)據(jù)更新前調(diào)用

componentDidUpdate:組件數(shù)據(jù)更新完成時(shí)調(diào)用

(3)銷(xiāo)毀階段:

componentUnmount:銷(xiāo)毀階段乞榨。一般用來(lái)銷(xiāo)毀不用的變量或者是解除無(wú)用定時(shí)器以及解綁無(wú)用事件秽之。防止內(nèi)存泄漏問(wèn)題。

二吃既、你了解 Virtual DOM 嗎考榨?解釋一下它的工作原理。

Virtual DOM 是一個(gè)輕量級(jí)的 JavaScript 對(duì)象鹦倚,它最初只是 real DOM 的副本河质。它是一個(gè)節(jié)點(diǎn)樹(shù),它將元素震叙、它們的屬性和內(nèi)容作為對(duì)象及其屬性掀鹅。 React 的渲染函數(shù)從 React 組件中創(chuàng)建一個(gè)節(jié)點(diǎn)樹(shù)。然后它響應(yīng)數(shù)據(jù)模型中的變化來(lái)更新該樹(shù)媒楼,該變化是由用戶(hù)或系統(tǒng)完成的各種動(dòng)作引起的乐尊。

Virtual DOM 工作過(guò)程有三個(gè)簡(jiǎn)單的步驟。

1.每當(dāng)?shù)讓訑?shù)據(jù)發(fā)生改變時(shí)划址,整個(gè) UI 都將在 Virtual DOM 描述中重新渲染扔嵌。

2.然后計(jì)算之前 DOM 表示與新表示的之間的差異。

3.完成計(jì)算后猴鲫,將只用實(shí)際更改的內(nèi)容更新 real DOM。

三谣殊、總結(jié)一下Redux的三大原則和數(shù)據(jù)流的管理

Redux三大原則:

1拂共、單一數(shù)據(jù)源,這個(gè)應(yīng)用的state被存儲(chǔ)在一棵object tree中姻几,并且這個(gè)object tree只存在于唯一的Store中宜狐。

2、state是只讀的蛇捌,唯一改變state的方法就是觸發(fā)action抚恒,action是一個(gè)用于描述已發(fā)生事件的普通對(duì)象。

3络拌、使用純函數(shù)來(lái)執(zhí)行修改俭驮,為了描述action如何改變state tree,需要編寫(xiě)reducer春贸。

4混萝、具體工作步驟如下:

Redux數(shù)據(jù)流的管理:

1、action:把數(shù)據(jù)傳遞到Store萍恕,唯一數(shù)據(jù)來(lái)源逸嘀。

2、reducer:action只描述有事情發(fā)生允粤,reducer指明如何更新state崭倘,即設(shè)計(jì)state結(jié)構(gòu)和action處理翼岁。

3、Store:把a(bǔ)ction和reducer聯(lián)系到一起司光,負(fù)責(zé)維持琅坡、獲取和更新state。

4飘庄、生命周期:數(shù)據(jù)流嚴(yán)格且單向

調(diào)用Store.dispatch(action)->Store調(diào)用傳入的reducer函數(shù)脑蠕,Store會(huì)把兩個(gè)參數(shù)傳入reducer:當(dāng)前的state樹(shù)和action->根reducer將多個(gè)子reducer輸出合并成一個(gè)單一的state樹(shù)->Store保存了根reducer,并返回完整的state樹(shù)跪削。

四谴仙、Redux與它的中間件

redux是一個(gè)可預(yù)測(cè)的狀態(tài)容器,

react-redux是將store和react結(jié)合起來(lái)碾盐,使得數(shù)據(jù)展示和修改對(duì)于react項(xiàng)目而言更簡(jiǎn)單

redux中間件就是在dispatch action前對(duì)action做一些處理

redux-thunk用于對(duì)異步做操作

redux-actions用于簡(jiǎn)化redux操作

redux-promise可以配合redux-actions用來(lái)處理Promise對(duì)象晃跺,使得異步操作更簡(jiǎn)單

redux-sage可以起到一個(gè)控制器的作用,集中處理邊際效用毫玖,并使得異步操作的寫(xiě)法更優(yōu)雅掀虎。

//這里還需要大家自己再去多了解一下redux中間件的知識(shí)。

###十付枫、實(shí)現(xiàn)一個(gè)觀察者模式

舉個(gè)生活比較常見(jiàn)常見(jiàn)的例子,比如你去面試之后烹玉,面試官看你表現(xiàn)不錯(cuò),最后會(huì)跟你要聯(lián)系方式阐滩,以便之后可以聯(lián)系你二打。在這角色扮演當(dāng)中,你就是“訂閱者”掂榔,面試官就是“發(fā)布者”继效。

那么發(fā)布訂閱模式是咋實(shí)現(xiàn)的呢?

思路:

給定一個(gè)發(fā)布者

面試者將聯(lián)系方式給發(fā)布者

發(fā)布者的一個(gè)列表有各種職位(web端的装获,java 的)瑞信,里面記載回調(diào)函數(shù)以便通知這些面試者

最后發(fā)布消息的時(shí)候,會(huì)遍歷這個(gè)列表的職位的回調(diào)函數(shù)穴豫,告訴面試者面試這個(gè)職位是通過(guò)還是不通過(guò)

如果面試者取消了訂閱凡简,那么將回調(diào)函數(shù)和之前的回調(diào)函數(shù)作對(duì)比,如果相等精肃,就將這個(gè)面試者的上班通知去掉

var Event = (function() {

? var events = {}; //發(fā)布者

? //subscribe也就是訂閱潘鲫,post 代表面試者要面的職位,callback表示為回調(diào)函數(shù)

? function subscribe(post, callback) {

? ? events[post] = events[post] || []; //發(fā)布者的列表里有沒(méi)有這個(gè)面試職位肋杖,如果沒(méi)有就創(chuàng)建一個(gè)空數(shù)組

? ? events[post].push(callback);

? }

? //publish 表示發(fā)布

? function publish() {

? ? var post = Array.prototype.shift.call(arguments); //第一個(gè)參數(shù)指定“鍵”

? ? var fns = events[post]; //設(shè)置緩存溉仑,提高性能

? ? if (!fns) { //如果發(fā)布者的列表里沒(méi)有這個(gè)職位,那肯定是不能發(fā)布

? ? ? return;

? ? }

? ? for (var i = 0; i < fns.length; i++) { //遍歷當(dāng)前的職位的數(shù)組里有幾個(gè)面試者

? ? ? fns[i].apply(this, arguments);

? ? }

? }

? //unsubscribe 表示取消訂閱

? function unsubscribe(post, fn) {

? ? var fns = events[post];

? ? if (fns) {

? ? ? if (fn) {

? ? ? ? for (var i = fns.length; i >= 0; i--) {

? ? ? ? ? if (fns[i] === fn) fns.splice(i, 1);

? ? ? ? }

? ? ? } else {//如果沒(méi)有傳入fn回調(diào)函數(shù)状植,直接取消post對(duì)應(yīng)消息的所有訂閱

? ? ? ? fns = [];

? ? ? }

? ? } else {//如果發(fā)布者的列表沒(méi)有這個(gè)職位浊竟,直接 return

? ? ? return;

? ? }

? }


? return {

? ? subscribe: subscribe,

? ? publish: publish,

? ? unsubscribe: unsubscribe

? };

})();

測(cè)試:

var fn1 = function(time) {

? console.log("小明你通過(guò)了面試怨喘,上班時(shí)間:" + time);

};

var fn2 = function(time) {

? console.log("小強(qiáng)你通過(guò)了面試,上班時(shí)間:" + time);

};

//小明將聯(lián)系方式給了發(fā)布者振定,發(fā)布者(hr)覺(jué)得小明不錯(cuò)必怜,可以通過(guò),于是在列表(java)里寫(xiě)下了一些回調(diào)函數(shù)后频,到時(shí)候發(fā)布的時(shí)候?qū)⑸习鄷r(shí)間告訴小明

Event.subscribe("java", fn1);

//小強(qiáng)也訂閱了

Event.subscribe("java", fn2);

Event.publish("java", "2017-10-01");

/*輸出:

小明你通過(guò)了面試梳庆,上班時(shí)間:2017-10-01

小強(qiáng)你通過(guò)了面試,上班時(shí)間:2017-10-01

*/

Event.unsubscribe("java", fn1);//刪除小明的上班通知

Event.publish("java", "2017-10-01");

/*輸出:

小強(qiáng)你通過(guò)了面試卑惜,上班時(shí)間:2017-10-01

*/

###十一膏执、實(shí)現(xiàn)一個(gè)數(shù)組去重的函數(shù)

//其實(shí)數(shù)組去重的方法有好多種,這就介紹常用的幾種就夠了

第一種:

let arr = [1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0, 2, 2, 3];

function unique(arr){

? ? return [...(new Set(arr))];

}

console.log(unique(arr)); // [1, "a", "b", "d", "e", 0, 2, 3]

第二種:

let arr = [1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0, 2, 2, 3];

function unique(arr){

? ? return Array.from(new Set(arr));

}

console.log(unique(arr)); // [1, "a", "b", "d", "e", 0, 2, 3]

第三種:

let arr = [1, 'a', 'a', 'b', 'd', 'e', 'e', 1, 0, 2, 2, 3];

function unique(arr){

? ? return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);

}

console.log(unique(arr)); // [1, "a", "b", "d", "e", 0, 2, 3]

第四種:最普通的一種露久,想了解更多更米,自己多多研究吧。

let arr = [1, 'a', 'a', 'b', 'd', 'e', 'e', '1', 0, 2, 2, 3];

function unique(arr){

? ? let newArr = [];

? ? let obj = {};

? ? for (let i = 0; i < arr.length; i++) {

? ? ? ? if (!obj[typeof arr[i] + arr[i]]) {

? ? ? ? ? ? obj[typeof arr[i] + arr[i]] = 1;

? ? ? ? ? ? newArr.push(arr[i]);

? ? ? ? }

? ? }

? ? return newArr;

}

console.log(unique(arr)); // [1, "a", "b", "d", "e", "1", 0, 2, 3]

###十二毫痕、介紹Promise的原理

在Promise的內(nèi)部征峦,有一個(gè)狀態(tài)管理器的存在,有三種狀態(tài):pending消请、fulfilled栏笆、rejected。

  (1) promise 對(duì)象初始化狀態(tài)為 pending臊泰。

  (2) 當(dāng)調(diào)用resolve(成功)蛉加,會(huì)由pending => fulfilled。

  (3) 當(dāng)調(diào)用reject(失敗)因宇,會(huì)由pending => rejected七婴。

  因此祟偷,看上面的的代碼中的resolve(num)其實(shí)是將promise的狀態(tài)由pending改為fulfilled察滑,然后向then的成功回掉函數(shù)傳值,reject反之修肠。但是需要記住的是注意promsie狀態(tài) 只能由 pending => fulfilled/rejected, 一旦修改就不能再變(記住贺辰,一定要記住,下面會(huì)考到)嵌施。

  當(dāng)狀態(tài)為fulfilled(rejected反之)時(shí)饲化,then的成功回調(diào)函數(shù)會(huì)被調(diào)用,并接受上面?zhèn)鱽?lái)的num吗伤,進(jìn)而進(jìn)行操作吃靠。promise.then方法每次調(diào)用,都返回一個(gè)新的promise對(duì)象 所以可以鏈?zhǔn)綄?xiě)法(無(wú)論resolve還是reject都是這樣)足淆。

Promise也是面試必問(wèn)的一個(gè)知識(shí)點(diǎn)巢块,多多學(xué)習(xí)礁阁。

###十三、JavaScript對(duì)象的淺拷貝與深拷貝實(shí)例分析

1族奢、淺拷貝

僅僅復(fù)制對(duì)象的引用姥闭,而不是對(duì)象本身

var person = {

? name: 'Alice',

? friends: ['Bruce', 'Cindy']

}

var student = {

? id: 30

}

student = simpleClone(person, student);

student.friends.push('David');

function simpleClone(oldObj, newObj) {

? var newObj = newObj || {};

? for (var i in oldObj)

? ? newObj[i] = oldObj[i];

? return newObj;

}

console.log(person.friends);//["Bruce", "Cindy", "David"]

2、深拷貝

把復(fù)制的對(duì)象所引用的全部對(duì)象都復(fù)制一遍越走,能夠?qū)崿F(xiàn)真正意義上的數(shù)組和對(duì)象的拷貝棚品。

淺拷貝的問(wèn)題:如果父對(duì)象的屬性值為一個(gè)數(shù)組或另一個(gè)對(duì)象,那么實(shí)際上子對(duì)象獲得的只是一個(gè)內(nèi)存地址廊敌,而不是對(duì)父對(duì)象的真正拷貝铜跑,因此存在父對(duì)象被篡改的可能。

方法1:

var person = {

? name: 'Alice',

? friends: ['Bruce', 'Cindy']

}

var student = {

? id: 30

}

student = deepClone(person, student);

student.friends.push('David');

function deepClone(oldObj, newObj) {

? var newObj = newObj || {};

? newObj = JSON.parse(JSON.stringify(oldObj));

? return newObj;

}

console.log(person.friends); // 'Bruce', 'Cindy'

方法2:

function deepClone(oldObj, newObj) {

? var newObj = newObj || {};

? for (var i in oldObj) {

? ? var prop = oldObj[i];

? ? if (prop === newObj)

? ? ? ? ? continue;

? ? if (typeof prop === 'object')

? ? ? newObj[i] = (prop.constructor === Array) ? [] : Object.create(prop);

? ? else

? ? ? newObj[i] = prop;

? }

? return newObj;

}

###十四庭敦、徹底弄清 Callback疼进、Promise、Generator以及Async/await

一秧廉、回調(diào)函數(shù)

所謂回調(diào)函數(shù)(callback)伞广,就是把任務(wù)分成兩步完成,第二步單獨(dú)寫(xiě)在一個(gè)函數(shù)里面疼电,等到重新執(zhí)行這個(gè)任務(wù)時(shí)嚼锄,就直接調(diào)用這個(gè)函數(shù)。

例如Node.js中讀取文件

fs.readFile('a,txt', (err,data) = >{

? if(err) throw err;

? console.log(data);

})

上面代碼中readFile的第二個(gè)參數(shù)就是回調(diào)函數(shù)蔽豺,等到讀取完a.txt文件時(shí)区丑,這個(gè)函數(shù)才會(huì)執(zhí)行。

二修陡、Promise

使用回調(diào)函數(shù)本身沒(méi)有問(wèn)題沧侥,但有“回調(diào)地獄”的問(wèn)題。

假定我們有一個(gè)需求魄鸦,讀取完A文件之后讀取B文件宴杀,再讀取C文件,代碼如下

fs.readFile(fileA,? (err, data) => {

? fs.readFile(fileB,? (err, data) => {

? ? ? fs.readFile(fileC, (err,data)=>{

? ? ? ? //do something

? ? })

? });

});

可見(jiàn)拾因,三個(gè)回調(diào)函數(shù)代碼看來(lái)就夠嗆了旺罢,有時(shí)在實(shí)際業(yè)務(wù)中還不止嵌套這幾個(gè),難以管理绢记。

這時(shí)候Promise出現(xiàn)了扁达!它不是新的功能,而是一種新的寫(xiě)法蠢熄,用來(lái)解決“回調(diào)地獄”的問(wèn)題跪解。

我們?cè)偌俣ㄒ粋€(gè)業(yè)務(wù),分多個(gè)步驟完成签孔,每個(gè)步驟都是異步的叉讥,而且依賴(lài)于上一個(gè)步驟的結(jié)果砾跃,用setTimeout()來(lái)模擬異步操作

/**

* 傳入?yún)?shù) n,表示這個(gè)函數(shù)執(zhí)行的時(shí)間(毫秒)

* 執(zhí)行的結(jié)果是 n + 200节吮,這個(gè)值將用于下一步驟

*/

function A(n) {

? ? return new Promise(resolve => {

? ? ? ? setTimeout(() => resolve(n + 200), n);

? ? });

}

function step1(n) {

? ? console.log(`step1 with ${n}`);

? ? return A(n);

}

function step2(n) {

? ? console.log(`step2 with ${n}`);

? ? ? return A(n);

}

function step3(n) {

? ? console.log(`step3 with ${n}`);

? ? return A(n);

}

上面代碼中有4個(gè)函數(shù)抽高,A()返回一個(gè)Promise對(duì)象,接收參數(shù)n透绩,n秒后執(zhí)行resolve(n+200)翘骂。step1、 step2帚豪、step3對(duì)應(yīng)三個(gè)步驟

現(xiàn)在用Promise實(shí)現(xiàn)這三個(gè)步驟:

function doIt() {

? ? console.time('do it now')

? ? const time1 = 300;

? ? step1(time1)

? ? ? ? ? .then( time2 =>step2(time2))

? ? ? ? ? .then( time3 => step3(time3))

? ? ? ? ? .then( result => {

? ? ? ? ? ? ? console.log(`result is ${result}`)

? ? ? ? ? });

}

doIt();

輸出結(jié)果如下

step1 with 300

step2 with 500

step3 with 700

result is 900

result是step3()的參數(shù)700+200 = 900碳竟。

可見(jiàn),Promise的寫(xiě)法只是回調(diào)函數(shù)的改進(jìn)狸臣,用then()方法免去了嵌套莹桅,更為直觀。

但這樣寫(xiě)絕不是最好的烛亦,代碼變得十分冗余诈泼,一堆的then。

所以煤禽,最優(yōu)秀的解決方案是什么呢铐达?

開(kāi)頭暴露了,就是async/await

講async前我們先講講協(xié)程與Generator

三檬果、協(xié)程

協(xié)程(coroutine)瓮孙,意思是多個(gè)線程相互協(xié)作,完成異步任務(wù)选脊。

它的運(yùn)行流程如下

協(xié)程A開(kāi)始執(zhí)行

協(xié)程A執(zhí)行到一半杭抠,暫停執(zhí)行,執(zhí)行的權(quán)利轉(zhuǎn)交給協(xié)程B恳啥。

一段時(shí)間后B交還執(zhí)行權(quán)

協(xié)程A重得執(zhí)行權(quán)偏灿,繼續(xù)執(zhí)行

上面的協(xié)程A就是一個(gè)異步任務(wù),因?yàn)樵趫?zhí)行過(guò)程中執(zhí)行權(quán)被B搶了角寸,被迫分成兩步完成菩混。

讀取文件的協(xié)程代碼如下:

function task() {

? // 其他代碼

? var f = yield readFile('a.txt')

? // 其他代碼

}

task()函數(shù)就是一個(gè)協(xié)程忿墅,函數(shù)內(nèi)部有個(gè)新單詞yield扁藕,yield中文意思為退讓?zhuān)?/p>

顧名思義,它表示執(zhí)行到此處疚脐,task協(xié)程該交出它的執(zhí)行權(quán)了亿柑。也可以把yield命令理解為異步兩個(gè)階段的分界線。

協(xié)程遇到y(tǒng)ield命令就會(huì)暫停棍弄,把執(zhí)行權(quán)交給其他協(xié)程望薄,等到執(zhí)行權(quán)返回繼續(xù)往后執(zhí)行疟游。最大的優(yōu)點(diǎn)就是代碼寫(xiě)法和同步操作幾乎沒(méi)有差別,只是多了yield命令痕支。

這也是異步編程追求的颁虐,讓它更像同步編程

四、Generator函數(shù)

Generator是協(xié)程在ES6的實(shí)現(xiàn)卧须,最大的特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán)另绩,懂得退讓。

function* gen(x) {

? ? var y = yield x +2;

? ? return y;

? }


? var g = gen(1);

? console.log( g.next()) // { value: 3, done: false }

? console.log( g.next()) // { value: undefined, done: true }

上面代碼中花嘶,函數(shù)多了*號(hào)笋籽,用來(lái)表示這是一個(gè)Generator函數(shù),和普通函數(shù)不一樣椭员,不同之處在于執(zhí)行它不會(huì)返回結(jié)果车海,

返回的是指針對(duì)象g,這個(gè)指針g有個(gè)next方法隘击,調(diào)用它會(huì)執(zhí)行異步任務(wù)的第一步侍芝。

對(duì)象中有兩個(gè)值,value和done埋同,value 屬性是 yield 語(yǔ)句后面表達(dá)式的值竭贩,表示當(dāng)前階段的值,done表示是否Generator函數(shù)是否執(zhí)行完畢莺禁。

下面看看Generator函數(shù)如何執(zhí)行一個(gè)真實(shí)的異步任務(wù)

var fetch = require('node-fetch');

function* gen(){

? var url = 'https://api.github.com/users/github';

? var result = yield fetch(url);

? console.log(result.bio);

}

var g = gen();

var result = g.next();

result.value.then( data => return data.json)

? ? ? ? ? ? ? ? ? .then (data => g.next(data))

上面代碼中留量,首先執(zhí)行Generator函數(shù),得到對(duì)象g哟冬,調(diào)用next方法楼熄,此時(shí)

result ={ value: Promise { <pending> }, done: false }

因?yàn)閒etch返回的是一個(gè)Promise對(duì)象,(即value是一個(gè)Promise對(duì)象)所以要用then才能調(diào)用下一個(gè)next方法浩峡。

雖然Generator將異步操作表示得很簡(jiǎn)潔可岂,但是管理麻煩,何時(shí)執(zhí)行第一階段翰灾,又何時(shí)執(zhí)行第二階段缕粹?

是的,這時(shí)候到Async/await出現(xiàn)了纸淮!

五平斩、Async/await

從回調(diào)函數(shù),到Promise對(duì)象咽块,再到Generator函數(shù)绘面,JavaScript異步編程解決方案歷程可謂辛酸,終于到了Async/await。很多人認(rèn)為它是異步操作的最終解決方案(謝天謝地揭璃,這下不用再學(xué)新的解決方案了吧)

其實(shí)async函數(shù)就是Generator函數(shù)的語(yǔ)法糖晚凿,例如下面兩個(gè)代碼:

var gen = function* (){

? var f1 = yield readFile('./a.txt');

? var f2 = yield readFile('./b.txt');

? console.log(f1.toString());

? console.log(f2.toString());

};

var asyncReadFile = async function (){

? var f1 = await? readFile('./a.txt');

? var f2 = await? readFile('./b.txt');

? console.log(f1.toString());

? console.log(f2.toString());

};

上面的為Generator函數(shù)讀取兩個(gè)文件,下面為async/await讀取瘦馍,比較可發(fā)現(xiàn)歼秽,兩個(gè)函數(shù)其實(shí)是一樣的,async不過(guò)是把Generator函數(shù)的*號(hào)換成async情组,yield換成await哲银。

1.async函數(shù)用法

上面說(shuō)了async不過(guò)是Generator函數(shù)的語(yǔ)法糖,那為什么要取這個(gè)名字呢呻惕?自然是有理由的荆责。

async是“異步”,而await是async wait的簡(jiǎn)寫(xiě)亚脆,即異步等待做院。所以應(yīng)該很好理解async用于聲明一個(gè)function是異步的,await用于等待一個(gè)異步方法執(zhí)行完成

下面來(lái)看一個(gè)例子理解async命令的作用

async function test() {

? return "async 有什么用濒持?";

}

const result = test();

console.log(result)

輸出:

Promise { 'async 有什么用键耕?' }

可以看到,輸出的是一個(gè)Promise對(duì)象柑营!

所以屈雄,async函數(shù)返回的是一個(gè)Promise對(duì)象,如果直接return 一個(gè)直接量官套,async會(huì)把這個(gè)直接量通過(guò)PromIse.resolve()封裝成Promise對(duì)象

注意點(diǎn)

一般來(lái)說(shuō)酒奶,都認(rèn)為await是在等待一個(gè)async函數(shù)完成,確切的說(shuō)等待的是一個(gè)表示式奶赔,這個(gè)表達(dá)式的計(jì)算結(jié)果是Promise對(duì)象或者是其他值(沒(méi)有限定是什么)

即await后面不僅可以接Promise惋嚎,還可以接普通函數(shù)或者直接量。

同時(shí)站刑,我們可以把a(bǔ)sync理解為一個(gè)運(yùn)算符另伍,用于組成表達(dá)式,表達(dá)式的結(jié)果取決于它等到的東西

等到非Promise對(duì)象 表達(dá)式結(jié)果為它等到的東西

等到Promise對(duì)象? await就會(huì)阻塞后面的代碼绞旅,等待Promise對(duì)象resolve疲扎,取得resolve的值磕道,作為表達(dá)式的結(jié)果

還是那個(gè)業(yè)務(wù)形葬,分多個(gè)步驟完成搅幅,每個(gè)步驟依賴(lài)于上一個(gè)步驟的結(jié)果溯职,用setTimeout模擬異步操作舔痕。

/**

* 傳入?yún)?shù) n食听,表示這個(gè)函數(shù)執(zhí)行的時(shí)間(毫秒)

* 執(zhí)行的結(jié)果是 n + 200奸披,這個(gè)值將用于下一步驟

*/

function takeLongTime(n) {

? ? return new Promise(resolve => {

? ? ? ? setTimeout(() => resolve(n + 200), n);

? ? });

}

function step1(n) {

? ? console.log(`step1 with ${n}`);

? ? return takeLongTime(n);

}

function step2(n) {

? ? console.log(`step2 with ${n}`);

? ? return takeLongTime(n);

}

function step3(n) {

? ? console.log(`step3 with ${n}`);

? ? return takeLongTime(n);

}

async實(shí)現(xiàn)方法

async function doIt() {

? ? console.time("doIt");

? ? const time1 = 300;

? ? const time2 = await step1(time1);

? ? const time3 = await step2(time2);

? ? const result = await step3(time3);

? ? console.log(`result is ${result}`);

? ? console.timeEnd("doIt");

}

doIt();

輸出結(jié)果和上面用Promise實(shí)現(xiàn)是一樣的,但這個(gè)代碼結(jié)構(gòu)看起來(lái)清晰得多蝎土,幾乎跟同步寫(xiě)法一樣视哑。

2. async函數(shù)的優(yōu)點(diǎn)

(1)內(nèi)置執(zhí)行器

Generator 函數(shù)的執(zhí)行必須靠執(zhí)行器,所以才有了 co 函數(shù)庫(kù)誊涯,而 async 函數(shù)自帶執(zhí)行器挡毅。也就是說(shuō),async 函數(shù)的執(zhí)行暴构,與普通函數(shù)一模一樣跪呈,只要一行。

(2) 語(yǔ)義化更好

async 和 await取逾,比起星號(hào)和 yield耗绿,語(yǔ)義更清楚了。async 是“異步”的簡(jiǎn)寫(xiě)砾隅,而 await 可以認(rèn)為是 async wait 的簡(jiǎn)寫(xiě)误阻。所以應(yīng)該很好理解 async 用于申明一個(gè) function 是異步的,而 await 用于等待一個(gè)異步方法執(zhí)行完成晴埂。

(3)更廣的適用性

yield 命令后面只能是 Thunk 函數(shù)或 Promise 對(duì)象究反,而 async 函數(shù)的 await 命令后面,可以跟 Promise 對(duì)象和原始類(lèi)型的值(數(shù)值儒洛、字符串和布爾值精耐,但這時(shí)等同于同步操作)。

以上就是中大廠的面試知識(shí)點(diǎn)匯總琅锻,算不上包羅萬(wàn)象卦停,但是涵蓋的知識(shí)點(diǎn)還是比較齊全的,只要你能認(rèn)真看完本文并理解了恼蓬,那面試應(yīng)該是沒(méi)啥大問(wèn)題的沫浆。當(dāng)然,其中也少不了你自己的努力滚秩,還有更多的知識(shí)點(diǎn)需要你去學(xué)習(xí)专执。前端就是這樣,招聘的是工程師郁油,干活卻是螺絲釘本股。這就要求各位前端coder們,不能只注重業(yè)務(wù)桐腌,平時(shí)也要給自己充電拄显,知其然知其所以然,擴(kuò)展自己的知識(shí)范圍案站,懂得越多躬审,在前端這條路上也才能走的更穩(wěn)、更遠(yuǎn)。

最后承边,希望看過(guò)我文章的都找到好工作遭殉,也不枉我辛苦一場(chǎng)!2┲险污!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市富岳,隨后出現(xiàn)的幾起案子蛔糯,更是在濱河造成了極大的恐慌,老刑警劉巖窖式,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚁飒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡萝喘,警方通過(guò)查閱死者的電腦和手機(jī)飒箭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蜒灰,“玉大人弦蹂,你說(shuō)我怎么就攤上這事∏拷眩” “怎么了凸椿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)翅溺。 經(jīng)常有香客問(wèn)我脑漫,道長(zhǎng),這世上最難降的妖魔是什么咙崎? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任优幸,我火速辦了婚禮,結(jié)果婚禮上褪猛,老公的妹妹穿的比我還像新娘网杆。我一直安慰自己,他們只是感情好伊滋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布碳却。 她就那樣靜靜地躺著,像睡著了一般笑旺。 火紅的嫁衣襯著肌膚如雪昼浦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天筒主,我揣著相機(jī)與錄音关噪,去河邊找鬼鸟蟹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛使兔,可吹牛的內(nèi)容都是我干的建钥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼火诸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锦针!你這毒婦竟也來(lái)了荠察?” 一聲冷哼從身側(cè)響起置蜀,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悉盆,沒(méi)想到半個(gè)月后盯荤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焕盟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年秋秤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脚翘。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灼卢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出来农,到底是詐尸還是另有隱情鞋真,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布沃于,位于F島的核電站涩咖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏繁莹。R本人自食惡果不足惜檩互,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咨演。 院中可真熱鬧闸昨,春花似錦、人聲如沸薄风。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)村刨。三九已至告抄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嵌牺,已是汗流浹背打洼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工龄糊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人募疮。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓炫惩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親阿浓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子他嚷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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