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┲险污!