web面試題
css面試
一、css盒模型
css中的盒子模型包括IE盒子模型和標准的W3C盒子模型。
border-sizing: border-box, inherit, content-box
標准盒子模型: 左右border+左右padding+contentwidth
IE盒子模型border-box: width = content+padding+border 元素指定的任何內邊距和邊框都將在已設定的寬度和高度內進行繪制
inherit: 從父類繼承box-sizing的值
二、前端一像素問題(畫一條0.5px的線)
* transform: scaleY(0.5) 使用偽元素設置1px的邊框,然后對邊框進行縮放(s
caleY)
實現思路:
1、設定目標元素的參考位置
2、給目標元素添加一個偽元素before或者after,並設置絕對定位
3、給偽元素添加1px的邊框
4、用box-sizing: border-box 屬性把邊框都包進寬和高里面
5、寬和高設置為 200%
6、整個盒子模型縮小為0.5
7、調整盒子模型的位置,以左上角為基准 transform-origin: 0 0;
代碼如下:
#box{position:relative;}
#box::after{
content:'';box-sizing:border-box;
position:absolute;top:0;left:0;
width:200%;height:200%;
border:1px solid red;
transform-origin:0 0;
transform:scale(0.5);
}
* border-image 設置圖片的邊框
三、4.transition和animation的區別
Animation和transition大部分屬性是相同的,他們都是隨時間改變元素的屬性值,他們的主要區別是transition需要觸發一個事件才能改變屬性,
而animation不需要觸發任何事件的情況下才會隨時間改變屬性值,並且transition為2幀,從from .... to,而animation可以一幀一幀的。
transition 規定動畫的名字 規定完成過渡效果需要多少秒或毫秒 規定速度效果 定義過渡效果何時開始
animation 指定要綁定到選擇器的關鍵幀的名稱
四、不定寬高的DIV居中
1.使用flex 在父盒子設置display: flex; justify-content: center;align-items: center
2.使用css的transform 父盒子設置:display:relative
Div 設置: transform: translate(-50%,-50%);position: absolute;top: 50%;left: 50%;
3.display:table-cell 父盒子設置:display:table-cell; text-align:center;vertical-align:middle;
Div 設置: display:inline-block;vertical-align:middle;
4.position:absolute;top:0;left:0;right:0;bottom:0;margin:auto;
五、浮動 https://juejin.im/post/5a954add6fb9a06348538c0d
特性:浮動元素影響的不僅是自己,他會影響周圍元素對其進行環繞
為什么要清除浮動?(解決父元素高度坍陷問題)
一個塊級元素如果沒有設置height,其height由子元素撐開,對子元素使用了浮動之后,子元素就會脫離文檔流
也就是說,父及元素中沒有內容可以撐開其高度,這樣父級元素height就會被忽略。這就是所謂的高度坍塌
如何清除浮動
1.給父級元素定義高度 2.讓父級元素也浮動 3.父級定義display:table 4.父元素設置overflow:hidden
clearfix:使用內容生成的方式清除浮動
.clearfix:after { // :after選擇器向選定的元素之后插入內容
content:""; // 生成內容為空
display: block; // 塊級元素顯示
clear:both; // 清除前面元素
}
不破壞文檔流,沒有副作用
七、position
值:relative,static(默認值),absolute,sticky,fixed
absolute會根據上一級position的值不為static進行定位,如果向上一直沒有找到position,則相對整個body進行定位
fixe相對的是視圖的窗口,或者frame框架(setFram的子框架,一種html標簽)
八、css選擇器分類:
基本的:
1.id選擇器(id="name")
2.類選擇器(class="head")
3.標簽選擇器(body, div, ul, li)
4.全局選擇器(*)
復雜的:
1.組合選擇器(.head .head_logo)
2.后代選擇器 (#head .nav ul li 從父集到子孫集)
3.群組選擇器 (div, span, img {color:Red} 具有相同樣式的標簽分組顯示)
4.繼承選擇器
5.偽類選擇器(鏈接樣式,a元素的偽類)
6.子選擇器(div>p, 帶大於號>)
7.CSS相鄰相鄰兄弟選擇器(h1+p, 帶加號+)
九、CSS優先級
不同級別:總結排序:!important > 行內樣式>ID選擇器 > 類選擇器 > 標簽 > 通配符 > 繼承 > 瀏覽器默認屬性
1.屬性后面加!import 會覆蓋頁面內任何位置定義的元素樣式
2.作為style屬性寫在元素內的樣式
3.id選擇器
4.類選擇器
5.標簽選擇器
6.通配符選擇器(*)
7.瀏覽器自定義或繼承
同一級別:后寫的會覆蓋先寫的
css選擇器的解析原則:選擇器定位DOM元素是從右往左的方向,這樣可以盡早的過濾掉一些不必要的樣式規則和元素
十、對於行內元素,font-size指定 他們的content area的高度,由於inline box = 上下的helf-leading,如果leading為0,在這種情況下,font-size指定了inline box的高度
font-size 指的是字體的高度,但是不能指定每個字形給定字體高度下的實際高度,導致了span的高度大於line-height
十一、z-index屬性 z-index 屬性設置元素的堆疊順序。擁有更高堆疊順序的元素總是會處於堆疊順序較低的元素的前面。
position的值的屬性大於z-index Z-index 僅能在定位元素上奏效(例如 position:absolute;)
元素可擁有負的 z-index 屬性值,父子關系的元素z-index設置無效
十二、塊元素和行內元素
1.塊元素會獨占一行,默認情況下,其寬度自動填滿父元素寬度 行元素不會占據一行,會一直排在一行,直到一行排不下
2.行元素沒有寬度和高度屬性,塊級元素即使設置了寬度,還是會獨占一行
塊級元素: div p form ul li ol dl h1-h6
行內元素:span img a i s u b a strong em
行內塊元素:input
十三、如何畫一個三角形: 設置寬高,然后用border去畫
width:0;
height:0;
border:25px solid transparent;
border-top-color:red;
十四、偽類:link 表示鏈接正常情況下(即頁面加載完成時)顯示的顏色
hover:表示鼠標懸停時顯示的顏色
visited:鏈接被點擊時顯示的位置
focus:元素獲得光標焦點時的顏色
active: 元素處於激活狀態
link -> visited -> hover -> focus -> active
十五、雪碧圖:多個圖片集成在一個圖片中的圖
使用雪碧圖可以減少網絡請求的次數,加快允許的速度
通過background-position,去定位圖片在屏幕的哪個位置
十六: Margin-top和translate 50%的區別
Margin-top默認以左上角為目標運動
Translate 默認以中心點為目標運動
十七:2px和2區別
沒有單位可以用於縮放的位置ex: flex-grow
十八:line-height是針對誰設置的,繼承誰?
設置數字,此數字會與當前的字體尺寸相乘來設置行間距
布局面試
一、flex彈性布局,
可以簡單的使一個元素居中(包括水平和垂直居中)
柵格式系統布局,bootstrap grid
二、聖杯和雙飛翼布局 三欄是布局(兩邊兩欄寬度固定,中間欄寬度自適應)
方案一:position(絕對定位法) center的div需要放在最后面
絕對定位法原理將左右兩邊使用absolute定位,因為絕對定位使其脫離文檔流,后面的center會自然流動到他們的上面去,然后margin屬性,留出左右兩邊的寬度。就可以自適應了。
方案二:float 自身浮動法 center的div需要放到后面
自身浮動法的原理就是對左右使用float:left和float:right,float使左右兩個元素脫離文檔流,中間的正常文檔流中,使用margin指定左右外邊距對其進行一個定位。
聖杯布局:原理就是margin負值法。使用聖杯布局首先需要在center元素外部包含一個div,包含的div需要設置float屬性使其形成一個BFC,並且這個寬度和margin的負值進行匹配
三、左邊定寬,右邊自適應
方案一:左邊設置浮動,右邊寬度設置100%
.left{float:left;height: 150px;width: 150px;}
.right{width: 100%;height: 200px;margin-left: 150px;}
方案二:同上將float設置為position
方案三:父容器設置display:flex right部分是設置flex:1
四、水平居中
行內元素居中(父元素text-align:center)
塊狀元素居中(塊狀元素沒發用text-align)
1.寬度一定:margin:auto
2.寬度不定:塊級變行內,然后在父上text-aligin
float
四、BFC https://juejin.im/post/5909db2fda2f60005d2093db
理解:BFC是css布局的一個概念,是一塊獨立的渲染區域,一個環境,里面的元素不會影響到外部的元素
如何生成BFC:(脫離文檔流)
【1】根元素,即HTML元素(最大的一個BFC)
【2】float的值不為none
【3】position的值為absolute或fixed
【4】overflow的值不為visible(默認值。內容不會被修剪,會呈現在元素框之外)
【5】display的值為inline-block、table-cell、table-caption
BFC布局規則:1.內部的Box會在垂直方向,一個接一個地放置。
2.屬於同一個BFC的兩個相鄰的Box的margin會發生重疊
3.BFC就是頁面上的一個隔離的獨立容器,容器里面的子元素不會影響到外面的元素。反之也如此, 文字環繞效果,設置float
4.BFC的區域不會與float box重疊。
5.計算BFC的高度,浮動元素也參與計算
BFC作用:1.自適應兩欄布局
2.可以阻止元素被浮動元素覆蓋
3.可以包含浮動元素---清除內部浮動 原理::觸發父div的BFC屬性,使下面的子div都處在父div的同一個BFC區域之內
4.分屬於不同的BFC時,可以阻止margin重疊
js面試
一、 this的指向:https://www.cnblogs.com/pssp/p/5216085.html
1.作為普通函數,this指向window。ex: fn();//window
2.自動引用正在調用當前方法.前的對象ex: a.fn();//a
3.如果有嵌套的情況,則this綁定到最近的一層對象上;ex: o.a.fn();//a
4.構造器調用(類型.prototype.fun),this指向返回的這個對象(將來調用函數.前的子對象)。
5.箭頭函數 箭頭函數的this綁定看的是this所在函數定義在哪個對象下,就綁定哪個對象
注意:
1.this永遠指的是最后調用它的對象ex:var j=o.b.fn; j(); //window
2.new Fun() ,this指向的是正在創建的新對象;ex: var a=new fn();a.uname//a
3.this碰到retuen,調用方法里return了一個對象,this指向return對象
怎么改變this的指向呢? 1.使用es6的箭頭函數;2.在函數內部使用that = this;3.使用apply,call,bind; 4.new實例化一個對象
二、什么是閉包和原型鏈
內部函數可以訪問定義他們外部函數的參數和變量。(作用域鏈的向上查找,把外圍的作用域中的變量值存儲在內存中而不是在函數調用完畢后銷毀)設計私有的方法和變量,避免全局變量的污染
函數嵌套函數
本質是將函數內部和外部連接起來。優點是可以讀取函數內部的變量,讓這些變量的值始終保存在內存中,不會在函數被調用之后自動清除
閉包的缺陷:
1.閉包的缺點就是常駐內存會增大內存使用量,並且使用不當容易造成內存泄漏
2.如果不是因為某些特殊任務而需要閉包,在沒有必要的情況下,在其它函數中創建函數是不明智的,因為閉包對腳本性能具有負面影響,包括處理速度和內存消耗。
內存溢出和內存泄漏(給的不夠用| 用了不歸還)
內存溢出:在程序中申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出
內存泄漏:在程序申請內存后,無法釋放已申請的內存空間,一次內存泄漏危害可以忽略,但內存泄漏堆積后果很嚴重,無論多少內存,遲到會被占光
舉列子:閉包中的this,對象函數。匿名函數返回函數return function
作用域:(由當前環境與上層環境一系列的變量對象組成!!!保證 當先執行環境里,有權訪問的變量和函數是有序的,作用域鏈變量只能被向上訪問)
定義:由當前環境與上層環境的一系列變量對象組成(函數嵌套函數,內部一級級往上有序訪問變量或對象)
作用是:保證當前執行環境里,有權訪問的變量和函數時有序的,作用域鏈的變量只能被向上訪問
變量訪問到window對象及被終止,作用域鏈向下訪問是不允許的
1.改變作用域有 with try..中的catch,
2.所有為定義的直接賦值的變量自動聲明為全局作用域
作用域:一套規則,管理引擎如何在當前作用域以及嵌套的子作用域中根據標識符名稱
查找變量(標識符就是變量或者函數名)(只用全局作用域和局部作用域)(作用域在它創建的時候就存在了)
代碼執行分為兩個階段:
1.代碼編譯階段:有編譯器完成,將代碼翻譯可執行的代碼,這個階段會被確定
2.代碼執行階段:有js引擎完成,主要執行可執行的大媽,這個階段執行上下文被創建(對象被創建)
執行上下文:一個看不見得對象,存在若干個屬性和變量,它被調用的時候創建的。函數被調用查看的this指向的object,object就是上下文(只有被調用的時候創建)
作用域鏈: https://blog.csdn.net/yooungt13/article/details/20581635
· 當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈,
舉例子:var name ="Tom"
function sayHi () {
alert('Hi,'+name)
}
sayHi() //Hi, Tom
函數sayHi()的執行環境為全局環境,所以它的變量對象為window。當函數執行到name時,先查找局部環境,找到則換回,否則順着作用域查找,在全局環境中,
找到name返回,這一查找變量的有序過程的依據就是作用域。
· 作用域鏈是保證執行環境有權訪問的所有變量和函數的有序訪問
原型鏈:函數的原型鏈對象constructor默認指向函數本身,原型對象除了有原型屬性外,為了實現繼承,還有一個原型鏈指針_proto_,
該指針是指向上一層的原型對象,而上一層的原型對象的結構依然類似。因此可以利用_proto_一直指向Object的原型對象上,而Object
原型對象用Object.prototype._proto_ = null表示原型鏈頂端。如此形成了js的原型鏈繼承。同時所有的js對象都有Object的基本防范
三、類的創建和繼承
(es5)new 一個function,在這個function的prototype里增加屬性和方法, 類里面有方法和屬性
(es6)中class, extends
繼承:
原型鏈繼承: function Cat(){ } Cat.prototype = new Animal(); Cat.prototype.name = 'cat'; 無法實現多繼承
構造繼承: 使用父類的構造函數來增強子類實例。function Cat(name){Animal.call(this);this.name = name || 'Tom';} 無法繼承父類原型鏈上的屬性跟方法 installof去檢驗
實例繼承: 為父類實例添加新特性,作為子類實例的返回
拷貝繼承: 拷貝父類元素上的屬性跟方法
組合繼承:構造繼承 + 原型繼承的組合體
寄生組合繼承:通過寄生方式,在構造繼承上加一個Super函數(沒有實例和方法) 讓他的原型鏈指向父類的原型鏈
砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性
如何判斷是那種類型
四、異步回調(如何解決回調地獄)
promise、generator、async/await
promise: 1.是一個對象,用來傳遞異步操作的信息。代表着某個未來才會知道結果的時間,並未這個事件提供統一的api,供進異步處理
2.有了這個對象,就可以讓異步操作以同步的操作的流程來表達出來,避免層層嵌套的回調地獄
3.promise代表一個異步狀態,有三個狀態pending(進行中),Resolve(以完成),Reject(失敗)
4.一旦狀態改變,就不會在變。任何時候都可以得到結果。從進行中變為以完成或者失敗
promise.all() 里面狀態都改變,那就會輸出,得到一個數組
promise.race() 里面只有一個狀態變為rejected或者fulfilled即輸出
promis.finally()不管指定不管Promise對象最后狀態如何,都會執行的操作(本質上還是then方法的特例)
五、前端事件流
事件流描述的是從頁面中接受事件的順序,事件 捕獲階段 處於目標階段 事件冒泡階段 addeventListener 最后這個布爾值參數如果是true,表示在捕獲階段調用事件處理程序;如果是false,表示在冒泡階段調用事件處理程序。
1、事件捕獲階段:實際目標div在捕獲階段不會接受事件,也就是在捕獲階段,事件從document到<html>再到<body>就停止了。
2、處於目標階段:事件在div發生並處理,但是事件處理會被看成是冒泡階段的一部分。
3、冒泡階段:事件又傳播回文檔
阻止冒泡事件event.stopPropagation()
function stopBubble(e) {
if (e && e.stopPropagation) { // 如果提供了事件對象event 這說明不是IE瀏覽器
e.stopPropagation()
} else {
window.event.cancelBubble = true //IE方式阻止冒泡
}
}
阻止默認行為event.preventDefault()
function stopDefault(e) {
if (e && e.preventDefault) {
e.preventDefault()
} else {
// IE瀏覽器阻止函數器默認動作的行為
window.event.returnValue = false
}
}
事件如何先捕獲后冒泡?
在DOM標准事件模型中,是先捕獲后冒泡。但是如果要實現先冒泡后捕獲的效果,
對於同一個事件,監聽捕獲和冒泡,分別對應相應的處理函數,監聽到捕獲事件,先暫緩執行,直到冒泡事件被捕獲后再執行捕獲事件。
哪些事件不支持冒泡事件:鼠標事件:mouserleave mouseenter
焦點事件:blur focus
UI事件:scroll resize
六、事件委托(提高性能)
簡介:事件委托指的是,不在事件的(直接dom)上設置監聽函數,而是在其父元素上設置監聽函數。通過事件冒泡,父元素可以監聽到子元素上事件的觸發
通過判斷事件發生元素DOM的類型,來做出不同的響應。
舉例子: 最經典的就是ui和li標簽的事件監聽,比如我們在添加事件的時候,采用事件委托機制,不會在li標簽上直接添加,而是在ul父元素上添加
好處:可以比較合適動態元素的綁定,新添加的子元素也會監聽函數,也可以有事件觸發機制
七、js的new操作符做了什么?
new操作符創建了一個空對象,這個對象原型指向構造函數的prototype,執行構造函數后返回這個對象(return this)。
如果不要父類的屬性跟方法,在函數的prototype上去new這個父類。
八、改變函數內部this指針的指向函數(bind,apply,call)
通過apply和call改變函數的this指向,他們兩個函數的第一個參數都是一樣的表示要改變指向的那個對象,第二個參數,apply是數組,而call則是arg1,arg2...這種形式。
bind 一個是返回一個函數,並不會立即執行 第二個是帶參數(第一個參數要指向的this,后面的的參數用來傳遞
九、深拷貝和淺拷貝 https://juejin.im/post/5b00e85af265da0b7d0ba63f 從堆和棧都是內存中划分出來用來存儲的區域開始講起
基本類型:undefined,null,Boolean,String,Number,Symbol 在內存中占據固定大小,保存在棧內存中
引用類型:Object,Array,Date,Function,RegExp等 引用類型的值是對象 保存在堆內存中,棧內存存儲的是對象的變量標識符以及對象在堆內存中的存儲地址。
基本類型的復制: 其實就是創建了一個新的副本給將這個值賦值給新變量, 改變值舊對象不會改變
引用類型的復制: 其實就是復制了指針,這個最終都將指向同一個對象,改變其值新對象也會改變
基本類型的比較 == 會進行類型轉換
淺拷貝:僅僅就是復制了引用,彼此操作不影響,slice() concat() object.assign
深拷貝:在堆中重新分配內存,不同的地址,相同的值,互不影響的 JSON.parse()將一個js對象序列化為一個json字符串 JSON.stringify()將json字符串反序列化為一個js對象 es6的展開 {...}
深拷貝和淺拷貝的主要區別是:在內存中的存儲類型不同
淺拷貝:重新在堆棧中創建內存,拷貝前后對象的基本類型互不影響。只拷貝一層,不能對對象進行子對象進行拷貝
深拷貝:對對象中的子對象進行遞歸拷貝,拷貝前后兩個對象互不影響
十、跨域
同源策略(協議+端口號+域名要相同)
1、jsonp跨域(只能解決get)
原理:動態創建一個script標簽。利用script標簽的src屬性不受同源策略限制,因為所有的src屬性和href屬性都不受同源策略的限制,可以請求第三方服務器資源內容
步驟:1.去創建一個script標簽
2.script的src屬性設置接口地址
3.接口參數,必須要帶一個自定義函數名,要不然后台無法返回數據
4.通過定義函數名去接受返回的數據
2、document.domain 基礎域名相同 子域名不同
3、window.name 利用在一個瀏覽器窗口內,載入所有的域名都是共享一個window.name
4、服務器設置對CORS的支持
原理:服務器設置Access-Control-Allow-Origin HTTP響應頭之后,瀏覽器將會允許跨域請求
5、利用h5新特性window.postMessage()
iframe元素創建包含另外一個文檔的內聯框架(行內框架)(setTimeout進行異步加載)
解釋:瀏覽器中的瀏覽器!用於設置文本或者圖形的浮動圖文框或容器
它和跨域
1、document.domain 實現主域名相同,子域名不同的網頁通信
都設置為超域:document.domain = 'demo.com'
2、window.postMessageht(data, url),h5的API,啟動跨域通信
十一、圖片的懶加載和預加載
預加載:提前加載圖片,當用戶需要查看是可以直接從本地緩存中渲染
為什么要使用預加載:在網頁加載之前,對一些主要內容進行加載,以提供用戶更好的體驗,減少等待時間。
否則,如果一個頁面的內容過於龐大,會出現留白。
解決頁面留白的方案:1.預加載 2.使用svg站位圖片,將一些結構快速搭建起來,等待請求的數據來了之后,替換當前的占位符
實現預加載的方法:
1.使用html標簽
2.使用Image對象
3.使用XMLHTTPRequest對像,但會精細控制預加載過程
懶加載(lazyload):客戶端優化,減少請求數和延遲請求數
提升用戶體驗,
減少無效資源的加載
防止並發加載的資源過多會阻塞js的加載,影響網站的正常使用
原理:首先將頁面上的圖片的src屬性設置為空字符串,而圖片的真是路經則設置帶data-original屬性中,
當頁面滾動的時候需要去監聽scroll事件,在scroll事件的回調中,判斷我們的懶加載的圖片是否進入到可視區域
,如果圖片在可視區域將圖片的src屬性設置為data-original的值,這樣就可以實現延遲加載。
十二、函數節流防抖
什么是防抖:短時間內多次觸發同一個事件,只執行最后一次,或者在開始時執行,中間不執行。比如公交車上車,要等待最后一個乘客上車
什么是節流:節流是連續觸發事件的過程中以一定時間間隔執行函數。節流會稀釋你的執行頻率,比如每間隔1秒鍾,只會執行一次函數,無論這1秒鍾內觸發了多少次事件
都為解決高頻事件而來, scroll mousewhell mousemover touchmove onresize
十三、將arguments類數組轉化為數組的方法
Array.apply(null, arguments)
Array.prototype.slice.apply(arguments)
Array.from(arguments)
十四、高階函數
一、函數作為參數傳遞 抽離出一部分容易變化的業務邏輯,把這部分業務邏輯放在函數參數中。這樣一來可以分離業務代碼中變化與不變的部分
回調函數
二、函數作為返回值傳遞
十五、如何判斷一個變量是對象還是數組(prototype.toString.call())。
千萬不要使用typeof來判斷對象和數組,因為這種類型都會返回object。
typeOf()是判斷基本類型的Boolean,Number,symbol, undefined, String。
對於引用類型:除function,都返回object null返回object。
installOf() 用來判斷A是否是B的實例,installof檢查的是原型。(arr instanceof Array)
toString() 是Object的原型方法,對於 Object 對象,直接調用 toString() 就能返回 [object Object] 。而對於其他對象,則需要通過 call / apply 來調用才能返回正確的類型信息。
hasOwnProperty()方法返回一個布爾值,指示對象自身屬性中是否具有指定的屬性,該方法會忽略掉那些從原型鏈上繼承到的屬性。
isProperty()方法測試一個對象是否存在另一個對象的原型鏈上。
十六、setTimeout 和 setInterval的機制
因為js是單線程的。瀏覽器遇到etTimeout 和 setInterval會先執行完當前的代碼塊,在此之前會把定時器推入瀏覽器的
待執行時間隊列里面,等到瀏覽器執行完當前代碼之后會看下事件隊列里有沒有任務,有的話才執行定時器里的代碼
十七、var let const
const:定義的變量不可修改,必須初始化 ,
var:定義的變量可以修改,如果不初始化輸出undefined,不會報錯
let:塊級作用域,函數內部使用let定義后,會函數外部無影響
let const 不會造成變量的提升
十八、js垃圾回收機制
1.JS具有自動垃圾收集的機制
2.JS的內存生命周期(變量的生命)
1.分配你所需要的空間 var a = 20
2.使用分配帶的內存(讀寫) alert(a + 10)
3.不適用的時候,釋放內存空間 a = null
3.JS的垃圾收集器每隔固定的時間就執行一次釋放操作,通用的是通過標記清除的算法
4.在局部作用域中,垃圾回收器很容易做出判斷並回收,全局比較難,因此應避免全局變量
標記清除算法:js最常見的垃圾回收方式,當變量進入執行環境的時候,比如函數中聲明一個變量,垃圾回收器將他標記為'進入環境',
當變量離開(函數執行完后),就其標記為'離開環境'。垃圾回收器會在運行的時候給存儲在內存中的所有變量加上標記,
然后去掉環境中的變量以及被環境中該變量所引用的變量(閉包)。在這些完成之后仍存在標記的就是要刪除的變量了
十九、漸進增強和優雅降級
1.漸進增強就是針對低版本瀏覽器進行構建頁面,保證最基本的功能,然后對高級瀏覽器進行效果、交互等改進和最佳功能達到更好的用戶體驗
2.優雅降級:一開始構建完整的功能,然后對低版本的進行兼容
二十、undefined 和 null
1.undefined類型只要一個,即undefined,當聲明變量還未被初始化時就是undefined
2.null類型也只有一個值,即null。null用來表示尚未存在的對象,常用來表示函數企圖返回一個不存在的對象
3.NaN 與任何值都是相比較的結果都是false
二十一、valueof和tostring
valueof:所有對象都有valueof,如果存在任意原始值,他就默認將對象轉化為表示它的原始值。
如果對象是復合值,而卻大部分對象無法真正表示一個原始值,因此默認的valueof()方法簡單的返回對象本身,而不是返回原始值。
數組、函數和正則表達式簡單的繼承了這個more方法,返回對象本身
二十二、輸入框的change和input事件
onchange事件:要在input失去焦點的時候才觸發
oninput事件:要在用戶輸入的時觸發,他是元素值發生變化時立即觸發
二十三、同步和異步
同步:由於js單線程,同步任務都在主線程上排隊執行,前面任務沒有執行完成,后面的任務會一直等待
異步:不進入主線程,進入任務隊列,等待主線程任務執行完成,開始執行。最基本的異步操作SetTimemot和SetInterval,等待主線程任務執行完,在開始執行里面的函數
二十四、函數的柯里化
概念:一個函數接受函數A作為參數,運行后返回return function一個新的函數,並且可以處理A中的參數(只接受單一參數的函數)
意義:將函數完全變成了接受一個參數,返回一個參數的固定形式,便於討論和優化
二十五、while
while循環會在指定條件為真時循環執行代碼
二十六、TypeScript的優點:
1、編譯時的強類型,變成了強類型語言,還是編譯成js 編譯的時候就可以檢驗
2、更好的模塊化
3、更好的是實現面向對象的編程,類、接口、模塊
二十七、js的阻塞特性:所有瀏覽器在下載JS的時候,會阻止一切其他活動,比如其他資源的下載,內容的呈現等等。
直到JS下載、解析、執行完畢后才開始繼續並行下載其他資源並呈現內容。
為了提高用戶體驗,新一代瀏覽器都支持並行下載JS,但是JS下載仍然會阻塞其它資源的下載(例如.圖片,css文件等)。
css阻塞:因為瀏覽器會維持html中css和js的順序,樣式表必須在嵌入的JS執行前先加載、解析完。
而嵌入的JS會阻塞后面的資源加載,所以就會出現上面CSS阻塞下載的情況。
二十八、meta元素可提供有關頁面的元信息,比如針對搜索引擎和更新頻度的描述和關鍵詞
二十九: POST提交數據時四種常見格式
服務端通常是根據請求頭(headers)中的 Content-Type 字段來獲知請求中的消息主體是用何種方式編碼,再對主體進行解析。
setRequestHeader(“Content-type” , ”application/x-www-form-urlencoded”);
1)1. application/x-www-form-urlencoded 原生表單
2)2. multipart/form-data 表單上傳文件
3)3. application/json
4)4. text/xml
三十: HTTP請求方法
GET:獲取資源 POST:傳輸文本
PUT:傳輸文件 DELETE:刪除文件
HEAD:獲得報文首部
OPTIONS:訪問服務器端可以提交哪些請求方法
三十一: js實現圖片上傳到服務器和回顯
<input type="file" value="" name="file" id = "input_file"
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg" onchange="imgPreview(this,0)">
function imgPreview(fileDom,i) {
var file = fileDom.files[0]; //獲取文件
reader.onload = function(e) {
document.getElementsByClassName('file-box')[i].style.background="url("+e.target.result+")no-repeat";//回顯圖片
document.getElementsByClassName('file-box')[i].style.backgroundSize='200px 160px';
}
reader.readAsDataURL(file); //轉碼保存
}
//ajax請求
var formData = new FormData();
formData.append('photo', $('#input_file')[0].files[0]);
$.ajax({})
三十二: 簡述瀏覽器渲染過程
解析html以構建dom樹 -> 構建render樹(加上css) -> 布局render樹 -> 繪制render樹
三十三: jQuery工作原理
1)jQuery采用的是構造函數模式進行開發的,jQuery是一個類
2)常用的方法(CSS、屬性、篩選、事件、動畫)都是定義在jQuery.prototype上的 ->只有jQuery的實例才能使用這些方法
三十四: Sessionid如何生成,由誰產生,保存在哪里?
sessionid是一個會話的key,瀏覽器第一次訪問服務器會在服務器端生成一個session,有一個sessionid和它對應。程序調用 HttpServletRequest.getSession(true)這樣的語句時才被創建。存放:服務器端的內存中
三十五: Session和Cookie的區別與聯系:
1、cookie數據存放在客戶的瀏覽器上,session數據放在服務器上。
2、cookie不是很安全,別人可以分析存放在本地的cookie並進行cookie欺騙,考慮到安全應當使用session。
3、session會在一定時間內保存在服務器上。當訪問增多,會比較占用你服務器的性能,考慮到減輕服務器性能方面,應當使用cookie。
4、單個cookie保存的數據不能超過4K,很多瀏覽器都限制一個站點最多保存20個cookie。
5、可以考慮將登陸信息等重要信息存放為session,其他信息如果需要保留,可以放在cookie中。
三十六: JSON.parse()與JSON.stringify()的區別
JSON.parse()從一個字符串中解析出json對象
JSON.stringify()從一個對象中解析出字符串
三十七:NaN 是什么?它的類型是什么?如何可靠地測試一個值是否等於 NaN ?
NAN:不是數字 類型:number
value!==value 為true就是nan(只有nan才會和自己不相等)
Number.isNaN()
三十八:javascript創建數組時,Array()和[]有什么區別
Array()通過調用數組構造函數生成,可以(8)直接定義數組長度,內存中開辟一個存儲空間,比較耗資源
[]通過字面量定義數組,直接在內存中開辟一個存儲字段,不耗資源
三十九: 定義函數時用 var foo = function () {} 和 function foo() {}有什么區別?
前者聲明了一個變量,這個變量是個方法,是可以改變的
后者聲明了一個方法,這個方法名字無法改變
本質變量和方法聲明提前的問題
四十:寫一個按照下面方式調用都能正常工作的sum方法
console.log(sum(2,3)); //輸出Outputs 5
console.log(sum(2)(3)); //輸出Outputs 5
function sum() {
var num = arguments[0];
if(arguments.length === 2) {
return arguments[0] + arguments[1]
} else {
return function(sec) {
return num + sec;
}
}
}
四十一:如果一個函數沒有返回語句,此函數返回值是什么?
無返回語句返回Undefined
return; 返回null,起到中斷方法執行效果
四十二: forEach()和map()的區別
Foreach:沒有返回值,遍歷數組對每個值進行處理
Map():有返回值,返回值組成一個新的數組
四十二:ES6語法
箭頭函數 ()=>{}
Var let const
解構
let {name,age} = {name: "lisi", age: "20"};
console.log(name); //lisi
模板字符串
For in && for of
New promise()
四十三.如何合並對象
Object.assign(o1,o2,o3,…)
四十四.不完全結構
Let [x,y]=[1,2,3] ; x=1,y=2
四十五.var/let/const的區別
Let/const 只在聲明塊級作用域內有效
Let定義變量,const定義常量,必須初始化,不可改變
Var 全局變量,會聲明提前值留原地,多次賦值,例如循環中新值會替換舊值
node面試
一、koa中間件執行機制
1.添加中間件的方式是使用Koa實例的use方法,並傳入一個generator函數,這個generator函數接受一個next參數
2.use的原理:function Application () {this.middleware = [] // 這個數組就是用來裝一個個中間間的}
3.每次執行use方法,就把外面傳進來的generator函數push到middleware數組中
app.use = function (fn) {this.middleware.push(fn)}
4.koa中是預先通過use方法,將請求可能會經過的中間間裝在一個數組中。
5.callback函數就是請求到來的時候執行的回調。把裝着中間件middleware的數組作為參數傳遞為compose這個方法。
6.componse把毫無關系的一個個中間件給收尾串起來了,就好比我們平常的烤面筋
7.componse將中間件從最后一個開始處理,並一直往前知道第一個中間件。其實最關鍵的就是將最后一個中間件得到generator
作為參數傳遞給前一個中間件。當最后一個中間件的參數next是空的generator函數生成對象
中間件是怎么跑起來的:https://juejin.im/post/591c8b4544d904006c90a2cb
vue面試
一、介紹下MVVM(數據的雙向綁定)
M: model數據模型
V: view 界面
MV:作為橋梁負責溝通view跟model
只關心數據的流傳,減少強耦合性。最關鍵的就是數據的雙向綁定
關鍵步驟:1.實現數據監聽器Observer,用object.defineProperty()重寫數據的get/set。值更新就在set中通知訂閱者更新數據
2.實現模板編譯compile,深度遍歷dom樹,對每個元素節點的指令模板替換數據以及訂閱數據
3.實現watch用於連接Observer和compile,能夠訂閱並接受每一個屬性的變動的通知,執行指令綁定的相應的回調函數,從而更新數據
mvc和mvvm其實區別並不大。都是一種設計思想。主要就是mvc中Controller演變成mvvm中的viewModel。mvvm主要解決了mvc中大量的DOM 操作使頁面渲染性能降低,
加載速度變慢,影響用戶體驗。和當 Model 頻繁發生變化,開發者需要主動更新到View 。
二、 eventBus vuex
原理:eventbus 解決了兄弟組件之間事件傳遞問題,本質是訂閱發布者模式,從而擺脫了兄弟之間需要父組件轉而傳遞的復雜。還有一種方法是vuex數據流,單一狀態樹,rootState樹根
名詞,專車。訂閱者跟發布者都引用專車,這個vue實例,來完成訂閱發布者。 emit(發布) on(訂閱一個組件)
npm包 vue-event-proxy
vuex 是將數據單獨的抽離出來,一種狀態管理工具,它借鑒的是Flux、redux的基本思想,將轉態抽離到全局形成一個store
三、watch:
對屬性進行監聽,允許我們執行異步操作,限制我們執行該操作的頻率(debounce),並在我們得到結果前,設置中間轉態。
四、Vue的雙向數據綁定實現原理
1.核心就是數據劫持 + 發布/訂閱者模式:vue使用的是Object.defineProperty()通過監聽他的get/set事件,監聽對數據的操作,從而觸發數據同步
Object.defineProperty缺陷的:
1.只能對屬性進行數據劫持,並且需要深度遍歷整個對象
2.對於數組不能監聽數據的變化
而proxy原生支持監聽數組的變化,並且可以直接對整個對象進行攔截,所有Vue在下個版本中用proxy替換object.defineProperty
五、nextTick原理
六、生命周期函數 https://juejin.im/post/5b41bdef6fb9a04fe63765f1
new Vue(創建一個Vue對象)--> beforeCreate --> observer Data(開始監控data對象數據變化) --> init event(vue內部初始化事件)
--> created() --> compile(編譯模板,把data里面的數據和模板生成html) --> beforeMount(還沒有生成HTML到元素上) -->
mounted(掛載完成,也就是模板中的html渲染到了html頁面中) --> beforeUpdate (Vritual Dom) --> updated --> beforeDestroy --> destroyed
1.ajax請求最好放在created里面,頁面可以訪問到this了
2.關於dom的操作要放在mounted里面,在mounted前面還沒有生成dom
3.每次進入/離開組件都要做一些事情,用什么鈎子函數:
不緩存:進入的時候可以用created和mounted鈎子,離開的時候可以使用beforedDestory(可以訪問this)和destoryed
緩存:緩存了組件之后,在次進入組件不會觸發beforeCreate,created, beforeMount,mounted
如果你想每次進入組件都做一些事情的話,你可以放在activated進入緩存組件的鈎子中
七、keep-alive
在被keep-alive包含的組件/路由,會多出兩個生命周期:activated 和 deactivated
actived在組件第一次渲染時會被調用,之后再每次緩存組件被激活時調用 調用機制:第一次進入緩存路由/組件,在mounted后面,beforeRouteEnter守衛傳給 next 的回調函數之前調用:
八、Vue的SPA 如何優化加載速度
1.減少入口文件體積
2.靜態資源本地緩存
3.開啟Gzip壓縮
4.使用SSR,nuxt.js
九、模塊化
基本概念: 1.在js中,一個模塊就是實現特定功能的文件(js文件)
2.遵循模塊的機制,想要什么就加載什么模塊
3.模塊化開發需要遵循規范
js實現模塊化規范
1.AMD 瀏覽器 requirejs 模塊被異步加載,模塊加載不影響后面語句的運行 默認使用baseURL+ paths的路經解析方式
2.CommonJS nodejs
3.ES6的import/export
4.CMD 瀏覽器端
解決的問題:1.命名沖突 2.文件依賴 3.模塊的復用 4.統一規范和開發方式
十、談談Vue和React組件化的思想
1.我們在各個頁面開發的時候,會產生很多重復的功能,比如element中的xxxx。像這種純粹非頁面的UI,便成為我們常用的UI組件,最初的前端組件也就僅僅指的是UI組件
2.隨着業務邏輯變得越來多是,我們就想要我們的組件可以處理很多事,這就是我們常說的組件化,這個組件就不是UI組件了,而是包具體業務的業務組件
3.這種開發思想就是分而治之。最大程度的降低開發難度和維護成本的效果。並且可以多人協作,每個人寫不同的組件,最后像撘積木一樣的把它構成一個頁面
十二、VUE如何自定義屬性
全局自定義:
Vue.directive(‘focus’,{
Inserted:function(el){
el.focus() //聚焦函數
}
})
組件自定義
directive{
inserted:function(el){
el.focus()
}
}
十三、.Vue和vuex 有什么區別
Vue是框架,vuex是插件,vuex是專門為vue應用程序開發的狀態管理模式
十四、.Vuex中actions和mutations的區別
Mutations的更改是同步更改,用於用戶執行直接數據更改,this.$store.commit(‘名’)觸發
Actions的更改是異步操作,用於需要與后端交互的數據更改,this.$store.dispath(“名”)觸發
注意:
1):定義actions方法創建一個更改函數時,這個函數必須攜帶一個context參數,用於觸發mutations方法,context.commit(‘修改函數名’ , ’異步請求值’);
2):mutations第一個參數必須傳入state,第二個參數是新值
React
一、react和vue的區別
=> 相同點:
1.數據驅動頁面,提供響應式的試圖組件
2.都有virtual DOM,組件化的開發,通過props參數進行父子之間組件傳遞數據,都實現了webComponents規范
3.數據流動單向,都支持服務器的渲染SSR
4.都有支持native的方法,react有React native, vue有wexx
=> 不同點:
1.數據綁定:Vue實現了雙向的數據綁定,react數據流動是單向的
2.數據渲染:大規模的數據渲染,react更快
3.使用場景:React配合Redux架構適合大規模多人協作復雜項目,Vue適合小快的項目
4.開發風格:react推薦做法jsx + inline style把html和css都寫在js了
vue是采用webpack + vue-loader單文件組件格式,html, js, css同一個文件
二、redux中的reducer(純函數)
Redux數據流里,reduces其實是根據之前的狀態(previous state)和現有的action(current action)更新state(這個state可以理解為上下累加器的結果)
每次redux reducer被執行時,state和action被傳入,這個state根據action進行累加或者是'自身消減'(reduce),進而返回最新的state,這也就是典型reduce函數的用法:state -> action -> state
三、react的refs
refs就想一個逃生窗,允許我們之間訪問dom元素或者組件實例,可以向組件添加一個ref屬性的值是一個回調函數,
它將接受地城dom元素或組件的已掛在實例,作為第一個參數
四、react中的keys
幫組我們跟蹤哪些項目已更改、添加、從列表中刪除,key是獨一無二的,可以讓我們高效的去定位元素,並且操作它
五、React的生命周期
三個狀態:Mounting(已插入真實的DOM)
Updating(正在被重新渲染)
Unmounting(已移除真實的DOM)
componentDIdMount 在第一次渲染后調用,只在客服端。之后組件已經生成對應的DOM結構,
componentDidUpdate 在組件完成更新后立即調用,在出初始化是不會調用
六、React子組件向父組件傳值
父組件通過props 給子組件傳遞數據,子組件則是通過調用父組件傳給它的函數給父組件傳遞數據。
七、React數據流
八、為什么虛擬DOM會提高性能 https://www.zhihu.com/question/29504639?sort=created
虛擬DOM相當於在js和真實dom中間加了一個緩存,利用dom diff算法避免了沒有必要的doom操作,從而提高性能
具體實現步驟:
·用JavaScript對象結構表示DOM樹的結構;然后用這個樹構建一個真正的DOM樹,插到文檔中
·當狀態變更的時候,重新構造一棵樹的對象樹,然后用新的樹和舊的樹進行對比,記錄兩棵樹差異
·把2所記錄的差異應用到步驟1所構建的真正的DOM樹上,試圖就更新了。
九、diff算法
1.把樹形結構按照層級分解,只比較同級元素
2.給列表結構的每個單元添加key屬性,方便比較。在實際代碼中,會對新舊兩棵樹進行一個深度優先的遍歷,這樣每個節點都會有一個標記
3.在深度優先遍歷的時候,每遍歷到一個節點就把該節點和新的樹進行對比。如果有差異的話就記錄到一個對象里面
Vritual DOM 算法主要實現上面步驟的三個函數:element, diff, patch。然后就可以實際的進行使用
react只會匹配相同的class的component(這里的class指的是組件的名字)
合並操作,條用component的setState方法的時候,React將其標記為dirty.到每一個時間循環借宿,React檢查所有標記dirty的component重新繪制
4.選擇性子樹渲染。可以重寫shouldComponentUpdate提高diff的性能
十、super
十一、簡述下flux的思想
flux的最大特點,就是數據的‘單向流動’
1.用戶訪問View
2.View發出用戶的Action
3.Dispatcher收到Action,要求state進行相應的更新
4.store更新后,發出一個‘change’事件后,更新頁面
十二、reac性能優化是哪個周期函
shouldComponentUpdate 這個方法用來判斷是否需要調用render方法重新描繪dom.因為dom的描繪非常消耗性能,
如果我們在shouldComponentUpdate方法中能夠寫出更優化的dom diff算法,可以極大的提高性能
十三、react怎么划分業務組件和技術組件
根據組件的職責通常把組件分為UI組件和容器組件
UI組件負責UI的呈現,容器組件負責管理數據和邏輯
兩者通過React-redux提供connect方法聯系起來
十四、setState
setState通過一個隊列機制實現state更新,當執行setState時,會將需要更新的state很后放入狀態隊列
而不會立即更新this.state,隊列機制可以高效地批量更新state。如果不通過setState而直接修改this.state的值
那么該state將不會被放入狀態隊列中。當下次調用setState並對狀態隊列進行合並時,就會忽略之前修改的state,造成不可預知的錯誤
同時,也利用了隊列機制實現了setState的異步更新,避免了頻繁的重復更新state
同步更新state:
setState 函數並不會阻塞等待狀態更新完畢,因此 setNetworkActivityIndicatorVisible 有可能先於數據渲染完畢就執行。第二個參數是一個回調函數,在setState的異步操作結束並且組件已經重新渲染的時候執行
也就是說,我們可以通過這個回調來拿到更新的state的值,實現代碼的同步
例子:componentDidMount() {
fetch('https://test.com')
.then((res) => res.json())
.then(
(data) => {
this.setState({ data:data });
StatusBar.setNetworkActivityIndicatorVisible(false);
}
性能優化
一、webpack打包文件體積過大?(最終打包為一個js文件)
1.異步加載模塊
2.提取第三庫
3.代碼壓縮
4.去除不必要的插件
如何優化webpack構建的性能
一、減少代碼體積 1.使用CommonsChunksPlugin 提取多個chunk之間的通用模塊,減少總體代碼體積
2.把部分依賴轉移到CDN上,避免每次編譯過程都由Webpack處理
3.對一些組件庫采用按需加載,避免無用的代碼
二、減少目錄檢索范圍
·在使用loader的時候,通過制定exclude和include選項,減少loader遍歷的目錄范圍,從而加快webpack編譯速度
三、減少檢索路經:resolve.alias可以配置webpack模塊解析的別名,對於比較深的解析路經,可以對其配置alias
二、我們把開發中的所有資源(圖片,js、css文件)都看成模塊,通過loader和plugins來對資源進行處理,打包成符合生產環節部署的前端資源。
三、移動端的性能優化
1、首屏加載和按需加載,懶加載
2、資源預加載
3、圖片壓縮處理,使用base64內嵌圖片
4、合理緩存dom對象
5、使用touchstart代替click(click 300毫秒的延遲)
6、利用transform:translateZ(0),開啟硬件GUP加速
7、不濫用web字體,不濫用float(布局計算消耗性能),減少font-size聲明
8、使用viewport固定屏幕渲染,加速頁面渲染內容
9、盡量使用事件代理,避免直接事件綁定
四、Vue的SPA 如何優化加載速度
1.減少入口文件體積
2.靜態資源本地緩存
3.開啟Gzip壓縮
4.使用SSR,nuxt.js
五、移動端300ms延遲
由來:300毫米延遲解決的是雙擊縮放。雙擊縮放,手指在屏幕快速點擊兩次。safari瀏覽器就會將網頁縮放值原始比例。
由於用戶可以雙擊縮放或者是滾動的操作,當用戶點擊屏幕一次之后,瀏覽器並不會判斷用戶確實要打開至這個鏈接,還是想要進行雙擊操作
因次,safair瀏覽器就會等待300ms,用來判斷用戶是否在次點擊了屏幕
解決方案:1.禁用縮放,設置meta標簽 user-scalable=no
2.fastclick.js
原理:FastClick的實現原理是在檢查到touchend事件的時候,會通過dom自定義事件立即
發出click事件,並把瀏覽器在300ms之后真正的click事件阻止掉
fastclick.js還可以解決穿透問題
六、頁面的重構;在不改變外部行為的前提下,簡化結構、添加可讀性
服務器端
一、狀態碼:
2XX(成功處理了請求狀態)
200 服務器已經成功處理請求,並提供了請求的網頁
201 用戶新建或修改數據成功
202 一個請求已經進入后台
204 用戶刪除成功
3XX(每次請求使用的重定向不要超過5次)
304 網頁上次請求沒有更新,節省帶寬和開銷
4XX(表示請求可能出錯,妨礙了服務器的處理)
400 服務器不理解請求的語法
401 用戶沒有權限(用戶名,密碼輸入錯誤)
403 用戶得到授權(401相反),但是訪問被禁止
404 服務器找不到請求的網頁,
5XX(表示服務器在處理請求的時候發生內部錯誤)
500 服務器遇到錯誤,無法完成請求
503 服務器目前無法使用(超載或停機維護)
二、304的緩存原理(添加Etag標簽.last-modified) 304 網頁上次請求沒有更新,節省帶寬和開銷
1.服務器首先產生Etag,服務器可在稍后使用它來判斷頁面是否被修改。本質上,客戶端通過該記號傳回服務器要求服務器驗證(客戶端)緩存)
2.304是 HTTP的狀態碼,服務器用來標識這個文件沒有被修改,不返回內容,瀏覽器接受到這個狀態碼會去去找瀏覽器緩存的文件
3.流程:客戶端請求一個頁面A。服務器返回頁面A,並在A上加一個Tage客服端渲染該頁面,並把Tage也存儲在緩存中。客戶端再次請求頁面A
並將上次請求的資源和ETage一起傳遞給服務器。服務器檢查Tage.並且判斷出該頁面自上次客戶端請求之后未被修改。直接返回304
last-modified: 客服端請求資源,同時有一個last-modified的屬性標記此文件在服務器最后修改的時間
客服端第二次請求此url時,根據http協議。瀏覽器會向服務器發送一個If-Modified-Since報頭,
詢問該事件之后文件是否被修改,沒修改返回304
有了Last-Modified,為什么還要用ETag?
1、因為如果在一秒鍾之內對一個文件進行兩次更改,Last-Modified就會不正確(Last—Modified不能識別秒單位的修改)
2、某些服務器不能精確的得到文件的最后修改時間
3、一些文件也行會周期新的更改,但是他的內容並不改變(僅僅改變修改的事件),這個時候我們並不希望客戶端認為文件被修改,而重新Get
ETag,為什么還要用Last-Modified?
1、兩者互補,ETag的判斷的缺陷,比如一些圖片等靜態文件的修改
2、如果每次掃描內容都生成ETag比較,顯然要比直接比較修改時間慢的多。
ETag是被請求變量的實體值(文件的索引節,大小和最后修改的時間的Hash值)
1、ETag的值服務器端對文件的索引節,大小和最后的修改的事件進行Hash后得到的。
三、get/post的區別
1.get數據是存放在url之后,以?分割url和傳輸數據,參數之間以&相連; post方法是把提交的數據放在http包的Body中
2.get提交的數據大小有限制,(因為瀏覽器對url的長度有限制),post的方法提交的數據沒有限制
3.get需要request.queryString來獲取變量的值,而post方式通過request.from來獲取變量的值
4.get的方法提交數據,會帶來安全問題,比如登錄一個頁面,通過get的方式提交數據,用戶名和密碼就會出現在url上
四、http協議的理解
1.超文本的傳輸協議,是用於從萬維網服務器超文本傳輸到本地資源的傳輸協議
2.基於TCP/IP通信協議來傳遞數據(HTML,圖片資源)
3.基於運用層的面向對象的協議,由於其簡潔、快速的方法、適用於分布式超媒體信息系統
4.http請求信息request:
請求行(request line)、請求頭部(header),空行和請求數據四部分構成
請求行,用來說明請求類型,要訪問的資源以及所使用的HTTP版本.
請求頭部,用來說明服務器要使用的附加信息
空行,請求頭部后面的空行是必須的
請求數據也叫主體,可以添加任意的其他數據。
5.http相應信息Response
狀態行、消息報頭、空行和響應正文
狀態行,由HTTP協議版本號, 狀態碼, 狀態消息 三部分組成
消息報頭,用來說明客戶端要使用的一些附加信息
空行,消息報頭后面的空行是必須的
響應正文,服務器返回給客戶端的文本信息。
五、http和https
https:是以安全為目標的HTTP通道,簡單講是HTTP的安全版本,通過SSL加密
http:超文本傳輸協議。是一個客服端和服務器端請求和應答的標准(tcp),使瀏覽器更加高效,使網絡傳輸減少
五、http1.0 1.1 2.0的區別
長連接:HTTP1.0需要使用keep-alive參數來告知服務器建立一個長連接,而HTP1.1默認支持長連接
節約寬帶:HTTP1.1支持只發送一個header信息(不帶任何body信息)
host域(設置虛擬站點,也就是說,web server上的多個虛擬站點可以共享同一個ip端口):HTTP1.0沒有host域
1.http2采用的二進制文本傳輸數據,而非http1文本格式,二進制在協議的解析和擴展更好
2.數據壓縮:對信息頭采用了HPACK進行壓縮傳輸,節省了信息頭帶來的網絡流量
3.多路復用:一個連接可以並發處理多個請求
4.服務器推送:我們對支持HTTP2.0的web server請求數據的時候,服務器會順便把一些客戶端需要的資源一起推送到客戶端,免得客戶端再次創建連接發送請求到服務器端獲取。這種方式非常合適加載靜態資源
六、koa和express的區別
1.異步流程的控制。express采用callback來處理異步,koa2采用的是async/await
2.錯誤處理。express采用callback捕獲異常,對深層次的異常捕獲不了。koa采用try/catch
七、web緩存
1.web緩存就是存在於客戶端與服務器之間的一個副本、當你第一個發出請求后,緩存根據請求保存輸出內容的副本
2.緩存的好處
(1)減少不必要的請求
(2)降低服務器的壓力,減少服務器的消耗
(3)降低網絡延遲,加快頁面打開速度(直接讀取瀏覽器的數據)
八、常見的web安全及防護原理
1.sql注入原理:通郭sql命令插入到web表單遞交或者輸入活命,達到欺騙服務器執行的惡意sql命令
防范:1.對用戶輸入進行校驗
2.不適用動態拼接sql
2.XSS(跨站腳本攻擊):往web頁面插入惡意的html標簽或者js代碼。
舉例子:在論壇放置一個看是安全的鏈接,竊取cookie中的用戶信息
防范:1.盡量采用post而不使用get提交表單
2.避免cookie中泄漏用戶的隱式
3.CSRF(跨站請求偽裝):通過偽裝來自受信任用戶的請求
舉例子:黃軼老師的webapp音樂請求數據就是利用CSRF跨站請求偽裝來獲取QQ音樂的數據
防范:在客服端頁面增加偽隨機數,通過驗證碼
XSS和CSRF的區別:
1.XSS是獲取信息,不需要提前知道其他用戶頁面的代碼和數據包
2.CSRF代替用戶完成指定的動作,需要知道其他頁面的代碼和數據包
九、CDN(內容分發網絡)
1.盡可能的避開互聯網有可能影響數據傳輸速度和穩定性的瓶頸和環節。使內容傳輸的更快更穩定。
2.關鍵技術:內容存儲和分發技術中
3.基本原理:廣泛采用各種緩存服務器,將這些緩存服務器分布到用戶訪問相對的地區或者網絡中。當用戶訪問網絡時利用全局負載技術
將用戶的訪問指向距離最近的緩存服務器,由緩存服務器直接相應用戶的請求(全局負載技術)
十、TCP三次握手 (客服端和服務器端都需要確認各自可收發)
客服端發c起請求連接服務器端s確認,服務器端也發起連接確認客服端確認。
第一次握手:客服端發送一個請求連接,服務器端只能確認自己可以接受客服端發送的報文段
第二次握手: 服務端向客服端發送一個鏈接,確認客服端收到自己發送的報文段
第三次握手: 服務器端確認客服端收到了自己發送的報文段
十一、從輸入url到獲取頁面的完整過程 https://blog.csdn.net/samjustin1/article/details/52650520
1.查詢NDS(域名解析),獲取域名對應的IP地址
查詢瀏覽器緩存
2.瀏覽器與服務器建立tcp鏈接(三次握手)
3.瀏覽器向服務器發送http請求(請求和傳輸數據)
4.服務器接受到這個請求后,根據路經參數,經過后端的一些處理生成html代碼返回給瀏覽器
5.瀏覽器拿到完整的html頁面代碼開始解析和渲染,如果遇到外部的css或者js,圖片一樣的步驟
6.瀏覽器根據拿到的資源對頁面進行渲染,把一個完整的頁面呈現出來
十二、瀏覽器渲染原理及流程 DOM -> CSSOM -> render -> layout -> print
流程:解析html以及構建dom樹 -> 構建render樹 -> 布局render樹 -> 繪制render樹
概念:1.構建DOM樹: 渲染引擎解析HTML文檔,首先將標簽轉換成DOM樹中的DOM node(包括js生成的標簽)生成內容樹
2.構建渲染樹: 解析對應的css樣式文件信息(包括js生成的樣式和外部的css)
3.布局渲染樹:從根節點遞歸調用,計算每一個元素的大小,位置等。給出每個節點所在的屏幕的精准位置
4.繪制渲染樹:遍歷渲染樹,使用UI后端層來繪制每一個節點
重繪:當盒子的位置、大小以及其他屬性,例如顏色、字體大小等到確定下來之后,瀏覽器便把這些顏色都按照各自的特性繪制一遍,將內容呈現在頁面上
觸發重繪的條件:改變元素外觀屬性。如:color,background-color等
重繪是指一個元素外觀的改變所觸發的瀏覽器行為,瀏覽器會根據元素的新屬性重新繪制,使元素呈現新的外觀
注意:table及其內部元素需要多次計算才能確定好其在渲染樹中節點的屬性值,比同等元素要多發時間,要盡量避免使用table布局
重排(重構/回流/reflow): 當渲染書中的一部分(或全部)因為元素的規模尺寸,布局,隱藏等改變而需要重新構建,這就是回流。
每個頁面都需要一次回流,就是頁面第一次渲染的時候
重排一定會影響重繪,但是重繪不一定會影響重排
十三、為什么css放在頂部而js寫在后面
1.瀏覽器預先加載css后,可以不必等待HTML加載完畢就可以渲染頁面了
2.其實HTML渲染並不會等到完全加載完在渲染頁面,而是一邊解析DOM一邊渲染。
3.js寫在尾部,主要是因為js主要扮演事件處理的功能,一方面很多操作是在頁面渲染后才執行的。另一方面可以節省加載時間,使頁面能夠更加的加載,提高用戶的良好體驗
但是隨着JS技術的發展,JS也開始承擔頁面渲染的工作。比如我們的UI其實可以分被對待,把渲染頁面的js放在前面,時間處理的js放在后面
設計模式
一、觀察者模式:https://juejin.im/post/5a14e9edf265da4312808d86 https://juejin.im/post/5af05d406fb9a07a9e4d2799
在軟件開發設計中是一個對象(subject),維護一系列依賴他的對象(observer),當任何狀態發生改變自動通知他們。強依賴關系
簡單理解:數據發生改變時,對應的處理函數就會自動執行。一個Subjet,用來維護Observers,為某些event來通知(notify)觀察者
二、發布-訂閱者 有一個信息中介,過濾 耦合性低
它定義了一種一對多的關系,可以使多個觀察者對象對一個主題對象進行監聽,當這個主題對象發生改變時,依賴的所有對象都會被通知到。
兩者的區別:
1.觀察者模式中,觀察者知道Subject ,兩者是相關聯的,而發發布訂閱者只有通過信息代理進行通信
2.在發布訂閱模式中,組件式松散耦合的。正好和觀察者模式相反。
3.觀察者大部分是同步的,比如事件的觸發。Subject就會調用觀察者的方法。而發布訂閱者大多數是異步的()
4.觀察者模式需要在單個應用程序地址空間中實現,而發布訂閱者更像交叉應用模式。
1004001111
數據結構和算法
一、兩個棧實現一個隊列,兩個隊列實現一個棧 https://www.cnblogs.com/MrListening/p/5697459.html
二、紅黑樹(解決二叉樹依次插入多個節點時的線型排列) https://juejin.im/post/5a27c6946fb9a04509096248
三、最小棧的實現(查找最小元素,用兩個棧配合棧內元素的下標)https://juejin.im/post/5a2ff8c651882533d0230a85
四、十大排序
1.冒泡排序:重復走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把它們交換過來。
實現過程:1.比較相鄰的元素。如果第一個比第二個大,就交換他們兩個
2.對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對,這樣在最后的元素應該會是最大的數
3.針對所有的元素重復以上的步驟,除了最后一個
4.重復步驟1-3,直到排序完成。
2.選擇排序:首先在未排序序列中找到最小值,放在排序序列的起始位置,然后,在從剩下未排序元素中繼續尋找最小值,然后放在與排序序列的末尾
實現過程:
3.插入排序:構建有序序列,對於未排序數據,在已排序序列中沖后向前掃描,找到相應位置並插入
實現過程:1.從第一個元素開始,該元素可以認為已經被排序
2.取出下一個元素,在已排序的元素序列中沖后向前掃描
3.如果該元素(以排序)大於新元素,將元素向后移一位
4.桶排序:將數據分布到有限數量的桶里,每個桶在分別排序
1.快速排序:快速排序使用分治法把一個串(list)分為兩個子串(sub-lists).具體算法實現
實現過程:1.從數組中挑出一個元素,成為一個基准
2.重新排列數組,所有元素比基准小的擺在基准前面,所有元素比基准大的擺在基准后面(相同的可以擺在一邊)
這個分區退出之后,該基准就處於數列的中間位置。成為分區操作。
3.遞歸的把小於基准值的子數列和大於基准值元素的子數列排序
算法實現: function quickSort (arr) {
if (arr.length <= 1) {return arr}
var destIndex = Math.floor(arr.length/2)
var left = [], right = [];
var dest = arr.splice(destIndex,1)[0];
for (var i =0;i<arr.length;i++){
if (arr[i]<dest) {
left.push(arr[i])
} else {
right.push(arr[i]) }
return quickSort(left).concat([dest],quickSort(right)
2.堆排序:利用對這種數據結構所涉及的一種排序算法,堆積是一個近乎完全二叉樹的結構,並同時滿足堆積的性質:即子節點的鍵值或索引總是小於(或大於)它的父節點。
實現過程:1.
五、數組去重 https://juejin.im/post/5aed6110518825671b026bed#heading-6
1.雙重循環
var arr=[1,3,6,1,3,5];
var newArr=[];
for(var i=0;i<arr.length;i++){
var flag=true;
for(var j=0;j<newArr.length;j++){
if(newArr[j]===arr[i]){
flag=false;
break
}
}
if(flag){
newArr.push(arr[i])
}
}
console.log(newArr)
2.indexOf
var arr=[1,3,6,1,3,5];
var newArr=[];
for(var i=0;i<arr.length;i++){
if(newArr.indexOf(arr[i])==-1){
newArr.push(arr[i]);
}
}
console.log(newArr)
3.數組排序去重 最快你Olong
var arr=[1,3,6,1,3,5];
var newArr=[];
arr=arr.sort();
for(var i=0;i<arr.length;i++){
if(arr[i]!=arr[i-1]){
newArr.push(arr[i]);
}
}
console.log(newArr);
4.ES6語法Set去重
var arr=[1,3,6,1,3,5];
console.log([...new Set(arr)]);
或者
console.log(Array.from(new Set(arr)));
六、字符串
判斷回文字符串:(遞歸的思想)
1.字符串分隔,倒轉,聚合[...obj].reverse().join('')
2.字符串頭部和尾部,逐次向中間檢測
實現:function isPalindrome(line) {
line += '';
for (var i=0,j=line.length-1;i<j;i++,j--) {
if (line.chartAt(i) !== line.chartAt(j) {
return false
}
3.遞歸
七、二分查找(有序數組的查找)
// 二分查找可以解決已排序數組的查找問題,即只要數組中包含T(要查找的值),那么通過不斷的縮小包含T的數據范圍,就可以最終要找到的數
// (1) 一開始,數據范圍覆蓋整個數組。
// (2) 將數組的中間項與T進行比較,如果T比數組的中間項小,則到數組的前半部分繼續查找,反之,則到數組的后半部分繼續查找。
// (3) 就這樣,每次查找都可以排除一半元素,相當於范圍縮小一半。這樣反復比較,反復縮小范圍,最終會在數組中找到T
代碼實現:function binarySearch (data, dest, start, end){
var end = end || data.length-1;
var start = start || 0;
var m = Math.floor((start+end)/2);
if (dest<data[m]){
return binarySearch(data, dest,0, m-1)
} else {
return binarySearch(data, dest, m+1, end)
}}
return false
手寫代碼
一、動手實現一個bind(原理通過apply,call)
一句話概括:1.bind()返回一個新函數,並不會立即執行。
2.bind的第一個參數將作為他運行時的this,之后的一系列參數將會在傳遞的實參前傳入作為他的參數
3.bind返回函數作為構造函數,就是可以new的,bind時指定的this值就會消失,但傳入的參數依然生效
Function.prototype.bind = function (obj, arg) {
var arg = Array.prototype.slice.call(arguments, 1);
var context = this;
var bound = function (newArg) {
arg = arg.concat(Array.prototype.slice.call(newArg);
return context.apply(obj, arg)
}
var F = function () {} // 在new一個bind會生成新函數,必須的條件就是要繼承原函數的原型,因此用到寄生繼承來完成我們的過程
F.prototype = context.prototype;00.......................
bound.prototype = new F();
return bound;
}
二、 AJAX (異步的javascript和xml)
ajax的原理:相當於在用戶和服務器之間加一個中間層(ajax引擎),使用戶操作與服務器響應異步化。
優點:在不刷新整個頁面的前提下與服務器通信維護數據。不會導致頁面的重載
可以把前端服務器的任務轉嫁到客服端來處理,減輕服務器負擔,節省寬帶
劣勢:不支持back。對搜索引擎的支持比較弱;不容易調試
怎么解決呢?通過location.hash值來解決Ajax過程中導致的瀏覽器前進后退按鍵失效,
解決以前被人常遇到的重復加載的問題。主要比較前后的hash值,看其是否相等,在判斷是否觸發ajax
function getData(url) {
var xhr = new XMLHttpRequest(); // 創建一個對象,創建一個異步調用的對象
xhr.open('get', url, true) // 設置一個http請求,設置請求的方式,url以及驗證身份
xhr.send() //發送一個http請求
xhr.onreadystatechange = function () { //設置一個http請求狀態的函數
if (xhr.readyState == 4 && xhr.status ==200) {
console.log(xhr.responseText) // 獲取異步調用返回的數據
}
}
}
Promise(getData(url)).resolve(data => data)
AJAX狀態碼:0 - (未初始化)還沒有調用send()方法
1 - (載入)已調用send方法,正在發送請求
2 - (載入完成呢)send()方法執行完成
3 - (交互)正在解析相應內容
4 - (完成)響應內容解析完成,可以在客戶端調用了
三、函數節流(throttle)
function throttle (func, wait) {
var timeout;
var previous = 0;
return function () {
context = this;
args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context,args)
}, wait);
}
}
}
}
四、函數防抖(dobounce)
function debounce (func, wait) {
var timeout;
return function() {
var context = this;
var args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context,args)
}, wait);
}
}
五、實現一個函數clone,可以對JavaScript中的5種主要的數據類型(包括Number、String、Object、Array、Boolean)進行值復制
Object.prototype.clone = function() {
var newObject = this.constructor === Array ? [] : {} //對象的深拷貝 獲取對應的構造函數 [] 或者 {}
for (let e in this) { //遍歷對象的屬性 in this[e]
newObject[e] = typeof this[e] === 'object' ? this[e].clone() : this[e] //對象中的屬性如果還是對象 那就繼續遞歸 否則就返回基本的數據類型
}
return newObject
}
六、實現一個簡單的Promise https://juejin.im/post/5b2f02cd5188252b937548ab
class Promise {
constructor (executor) { // executor里面有兩個參數,一個叫resolve(成功),一個叫reject(失敗)。
this.status = 'pending',
this.value = undefined;
this.reason = undefined;
// 成功存放的數組
this.onResolvedCallbacks = [];
// 失敗存放法數組
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status == 'pending') {
this.status = 'resolve';
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status == 'pending') {
this.status = 'reject';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then (onFullFilled,onRejected) {
if (this.status == 'resolved') {
onFullFilled(this.value)
}
if (this.status == 'rejectd') {
onRejected(this.reason);
}
if (this.status == 'pending') {
this.onResolvedCallbacks.push(()=>{
onFullFilled(this.value);
})
this.onRejectedCallbacks.push(()=> {
onRejected(this.reason);
})
}
}
}
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello world')
}, 1000);
})
p.then((data) =>{
console.log(data)
},(err) =>{
console.log(err);
})
七、發布訂閱者模式(觀察者模式)
var event = {}; // 發布者
event.clientList = [] //發布者的緩存列表
event.listen = function (fn) { // 增加訂閱者函數
this.clientList.push(fn)
}
event.trigger = function () { // 發布信息
for (var i =0;i<this.clientList.length;i++) {
var fn = this.clientList[i];
fn.apply(this, arguments);
}
}
event.listen (function(time) {
console.log('正式上班時間為:' +time)
})
event.trigger ('2018/7')
八、手動寫一個node服務器
const http = require('http');
const fs = require('fs');
const server = http.createServer((req,res) => {
if (reu.url == '/') {
const indexFile = fs.createReadStream('./index.html')
req.writeHead(200,{'context-Type':'text/html;charset = utf8})
indexFile.pipe(res)
}
server.listen(8080)