javaScript基礎

1.繼承javascript

一、原型鏈繼承,將父類的實例做爲子類的原型,他的特色是實例是子類的實例也是父類的實例,父類新增的原型方法/屬性,子類都可以訪問,而且原型鏈繼承簡單易於實現,缺點是來自原型對象的全部屬性被全部實例共享,沒法實現多繼承,沒法向父類構造函數傳參。css

二、構造繼承,使用父類的構造函數來加強子類實例,即複製父類的實例屬性給子類,構造繼承能夠向父類傳遞參數,能夠實現多繼承,經過call多個父類對象。可是構造繼承只能繼承父類的實例屬性和方法,不能繼承原型屬性和方法,沒法實現函數服用,每一個子類都有父類實例函數的副本,影響性能前端

三、實例繼承,爲父類實例添加新特性,做爲子類實例返回,實例繼承的特色是不限制調用方法,無論是new 子類()仍是子類()返回的對象具備相同的效果,缺點是實例是父類的實例,不是子類的實例,不支持多繼承vue

四、拷貝繼承:特色:支持多繼承,缺點:效率較低,內存佔用高(由於要拷貝父類的屬性)沒法獲取父類不可枚舉的方法(不可枚舉方法,不能使用for in 訪問到)java

五、組合繼承:經過調用父類構造,繼承父類的屬性並保留傳參的優勢,而後經過將父類實例做爲子類原型,實現函數複用node

六、寄生組合繼承:經過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點webpack

2.this指向
(1).this 指向有哪幾種css3

1.默認綁定:全局環境中,this默認綁定到window。nginx

2.隱式綁定:通常地,被直接對象所包含的函數調用時,也稱爲方法調用,this隱式綁定到該直接對象。程序員

3.隱式丟失:隱式丟失是指被隱式綁定的函數丟失綁定對象,從而默認綁定到window。顯式綁定:經過call()、apply()、bind()方法把對象綁定到this上,叫作顯式綁定。

4.new綁定:若是函數或者方法調用以前帶有關鍵字new,它就構成構造函數調用。對於this綁定來講,稱爲new綁定。

構造函數一般不使用return關鍵字,它們一般初始化新對象,當構造函數的函數體執行完畢時,它會顯式返回。在這種狀況下,構造函數調用表達式的計算結果就是這個新對象的值。
若是構造函數使用return語句但沒有指定返回值,或者返回一個原始值,那麼這時將忽略返回值,同時使用這個新對象做爲調用結果。
若是構造函數顯式地使用return語句返回一個對象,那麼調用表達式的值就是這個對象。

(2).改變函數內部 this 指針的指向函數(bind,apply,call的區別)

1.apply:調用一個對象的一個方法,用另外一個對象替換當前對象。例如:B.apply(A, arguments);即A對象應用B對象的方法。

2.call:調用一個對象的一個方法,用另外一個對象替換當前對象。例如:B.call(A, args1,args2);即A對象調用B對象的方法。

3.bind除了返回是函數之外,它的參數和call同樣。

(3).箭頭函數

1.箭頭函數沒有this,因此須要經過查找做用域鏈來肯定this的值,這就意味着若是箭頭函數被非箭頭函數包含,this綁定的就是最近一層非箭頭函數的this,
2.箭頭函數沒有本身的arguments對象,可是能夠訪問外圍函數的arguments對象
3.不能經過new關鍵字調用,一樣也沒有new.target值和原型

3.數據類型
(1).基本數據類型
Undefined、Null、Boolean、Number 、String、Symbol
(2).symbol

1.語法:
// 不能用 new
let s = Symbol()

// 能夠接受一個字符串做爲參數,表示對 Symbol 實例的描述,主要是爲了在控制檯顯示,或者轉爲字符串時,比較容易區分。
let s1 = Symbol('foo');
let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
2.做用:定義一個獨一無二的值

1.用做對象的屬性名

1.不會出如今for...in、for...of循環中,也不會被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
2.Object.getOwnPropertySymbols()方法,能夠獲取指定對象的全部 Symbol 屬性名。該方法返回一個數組,成員是當前對象的全部用做屬性名的 Symbol 值。
3.Reflect.ownKeys()方法能夠返回全部類型的鍵名,包括常規鍵名和 Symbol 鍵名。
2.用於定義一組常量
log.levels = {
DEBUG: Symbol('debug'),
INFO: Symbol('info'),
WARN: Symbol('warn')
};
3.類型轉換:
1.轉成字符串
String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
2.轉成布爾值
Boolean(sym)
3.不能轉成數字
4.不能與其餘類型的值進行運算
let sym = Symbol('My symbol');
"your symbol is " + sym
// TypeError: can't convert symbol to string
your symbol is ${sym}
// TypeError: can't convert symbol to string

4.屬性:Symbol.prototype.description

5.Symbol.for(),Symbol.keyFor()

1.在全局環境中登記 Symbol 值。以後不會再重複生成

(3).如何判斷類型
typeof(),instanceof,Object.prototype.toString.call()

1.typeof操做符

1."undefined"——若是這個值未定義;
2."boolean"——若是這個值是布爾值;
3."string"——若是這個值是字符串;
4."number"——若是這個值是數值;
5."object"——若是這個值是對象或 null;
6."function"——若是這個值是函數。
7."symbol"——es6新增的symbol類型

2.instanceof:用來判斷對象是否是某個構造函數的實例。會沿着原型鏈找的

3.Object.prototype.toString.call()
var toString = Object.prototype.toString;

toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]
toString.call([]); // [Object Array]
toString.call(new Number) // [object Number]
toString.call(true) // [object Boolean]
toString.call(function(){}) // [object Function]
toString.call({}) // [object Object]
toString.call(new Promise(() => {})) // [object Promise]

toString.call(new Map) // [object Map]
toString.call(new RegExp) // [object RegExp]
toString.call(Symbol()) // [object Symbol]
toString.call(function *a(){}) // [object GeneratorFunction]
toString.call(new DOMException()) // [object DOMException]
toString.call(new Error) // [object Error]

toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]

// 還有 WeakMap、 WeakSet、Proxy 等

(4).判斷是不是數組

1.Array.isArray(arr)
2.Object.prototype.toString.call(arr) === '[Object Array]'
3.arr instanceof Array
4.array.constructor === Array

(5).字符串轉數字
parseInt(string, radix)
4.CallBack Hell
大腦對於事情的計劃方式是線性的、阻塞的、單線程的語義,可是回調錶達異步流 程的方式是非線性的、非順序的,這使得正確推導這樣的代碼難度很大。難於理解的代碼 是壞代碼,會致使壞 bug。咱們須要一種更同步、更順序、更阻塞的的方式來表達異步,就像咱們的大腦同樣。
也是更重要的一點,回調會受到控制反轉的影響,由於回調暗中把控制權交給第三 方(一般是不受你控制的第三方工具!)來調用你代碼中的 continuation。能夠發明一些特定邏輯來解決這些信任問題,可是其難度高於應有的水平,可能會產生更 笨重、更難維護的代碼,而且缺乏足夠的保護,其中的損害要直到你受到 bug 的影響纔會 被發現。
咱們須要一個通用的方案來解決這些信任問題。無論咱們建立多少回調,這一方案都應可 以複用,且沒有重複代碼的開銷。
(1).Promise 爲何以及如何用於解決控制反轉信任問題
Promise 的實現能夠看這裏
Promise 這種模式經過可信任的語義把回調做爲參數傳遞,使得這種行爲更可靠更合理。 經過把回調的控制反轉反轉回來,咱們把控制權放在了一個可信任的系統(Promise)中, 這種系統的設計目的就是爲了使異步編碼更清晰。Promise 並無擯棄回調,只是把回調的安排轉交給了一個位於咱們和其餘工具之間的可信任 的中介機制。
調用回調過早;

這個問題主要就是擔憂代碼是否會引入相似 Zalgo 這樣的反作用(參見第 2 章)。在這類問 題中,一個任務有時同步完成,有時異步完成,這可能會致使競態條件。
根據定義,Promise 就沒必要擔憂這種問題,由於即便是當即完成的 Promise(相似於 new Promise(function(resolve){ resolve(42); }))也沒法被同步觀察到。
也就是說,對一個 Promise 調用 then(..) 的時候,即便這個 Promise 已經決議,提供給 then(..) 的回調也總會被異步調用(對此的更多討論,請參見 1.5 節)。

調用回調過晚(或不被調用);

和前面一點相似,Promise 建立對象調用 resolve(..) 或 reject(..) 時,這個 Promise 的 then(..) 註冊的觀察回調就會被自動調度。能夠確信,這些被調度的回調在下一個異步事 件點上必定會被觸發(參見 1.5 節)。

回調未調用

首先,沒有任何東西(甚至 JavaScript 錯誤)能阻止 Promise 向你通知它的決議(若是它 決議了的話)。若是你對一個 Promise 註冊了一個完成回調和一個拒絕回調,那麼 Promise 在決議時老是會調用其中的一個。
可是,若是 Promise 自己永遠不被決議呢?即便這樣,Promise 也提供瞭解決方案,其使用 了一種稱爲競態的高級抽象機制:
調用回調次數過多;
Promise 的定義方式使得它只能被決議一次。若是出於某種 緣由,Promise 建立代碼試圖調用 resolve(..) 或 reject(..) 屢次,或者試圖二者都調用, 那麼這個 Promise 將只會接受第一次決議,並默默地忽略任何後續調用。
因爲 Promise 只能被決議一次,因此任何經過 then(..) 註冊的(每一個)回調就只會被調 用一次。
未能傳遞所需的環境和參數;

Promise 至多隻能有一個決議值(完成或拒絕)。
若是你沒有用任何值顯式決議,那麼這個值就是 undefined,這是 JavaScript 常見的處理方 式。但無論這個值是什麼,不管當前或將來,它都會被傳給全部註冊的(且適當的完成或 拒絕)回調。

吞掉可能出現的錯誤和異常。

若是拒絕一個 Promise 並給出一個理由(也就是一個出錯消息),這個值就會被傳給拒絕回調

(2).promise、generator、async/await

promise

優勢:解決了回調地獄的問題
缺點:沒法取消 Promise ,錯誤須要經過回調函數來捕獲

generator

生成器內部的代碼是以天然的同步 / 順序方式表達任務的一系列步驟

async/await

優勢:代碼清晰,不用像 Promise 寫一大堆 then 鏈,處理了回調地獄的問題

缺點:await 將異步代碼改形成同步代碼,若是多個異步操做沒有依賴性而使用 await 會致使性能上的下降。

5.加載
(1).異步加載js的方法

defer:只支持IE若是您的腳本不會改變文檔的內容,可將 defer 屬性加入到<script>標籤中,以便加快處理文檔的速度。由於瀏覽器知道它將可以安全地讀取文檔的剩餘部分而不用執行腳本,它將推遲對腳本的解釋,直到文檔已經顯示給用戶爲止。

async:HTML5 屬性,僅適用於外部腳本;而且若是在IE中,同時存在defer和async,那麼defer的優先級比較高;腳本將在頁面完成時執行。

(2).圖片的懶加載和預加載

預加載:提早加載圖片,當用戶須要查看時可直接從本地緩存中渲染。
懶加載:懶加載的主要目的是做爲服務器前端的優化,減小請求數或延遲請求數。

兩種技術的本質:二者的行爲是相反的,一個是提早加載,一個是遲緩甚至不加載。懶加載對服務器前端有必定的緩解壓力做用,預加載則會增長服務器前端壓力。
6.事件
(1).事件流
HTML中與javascript交互是經過事件驅動來實現的,例如鼠標點擊事件onclick、頁面的滾動事件onscroll等等,能夠向文檔或者文檔中的元素添加事件偵聽器來預訂事件。想要知道這些事件是在何時進行調用的,就須要瞭解一下「事件流」的概念。
什麼是事件流:事件流描述的是從頁面中接收事件的順序,DOM2級事件流包括下面幾個階段。

事件捕獲階段
處於目標階段
事件冒泡階段

IE只支持事件冒泡。
(2).什麼是事件監聽
addEventListener()方法,用於向指定元素添加事件句柄,它能夠更簡單的控制事件,語法爲
element.addEventListener(event, function, useCapture);

第一個參數是事件的類型(如 "click" 或 "mousedown").

第二個參數是事件觸發後調用的函數。

第三個參數是個布爾值用於描述事件是冒泡仍是捕獲。該參數是可選的。

target.addEventListener(type, listener, options: EventListenerOptions);
target.addEventListener(type, listener, useCapture: boolean);
target.addEventListener(type, listener, useCapture: boolean, wantsUntrusted: boolean ); // Gecko/Mozilla only
interface EventListenerOptions {
capture?: boolean // 表示 listener 會在該類型的事件捕獲階段傳播到該 EventTarget 時觸發
once?: boolean // 表示 listener 在添加以後最多隻調用一次。若是是 true, listener 會在其被調用以後自動移除
passive?: boolean // 設置爲true時,表示 listener 永遠不會調用 preventDefault()。若是 listener 仍然調用了這個函數,客戶端將會忽略它並拋出一個控制檯警告
}
(3). mouseover 和 mouseenter 的區別

mouseover:當鼠標移入元素或其子元素都會觸發事件,因此有一個重複觸發,冒泡的過程。對應的移除事件是mouseout
mouseenter:當鼠標移除元素自己(不包含元素的子元素)會觸發事件,也就是不會冒泡,對應的移除事件是mouseleave

(4). 事件委託以及冒泡原理
簡介:事件委託指的是,不在事件的發生地(直接dom)上設置監聽函數,而是在其父元素上設置監聽函數,經過事件冒泡,父元素能夠監聽到子元素上事件的觸發,經過判斷事件發生元素DOM的類型,來作出不一樣的響應。
舉例:最經典的就是ul和li標籤的事件監聽,好比咱們在添加事件時候,採用事件委託機制,不會在li標籤上直接添加,而是在ul父元素上添加。
好處:比較合適動態元素的綁定,新添加的子元素也會有監聽函數,也能夠有事件觸發機制。
(5). 事件代理在捕獲階段的實際應用
能夠在父元素層面阻止事件向子元素傳播,也可代替子元素執行某些操做。
7.跨域
(1).CORS
CORS(Cross-Origin Resource Sharing,跨源資源共享) 背後的基本思想,就是使用自定義的 HTTP 頭部 讓瀏覽器與服務器進行溝通。
好比一個簡單的使用 GET 或 POST 發送的請求,它沒有自定義的頭部,而主體內容是 text/plain。在 發送該請求時,須要給它附加一個額外的 Origin 頭部,其中包含請求頁面的源信息(協議、域名和端 口),以便服務器根據這個頭部信息來決定是否給予響應。下面是 Origin 頭部的一個示例:
Origin: http://www.nczonline.net
若是服務器認爲這個請求能夠接受,就在 Access-Control-Allow-Origin 頭部中回發相同的源
信息(若是是公共資源,能夠回發"*")。例如:
Access-Control-Allow-Origin: http://www.nczonline.net
若是沒有這個頭部,或者有這個頭部但源信息不匹配,瀏覽器就會駁回請求。正常狀況下,瀏覽器 會處理請求。注意,請求和響應都不包含 cookie 信息。
(2).IE
微軟在 IE8 中引入了 XDR(XDomainRequest)類型。如下是 XDR 與 XHR 的一些不一樣之 處。

cookie 不會隨請求發送,也不會隨響應返回。
只能設置請求頭部信息中的 Content-Type 字段。
不能訪問響應頭部信息。
只支持GET和POST請求。

(3).其餘瀏覽器
經過 XMLHttpRequest 對象實現了對 CORS 的原生支持

不能使用 setRequestHeader()設置自定義頭部。
不能發送和接收 cookie。
調用 getAllResponseHeaders()方法總會返回空字符串。

(4).JSONP
微信公衆號:世界上有意思的事

function handleResponse(response){
alert("You’re at IP address " + response.ip + ", which is in " +
response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);

JSON只支持get,由於script標籤只能使用get請求;
JSONP須要後端配合返回指定格式的數據。

(5). 代理
起一個代理服務器,實現數據的轉發
(6).利用 iframe

window.postMessage
Cross Frame(aba)
window.name

lovelock.coding.me/javascript/…
(7).window.postMessage
只支持到IE8及以上的IE瀏覽器,其餘現代瀏覽器固然沒有問題。
(8). child 與 parent 通訊
不受同源策略的限制

給接收數據的一方添加事件綁定:addEventListener('message', receiveMessage);

發送數據的一方拿到接收數據一方的window:targetWindow.postMessage("Welcome to unixera.com", "http://iframe1.unixera.com");

(9).chilid 與 child 通訊
有跨域問題,只適合站內不一樣子域間的通訊(設置document.domain爲同一級域名)
(10).Cross Frame
這是一個通用的方法,簡單來講是A iframe包含B iframe,在B iframe中調用了相關的接口,完成調用以後獲取到結果,location.href到和A iframe位於同一個域的C iframe,在C iframe中調用A iframe中定義的方法,將B iframe中獲取的結果做爲參數傳到要跳轉的url後,在C iframe中經過location.search變量來獲取變量。

(11).window.name
window對象的name屬性是一個很特殊的屬性,在設定了window.name以後,執行location.href跳轉,window.name屬性仍然不會發生變化,能夠經過這種方式實現變量的傳遞。
8.Ajax
(1).實現一個Ajax
微信公衆號:世界上有意思的事

var xhr = new XMLHttpRequest()
// 必須在調用 open()以前指定 onreadystatechange 事件處理程序才能確保跨瀏覽器兼容性
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status ==== 304) {
console.log(xhr.responseText)
} else {
console.log('Error:' + xhr.status)
}
}
}
// 第三個參數表示異步發送請求
xhr.open('get', '/api/getSth', true)
// 參數爲做爲請求主體發送的數據
xhr.send(null)
(2).Ajax狀態

未初始化。還沒有調用 open()方法。
啓動。已經調用 open()方法,但還沒有調用 send()方法。
發送。已經調用 send()方法,但還沒有接收到響應。
接收。已經接收到部分響應數據。
完成。已經接收到所有響應數據,並且已經能夠在客戶端使用了。

(3).將原生的 ajax 封裝成 promise
微信公衆號:世界上有意思的事

const ajax = (url, method, async, data) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
// 已經接收到所有響應數據,並且已經能夠在客戶端使用了
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
} else if (xhr.status > 400) {
reject('發生錯誤')
}
}
}
xhr.open(url, method, async)
xhr.send(data || null)
})
}
9.垃圾回收
找出那些再也不繼續使用的變 量,而後釋放其佔用的內存。爲此,垃圾收集器會按照固定的時間間隔(或代碼執行中預約的收集時間), 週期性地執行這一操做。
(1).標記清除
先全部都加上標記,再把環境中引用到的變量去除標記。剩下的就是沒用的了
(2).引用計數
跟蹤記錄每 個值被引用的次數。清除引用次數爲0的變量
⚠️會有循環引用問題 。循環引用若是大量存在就會致使內存泄露。
10.eval是什麼
eval 方法就像是一個完整的 ECMAScript 解析器,它只接受一個參數,即要執行的 ECMAScript (或JavaScript) 字符串

1.性能差:引擎沒法在編譯時對做用域查找進行優化

1.JavaScript 引擎會在編譯階段進行數項的性能優化。其中有些優化依賴於可以根據代碼的 詞法進行靜態分析,並預先肯定全部變量和函數的定義位置,才能在執行過程當中快速找到 標識符。
2.沒法在詞法分析階段明確知道 eval(..) 會接收到什麼代碼,這些代碼會 如何對做用域進行修改,也沒法知道傳遞給 with 用來建立新詞法做用域的對象的內容到底 是什麼。最悲觀的狀況是若是出現了 eval(..) 或 with,全部的優化可能都是無心義的,所以最簡 單的作法就是徹底不作任何優化。

2.欺騙做用域:但在嚴格模式的程序中,eval(..) 在運行時有其本身的詞法做用域,意味着其 中的聲明沒法修改所在的做用域。

11.監聽對象屬性的改變
(一).ES5 中
微信公衆號:世界上有意思的事

Object.defineProperty(user,'name',{
set:function(key,value){
// 這也是 Vue 的原理
}
})
(二). ES6 中
微信公衆號:世界上有意思的事

var user = new Proxy({}, {
set:function(target,key,value,receiver){

}
})
能夠監聽動態增長的屬性。例如 user.id = 1
12.實現一個私有變量

1.配置屬性
obj={
name: 'xujiahui',
getName:function(){
return this.name
}
}
object.defineProperty(obj,"name",{
//不可枚舉不可配置

});

2.代碼

微信公衆號:世界上有意思的事

function product(){
var name='xujiahui';
this.getName=function(){
return name;
}
}
var obj=new product();
13.操做符
(1).==和===、以及Object.is的區別

1.==

1.會進行強制類型轉換(!=也是)

2.在轉換不一樣的數據類型時,相等和不相等操做符遵循下列基本規則:

3.若是有一個操做數是布爾值,則在比較相等性以前先將其轉換爲數值——false 轉換爲 0,而true 轉換爲 1

4.若是一個操做數是字符串,另外一個操做數是數值,在比較相等性以前先將字符串轉換爲數值;

5.若是一個操做數是對象,另外一個操做數不是,則調用對象的 valueOf()方法,用獲得的基本類型值按照前面的規則進行比較; 這兩個操做符在進行比較時則要遵循下列規則。

6.null 和 undefined 是相等的。

7.要比較相等性以前,不能將 null 和 undefined 轉換成其餘任何值。

8.若是有一個操做數是 NaN,則相等操做符返回 false,而不相等操做符返回 true。重要提示⚠️:即便兩個操做數都是 NaN,相等操做符也返回 false;由於按照規則,NaN 不等於 NaN。

9.若是兩個操做數都是對象,則比較它們是否是同一個對象。若是兩個操做數都指向同一個對象,則相等操做符返回 true;不然,返回 false。

2.===:全等於,不轉換

3.Object.is

1.也不會進行強制類型轉換。
2.與===有如下幾點不一樣:

1.+0===-0,Object.is(+0, -0)爲 false
2.NaN !== NaN,Object.is(NaN, NaN)爲 true

(2).new 操做符作了哪些事情
用 new 操做符調用構造函數實際上會經歷如下 4 個步驟:

1.建立一個新對象;
2.將構造函數的做用域賦給新對象(所以 this 就指向了這個新對象);
3.執行構造函數中的代碼(爲這個新對象添加屬性);
4.返回新對象。
5.將構造函數的prototype關聯到實例的proto

14.數組
(1).數組經常使用方法
push(),pop(),shift(),unshift(),splice(),sort(),reverse(),map()等
(2).數組去重
要注意的是對象咋去重

1.雙重循環
每次插入一個元素的時候都和前面的每一個元素比較一下
var array = [1, 1, '1', '1'];

function unique(array) {
// res用來存儲結果
var res = [];
for (var i = 0, arrayLen = array.length; i < arrayLen; i++) {
for (var j = 0, resLen = res.length; j < resLen; j++ ) {
if (array[i] === res[j]) {
break;
}
}
// 若是array[i]是惟一的,那麼執行完循環,j等於resLen
if (j === resLen) {
res.push(array[i])
}
}
return res;
}

console.log(unique(array)); // [1, "1"]

2.indexOf
原理和雙重循環是同樣的
var array = [1, 1, '1'];

function unique(array) {
var res = [];
for (var i = 0, len = array.length; i < len; i++) {
var current = array[i];
if (res.indexOf(current) === -1) {
res.push(current)
}
}
return res;
}

console.log(unique(array));

3.排序後去重
對於排好序的數組,能夠將每一個元素與前一個比較
var array = [1, 1, '1'];

function unique(array) {
var res = [];
var sortedArray = array.concat().sort();
var seen;
for (var i = 0, len = sortedArray.length; i < len; i++) {
// 若是是第一個元素或者相鄰的元素不相同
if (!i || seen !== sortedArray[i]) {
res.push(sortedArray[i])
}
seen = sortedArray[i];
}
return res;
}

console.log(unique(array));

4.Object 鍵值對
把每個元素存成 object 的 key。例如 ['a'],存成{'a': true}
var array = [1, 2, 1, 1, '1'];

function unique(array) {
var obj = {};
return array.filter(function(item, index, array){
return obj.hasOwnProperty(item) ? false : (obj[item] = true)
})
}

console.log(unique(array)); // [1, 2]
咱們能夠發現,是有問題的,由於 1 和 '1' 是不一樣的,可是這種方法會判斷爲同一個值,這是由於對象的鍵值只能是字符串,因此咱們能夠使用 typeof item + item 拼成字符串做爲 key 值來避免這個問題:
var array = [1, 2, 1, 1, '1'];

function unique(array) {
var obj = {};
return array.filter(function(item, index, array){
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}

console.log(unique(array)); // [1, 2, "1"]
然而,即使如此,咱們依然沒法正確區分出兩個對象,好比 {value: 1} 和 {value: 2},由於 typeof item + item 的結果都會是 object[object Object],不過咱們能夠使用 JSON.stringify 將對象序列化:
var array = [{value: 1}, {value: 1}, {value: 2}];

function unique(array) {
var obj = {};
return array.filter(function(item, index, array){
console.log(typeof item + JSON.stringify(item))
return obj.hasOwnProperty(typeof item + JSON.stringify(item)) ? false : (obj[typeof item + JSON.stringify(item)] = true)
})
}

console.log(unique(array)); // [{value: 1}, {value: 2}]

5.ES6 Set去重
function unique(array) {
return Array.from(new Set(array));
}
function unique(array) {
return [...new Set(array)];
}

6.ES6 Map
function unique (arr) {
const seen = new Map()
return arr.filter((a) => !seen.has(a) && seen.set(a, 1))
}

3、高級技巧
1.防抖節流
(1).節流
在 n 秒內只會執行一次,因此節流會稀釋函數的執行頻率
(2). 防抖
按最後一次算。好比說「中止輸入5s後才發送請求」
3.數組展開

1.遞歸

微信公衆號:世界上有意思的事

function flat1 (arr) {
let result = []
arr.forEach(element => {
if (Array.isArray(element)) {
result = result.concat(flat1(element))
} else {
result.push(element)
}
});
return result
}

2.toString

function flat2 (arr) {
// 有缺陷,toString 後沒法保持以前的類型
return arr.toString().split(',')
}

3.reduce

微信公衆號:世界上有意思的事

function flat3 (arr) {
// 本質和 flat1 同樣的,都是遞歸
return arr.reduce((pre, next) => {
return pre.concat(Array.isArray(next) ? flat3(next) : next)
}, [])
}

4.rest運算符

微信公衆號:世界上有意思的事

function flat4 (arr) {
while (arr.some(item => Array.isArray(item))) {
// 至關於 [].concat('1', 2, [3, 4])
// concat 方法自己就會把參數中的數組展開
arr = [].concat(...arr);
}
return arr;
}

5.ES6 flat

微信公衆號:世界上有意思的事

function flat5 (arr: any[]) {
// flat() 方法會移除數組中的空項
return arr.flat(Infinity)
}
4.拖放
微信公衆號:世界上有意思的事

var DragDrop = function(){
var dragging = null;
function handleEvent(event){

//獲取事件和目標
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);

//肯定事件類型 
switch(event.type){
  case "mousedown":
    if (target.className.indexOf("draggable") > -1){
      dragging = target; 
    }
    break;
  case "mousemove":
    if (dragging !== null){ 
      //指定位置
      dragging.style.left = event.clientX + "px";
      dragging.style.top = event.clientY + "px";
    }
    break;
  case "mouseup": 
    dragging = null;
    break; 
}

};
//公共接口
return {
enable: function(){
EventUtil.addHandler(document, "mousedown", handleEvent);
EventUtil.addHandler(document, "mousemove", handleEvent);
EventUtil.addHandler(document, "mouseup", handleEvent);
},
disable: function(){
EventUtil.removeHandler(document, "mousedown", handleEvent);
EventUtil.removeHandler(document, "mousemove", handleEvent);
EventUtil.removeHandler(document, "mouseup", handleEvent);
}
}
}();

1.DragDrop 對象封裝了拖放的全部基本功能。這是一個單例對象,並使用了模塊模式來隱藏某些實 現細節。dragging 變量起初是 null,將會存放被拖動的元素,因此當該變量不爲 null 時,就知道正 在拖動某個東西。handleEvent()函數處理拖放功能中的全部的三個鼠標事件。它首先獲取 event 對 象和事件目標的引用。以後,用一個 switch 語句肯定要觸發哪一個事件樣式。當 mousedown 事件發生 時,會檢查 target 的 class 是否包含"draggable"類,若是是,那麼將 target 存放到 dragging 中。這個技巧能夠很方便地經過標記語言而非 JavaScript 腳原本肯定可拖動的元素。

2.handleEvent()的 mousemove 狀況和前面的代碼同樣,不過要檢查 dragging 是否爲 null。當 它不是 null,就知道 dragging 就是要拖動的元素,這樣就會把它放到恰當的位置上。mouseup 狀況 就僅僅是將 dragging 重置爲 null,讓 mousemove 事件中的判斷失效。

3.DragDrop 還有兩個公共方法:enable()和 disable(),它們只是相應添加和刪除全部的事件處 理程序。這兩個函數提供了額外的對拖放功能的控制手段。

4.要使用 DragDrop 對象,只要在頁面上包含這些代碼並調用 enable()。拖放會自動針對全部包含 "draggable"類的元素啓用,以下例所示:
<div class="draggable" style="position:absolute; background:red"> </div>
注意爲了元素能被拖放,它必須是絕對定位的。

5.once
微信公衆號:世界上有意思的事

function once (func) {
var done;
return function () {
if (!done) {
func.apply(null, arguments)
done = true
}
}
}
微信公衆號:世界上有意思的事

function onlyDoOne = once(function() {
console.log('1')
})
6.promise
Promise 是一個對象,保存着將來將要結束的事件,她有兩個特徵:

1.對象的狀態不受外部影響,Promise 對象表明一個異步操做,有三種狀態,pending進行中,fulfilled已成功,rejected已失敗,只有異步操做的結果,才能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態,這也就是promise名字的由來

2.一旦狀態改變,就不會再變,Promise對象狀態改變只有兩種可能,從pending改到fulfilled或者從pending改到rejected,只要這兩種狀況發生,狀態就凝固了,不會再改變,這個時候就稱爲定型resolved

7.sleep
用 Promise

function sleep (ms) {
return new Promise((resolve) => {
window.setTimeout(resolve, ms)
})
}

sleep(1000).then(()=>{
console.log('已經 sleep 1000ms')
})

function sleep (ms) {
return new Promise((resolve) => {
window.setTimeout(resolve, ms)
})
}

// 使用async/await調用
async function test () {
var example = await sleep(1000)
console.log('已經 sleep 1000ms')
}

// 使用 generator 定義 sleep 函數
function *sleep (ms) {
yield new Promise((resolve) => {
window.setTimeout(resolve, ms)
})
}
sleep(1000).next().value.then(()=>{
console.log('已經 sleep 1000ms')
})

4、瀏覽器
1.緩存

(1).按緩存位置分

1.Service Worker

1.有兩種狀況會致使這個緩存中的資源被清除:手動調用 API cache.delete(resource) 或者容量超過限制,被瀏覽器所有清空。
2.若是 Service Worker 沒能命中緩存,通常狀況會使用 fetch() 方法繼續獲取資源。這時候,瀏覽器就去 memory cache 或者 disk cache 進行下一次找緩存的工做了。注意:通過 Service Worker 的 fetch() 方法獲取的資源,即使它並無命中 Service Worker 緩存,甚至實際走了網絡請求,也會標註爲 from ServiceWorker。

2.Memory Cache:tab關閉則失效

1.memory cache 機制保證了一個頁面中若是有兩個相同的請求 (例如兩個 src 相同的 image,兩個 href 相同的 link)都實際只會被請求最多一次,避免浪費。
2.在從 memory cache 獲取緩存內容時,瀏覽器會忽視例如 max-age=0, no-cache 等頭部配置。例如頁面上存在幾個相同 src 的圖片,即使它們可能被設置爲不緩存,但依然會從 memory cache 中讀取。這是由於 memory cache 只是短時間使用,大部分狀況生命週期只有一次瀏覽而已。而 max-age=0 在語義上廣泛被解讀爲「不要在下次瀏覽時使用」,因此和 memory cache 並不衝突。
3.但若是站長是真心不想讓一個資源進入緩存,就連短時間也不行,那就須要使用 no-store。存在這個頭部配置的話,即使是 memory cache 也不會存儲,天然也不會從中讀取了。

3.Disk Cache:disk cache 會嚴格根據 HTTP 頭信息中的各種字段來斷定哪些資源能夠緩存,哪些資源不能夠緩存;哪些資源是仍然可用的,哪些資源是過期須要從新請求的。當命中緩存以後,瀏覽器會從硬盤中讀取資源,雖然比起從內存中讀取慢了一些,但比起網絡請求仍是快了很多的。絕大部分的緩存都來自 disk cache。
4.網絡請求:若是一個請求在上述 3 個位置都沒有找到緩存,那麼瀏覽器會正式發送網絡請求去獲取內容。以後容易想到,爲了提高以後請求的緩存命中率,天然要把這個資源添加到緩存中去。具體來講:

1.根據 Service Worker 中的 handler 決定是否存入 Cache Storage (額外的緩存位置)。
2.根據 HTTP 頭部的相關字段(Cache-control, Pragma 等)決定是否存入 disk cache
3.memory cache 保存一份資源 的引用,以備下次使用。

(2).按失效策略分

memory cache 是瀏覽器爲了加快讀取緩存速度而進行的自身的優化行爲,不受開發者控制,也不受 HTTP 協議頭的約束,算是一個黑盒。Service Worker 是由開發者編寫的額外的腳本,且緩存位置獨立,出現也較晚,使用還不算太普遍。因此咱們平時最爲熟悉的實際上是 disk cache,也叫 HTTP cache (由於不像 memory cache,它遵照 HTTP 協議頭中的字段)。平時所說的強制緩存(強緩存),對比緩存(協商緩存),以及 Cache-Control 等,也都歸於此類。

強制緩存 (也叫強緩存)
強制緩存直接減小請求數,是提高最大的緩存策略。 它的優化覆蓋了請求、處理、響應三個階段
能夠形成強制緩存的字段是 Cache-control 和 Expires。

Expires:

HTTP1.0
因爲是絕對時間,用戶可能會將客戶端本地的時間進行修改,而致使瀏覽器判斷緩存失效,從新請求該資源。此外,即便不考慮自信修改,時差或者偏差等因素也可能形成客戶端與服務端的時間不一致,導致緩存失效。
寫法太複雜了。表示時間的字符串多個空格,少個字母,都會致使非法屬性從而設置失效

Cache-control

HTTP1.1

優先級高

max-age:即最大有效時間
must-revalidate:若是超過了 max-age 的時間,瀏覽器必須向服務器發送請求,驗證資源是否還有效。
no-cache:雖然字面意思是「不要緩存」,但實際上仍是要求客戶端緩存內容的,只是是否使用這個內容由後續的對比來決定。
no-store: 真正意義上的「不要緩存」。全部內容都不走緩存,包括強制和對比。
public:全部的內容均可以被緩存 (包括客戶端和代理服務器, 如 CDN)
private:全部的內容只有客戶端才能夠緩存,代理服務器不能緩存。默認值。

對比緩存 (協商緩存)
對比緩存在請求數上和沒有緩存是一致的,但若是是 304 的話,返回的僅僅是一個狀態碼而已,並無實際的文件內容,所以 在響應體體積上的節省是它的優化點。

Last-Modified & If-Modified-Since

服務器經過 Last-Modified 字段告知客戶端,資源最後一次被修改的時間
瀏覽器將這個值和內容一塊兒記錄在緩存數據庫中。
下一次請求相同資源時時,瀏覽器從本身的緩存中找出「不肯定是否過時的」緩存。所以在請求頭中將上次的 Last-Modified 的值寫入到請求頭的 If-Modified-Since 字段
服務器會將 If-Modified-Since 的值與 Last-Modified 字段進行對比。若是相等,則表示未修改,響應 304;反之,則表示修改了,響應 200 狀態碼,並返回數據。
若是資源更新的速度是秒如下單位,那麼該緩存是不能被使用的,由於它的時間單位最低是秒。
若是文件是經過服務器動態生成的,那麼該方法的更新時間永遠是生成的時間,儘管文件可能沒有變化,因此起不到緩存的做用。

Etag & If-None-Match

Etag 的優先級高於 Last-Modified
Etag 存儲的是文件的特殊標識(通常都是 hash 生成的),服務器存儲着文件的 Etag 字段。
以後的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新時間改變成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 變成了 If-None-Match。
服務器一樣進行比較,命中返回 304, 不命中返回新資源和 200。

(3).Ajax 解決瀏覽器緩存問題

1.在ajax發送請求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")。

2.在ajax發送請求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。

3.在URL後面加上一個隨機數: "fresh=" + Math.random()。

4.在URL後面加上時間搓:"nowtime=" + new Date().getTime()。

5.若是是使用jQuery,直接這樣就能夠了 $.ajaxSetup({cache:false})。這樣頁面的全部ajax都會執行這條語句就是不須要保存緩存記錄。

2.瀏覽器渲染原理
(1).Render Tree

不顯示(display: none)的元素不會被生成
有了RenderTree,咱們就知道了全部節點的樣式,而後計算他們在頁面上的大小和位置(佈局),最後把節點繪製到頁面上(繪製)。
因爲瀏覽器使用流式佈局,對Render Tree的計算一般只須要遍歷一次就能夠完成,但table及其內部元素除外,他們可能須要屢次計算,一般要花3倍於同等元素的時間,這也是爲何要避免使用table佈局的緣由之一。

(2).重繪
因爲節點的幾何屬性發生改變或者因爲樣式發生改變而不會影響佈局的,稱爲重繪,例如outline, visibility, color、background-color等,重繪的代價是高昂的,由於瀏覽器必須驗證DOM樹上其餘節點元素的可見性。
(3)迴流
迴流是佈局或者幾何屬性須要改變就稱爲迴流。迴流是影響瀏覽器性能的關鍵因素,由於其變化涉及到部分頁面(或是整個頁面)的佈局更新。一個元素的迴流可能會致使了其全部子元素以及DOM中緊隨其後的節點、祖先節點元素的隨後的迴流。
(4).瀏覽器優化
現代瀏覽器大多都是經過隊列機制來批量更新佈局,瀏覽器會把修改操做放在隊列中,至少一個瀏覽器刷新(即16.6ms)纔會清空隊列,但當你獲取佈局信息的時候,隊列中可能有會影響這些屬性或方法返回值的操做,即便沒有,瀏覽器也會強制清空隊列,觸發迴流與重繪來確保返回正確的值。
主要包括如下屬性或方法:

offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
width、height
getComputedStyle()
getBoundingClientRect()

因此,咱們應該避免頻繁的使用上述的屬性,他們都會強制渲染刷新隊列。
(5).減小重繪與迴流

1.CSS

2.使用 transform 替代 top

3.使用 visibility 替換 display: none ,由於前者只會引發重繪,後者會引起迴流(改變了佈局

4.避免使用table佈局,可能很小的一個小改動會形成整個 table 的從新佈局。

5.儘量在DOM樹的最末端改變class,迴流是不可避免的,但能夠減小其影響。儘量在DOM樹的最末端改變class,能夠限制了迴流的範圍,使其影響儘量少的節點。

6.避免設置多層內聯樣式,CSS 選擇符從右往左匹配查找,避免節點層級過多。
<div>
<a> <span></span> </a>
</div>
<style>
span {
color: red;
}
div > a > span {
color: red;
}
</style>
複製代碼對於第一種設置樣式的方式來講,瀏覽器只須要找到頁面中全部的 span 標籤而後設置顏色,可是對於第二種設置樣式的方式來講,瀏覽器首先須要找到全部的 span 標籤,而後找到 span 標籤上的 a 標籤,最後再去找到 div 標籤,而後給符合這種條件的 span 標籤設置顏色,這樣的遞歸過程就很複雜。因此咱們應該儘量的避免寫過於具體的 CSS 選擇器,而後對於 HTML 來講也儘可能少的添加無心義標籤,保證層級扁平。

7.將動畫效果應用到position屬性爲absolute或fixed的元素上,避免影響其餘元素的佈局,這樣只是一個重繪,而不是迴流,同時,控制動畫速度能夠選擇 requestAnimationFrame,詳見探討 requestAnimationFrame。

8.避免使用CSS表達式,可能會引起迴流。

9.將頻繁重繪或者回流的節點設置爲圖層,圖層可以阻止該節點的渲染行爲影響別的節點,例如will-change、video、iframe等標籤,瀏覽器會自動將該節點變爲圖層。

10.CSS3 硬件加速(GPU加速),使用css3硬件加速,可讓transform、opacity、filters這些動畫不會引發迴流重繪 。可是對於動畫的其它屬性,好比background-color這些,仍是會引發迴流重繪的,不過它仍是能夠提高這些動畫的性能。

2.JavaScript

1.避免頻繁操做樣式,最好一次性重寫style屬性,或者將樣式列表定義爲class並一次性更改class屬性。
2.避免頻繁操做DOM,建立一個documentFragment,在它上面應用全部DOM操做,最後再把它添加到文檔中。
3.避免頻繁讀取會引起迴流/重繪的屬性,若是確實須要屢次使用,就用一個變量緩存起來。
4.對具備複雜動畫的元素使用絕對定位,使它脫離文檔流,不然會引發父元素及後續元素頻繁迴流。

(6).JS 何時解析?

<script>

渲染過程當中,若是遇到 JS 就中止渲染,執行 JS 代碼。

若是 JS 須要操做CSSOM,則會先讓CSSOM構建完,再執行JS,最後構建DOM

<script async>

異步執行引入的 JavaScript,加載完成後就執行 JS,阻塞DOM

<script defer>

延遲執行。載入 JavaScript 文件時不阻塞 HTML 的解析,執行階段被放到 HTML 標籤解析完成以後。

5、計算機基礎
1.計算機網絡
(1).TCP 三次握手

1.第一次握手:起初兩端都處於CLOSED關閉狀態,Client將標誌位SYN置爲1,隨機產生一個值seq=x,並將該數據包發送給Server,Client進入SYN-SENT狀態,等待Server確認;

2.第二次握手:Server收到數據包後由標誌位SYN=1得知Client請求創建鏈接,Server將標誌位SYN和ACK都置爲1,ack=x+1,隨機產生一個值seq=y,並將該數據包發送給Client以確認鏈接請求,Server進入SYN-RCVD狀態,此時操做系統爲該TCP鏈接分配TCP緩存和變量;

3.第三次握手:Client收到確認後,檢查ack是否爲x+1,ACK是否爲1,若是正確則將標誌位ACK置爲1,ack=y+1,而且此時操做系統爲該TCP鏈接分配TCP緩存和變量,並將該數據包發送給Server,Server檢查ack是否爲y+1,ACK是否爲1,若是正確則鏈接創建成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client和Server就能夠開始傳輸數據。

(2).CDN 原理
CDN的全稱是Content Delivery Network,即內容分發網絡。CDN的基本原理是普遍採用各類緩存服務器,將這些緩存服務器分佈到用戶訪問相對集中的地區或網絡中,在用戶訪問網站時,利用全局負載技術將用戶的訪問指向距離最近的工做正常的緩存服務器上,由緩存服務器直接響應
(4).DNS 解析

瀏覽器緩存:瀏覽器會按照必定的頻率緩存 DNS 記錄。
操做系統緩存:若是瀏覽器緩存中找不到須要的 DNS 記錄,那就去操做系統中找。
路由緩存:路由器也有 DNS 緩存。
ISP 的 DNS 服務器:ISP 是互聯網服務提供商(Internet Service Provider)的簡稱,ISP 有專門的 DNS 服務器應對 DNS 查詢請求。
根服務器:ISP 的 DNS 服務器還找不到的話,它就會向根服務器發出請求,進行遞歸查詢(DNS 服務器先問根域名服務器.com 域名服務器的 IP 地址,而後再問.baidu 域名服務器,依次類推)

(5).HTTP 經常使用請求頭
能夠將http首部分爲通用首部,請求首部,響應首部,實體首部

協議頭
說明
Accept
可接受的響應內容類型(Content-Types)。
Accept-Charset
可接受的字符集
Accept-Encoding
可接受的響應內容的編碼方式。
Accept-Language
可接受的響應內容語言列表。
Accept-Datetime
可接受的按照時間來表示的響應內容版本
Authorization
用於表示HTTP協議中須要認證資源的認證信息
Cache-Control
用來指定當前的請求/回覆中的,是否使用緩存機制。
Connection
客戶端(瀏覽器)想要優先使用的鏈接類型
Cookie
由以前服務器經過Set-Cookie(見下文)設置的一個HTTP協議Cookie
Content-Length
以8進製表示的請求體的長度
Content-MD5
請求體的內容的二進制 MD5 散列值(數字簽名),以 Base64 編碼的結果
Content-Type
請求體的MIME類型 (用於POST和PUT請求中)
Date
發送該消息的日期和時間(以RFC 7231中定義的"HTTP日期"格式來發送)

Expect
表示客戶端要求服務器作出特定的行爲

From
發起此請求的用戶的郵件地址

Host
表示服務器的域名以及服務器所監聽的端口號。若是所請求的端口是對應的服務的標準端口(80),則端口號能夠省略。

If-Match
僅當客戶端提供的實體與服務器上對應的實體相匹配時,才進行對應的操做。主要用於像 PUT 這樣的方法中,僅當從用戶上次更新某個資源後,該資源未被修改的狀況下,才更新該資源。

If-Modified-Since
容許在對應的資源未被修改的狀況下返回304未修改

If-None-Match
容許在對應的內容未被修改的狀況下返回304未修改( 304 Not Modified ),參考 超文本傳輸協議 的實體標記

If-Range
若是該實體未被修改過,則向返回所缺乏的那一個或多個部分。不然,返回整個新的實體

If-Unmodified-Since
僅當該實體自某個特定時間以來未被修改的狀況下,才發送迴應。

Max-Forwards
限制該消息可被代理及網關轉發的次數。

Origin
發起一個針對跨域資源共享的請求(該請求要求服務器在響應中加入一個Access-Control-Allow-Origin的消息頭,表示訪問控制所容許的來源)。

Pragma
與具體的實現相關,這些字段可能在請求/迴應鏈中的任什麼時候候產生。

Proxy-Authorization
用於向代理進行認證的認證信息。

Range
表示請求某個實體的一部分,字節偏移以0開始。

Referer
表示瀏覽器所訪問的前一個頁面,能夠認爲是以前訪問頁面的連接將瀏覽器帶到了當前頁面。Referer實際上是Referrer這個單詞,但RFC製做標準時給拼錯了,後來也就將錯就錯使用Referer了。

TE
瀏覽器預期接受的傳輸時的編碼方式:可以使用迴應協議頭Transfer-Encoding中的值(還能夠使用"trailers"表示數據傳輸時的分塊方式)用來表示瀏覽器但願在最後一個大小爲0的塊以後還接收到一些額外的字段。

User-Agent
瀏覽器的身份標識字符串

Upgrade
要求服務器升級到一個高版本協議。

Via
告訴服務器,這個請求是由哪些代理髮出的。

Warning
一個通常性的警告,表示在實體內容體中可能存在錯誤。

(5).OSI 七層模型
應用層:文件傳輸,經常使用協議HTTP,snmp,FTP ,
表示層:數據格式化,代碼轉換,數據加密,
會話層:創建,解除會話
傳輸層:提供端對端的接口,tcp,udp
網絡層:爲數據包選擇路由,IP,icmp
數據鏈路層:傳輸有地址的幀
物理層:二進制的數據形式在物理媒體上傳輸數據
(5).TCP和UDP的區別

1.UDP

1.無鏈接
2.面向報文,只是報文的搬運工
3.不可靠,沒有擁塞控制
4.高效,頭部開銷只有8字節
5.支持一對1、一對多、多對多、多對一
6.適合直播、視頻、語音、會議等實時性要求高的

2.TCP

1.面向鏈接:傳輸前須要先鏈接
2.可靠的傳輸
3.流量控制:發送方不會發送速度過快,超過接收方的處理能力
4.擁塞控制:當網絡負載過多時能限制發送方的發送速率
5.不提供時延保障
6.不提供最小帶寬保障

(6).爲何三次握手四次揮手

1.四次揮手

1.由於是雙方彼此都創建了鏈接,所以雙方都要釋放本身的鏈接,A向B發出一個釋放鏈接請求,他要釋放連接代表再也不向B發送數據了,此時B收到了A發送的釋放連接請求以後,給A發送一個確認,A不能再向B發送數據了,它處於FIN-WAIT-2的狀態,可是此時B還能夠向A進行數據的傳送。此時B向A 發送一個斷開鏈接的請求,A收到以後給B發送一個確認。此時B關閉鏈接。A也關閉鏈接。
2.爲何要有TIME-WAIT這個狀態呢,這是由於有可能最後一次確認丟失,若是B此時繼續向A發送一個我要斷開鏈接的請求等待A發送確認,但此時A已經關閉鏈接了,那麼B永遠也關不掉了,因此咱們要有TIME-WAIT這個狀態。
固然TCP也並非100%可靠的。

1.三次握手:爲了防止已失效的鏈接請求報文段忽然又傳送到了服務端,於是產生錯誤

(7).websocket和ajax的區別是什麼,websocket的應用場景有哪些
WebSocket的誕生本質上就是爲了解決HTTP協議自己的單向性問題:請求必須由客戶端向服務端發起,而後服務端進行響應。這個Request-Response的關係是沒法改變的。對於通常的網頁瀏覽和訪問固然沒問題,一旦咱們須要服務端主動向客戶端發送消息時就麻煩了,由於此前的TCP鏈接已經釋放,根本找不到客戶端在哪。
爲了能及時從服務器獲取數據,程序員們煞費苦心研究出來的各類解決方案其實都是在HTTP框架下作的妥協,無法子,瀏覽器這東西只支持HTTP,咱們有什麼辦法。因此你們要麼定時去輪詢,要麼就靠長鏈接——客戶端發起請求,服務端把這個鏈接攥在手裏不回覆,等有消息了再回,若是超時了客戶端就再請求一次——其實你們也懂,這只是個減小了請求次數、實時性更好的輪詢,本質沒變。
WebSocket就是從技術根本上解決這個問題的:看名字就知道,它借用了Web的端口和消息頭來建立鏈接,後續的數據傳輸又和基於TCP的Socket幾乎徹底同樣,但封裝了好多本來在Socket開發時須要咱們手動去作的功能。好比原生支持wss安全訪問(跟https共用端口和證書)、建立鏈接時的校驗、從數據幀中自動拆分消息包等等。
換句話說,本來咱們在瀏覽器裏只能使用HTTP協議,如今有了Socket,仍是個更好用的Socket。
瞭解了WebSocket的背景和特性以後,就能夠回答它能不能取代AJAX這個問題了:
對於服務器與客戶端的雙向通訊,WebSocket簡直是不二之選。若是不是還有少數舊版瀏覽器尚在服役的話,全部的輪詢、長鏈接等方式早就該廢棄掉。那些整合多種雙向推送消息方式的庫(如http://Socket.IO、SignalR)當初最大的賣點就是兼容全部瀏覽器版本,自動識別舊版瀏覽器並採起不一樣的鏈接方式,如今也漸漸失去了優點——全部新版瀏覽器都兼容WebSocket,直接用原生的就好了。說句題外話,這點很像jQuery,在原生js難用時迅速崛起,當其餘庫和原生js都吸取了它的不少優點時,慢慢就不那麼重要了。可是,很大一部分AJAX的使用場景仍然是傳統的請求-響應形式,好比獲取json數據、post表單之類。這些功能雖然靠WebSocket也能實現,但就像在本來傳輸數據流的TCP之上定義了基於請求的HTTP協議同樣,咱們也要在WebSocket之上從新定義一種新的協議,最少也要加個request id用來區分每次響應數據對應的請求吧。
……可是,何苦一層疊一層地造個新輪子呢?直接使用AJAX不是更簡單、更成熟嗎?
另外還有一種狀況,也就是傳輸大文件、圖片、媒體流的時候,最好仍是老老實實用HTTP來傳。若是必定要用WebSocket的話,至少也專門爲這些數據專門開闢個新通道,而別去佔用那條用於推送消息、對實時性要求很強的鏈接。不然會把串行的WebSocket完全堵死的。
因此說,WebSocket在用於雙向傳輸、推送消息方面可以作到靈活、簡便、高效,但在普通的Request-Response過程當中並無太大用武之地,比起普通的HTTP請求來反倒麻煩了許多,甚至更爲低效。
每項技術都有自身的優缺點,在適合它的地方能發揮出最大長處,而看到它的幾個優勢就不分場合地全方位推廣的話,可能會拔苗助長。
咱們本身在開發能與手機通訊的互聯網機器人時就使用了WebSocket,效果很好。但並非用它取代HTTP,而是取代了原先用於通訊的基於TCP的Socket。
優勢是:
原先在Socket鏈接後還要進行一些複雜的身份驗證,同時要阻止未驗證的鏈接發送控制指令。如今不須要了,在創建WebSocket鏈接的url裏就能攜帶身份驗證參數,驗證不經過能夠直接拒絕,不用設置狀態;
原先本身實現了一套相似SSL的非對稱加密機制,如今徹底不須要了,直接經過wss加密,還能順便保證證書的可信性;
原先要本身定義Socket數據格式,設置長度與標誌,處理粘包、分包等問題,如今WebSocket收到的直接就是完整的數據包,徹底不用本身處理;
前端的nginx能夠直接進行轉發與負載均衡,部署簡單多了
(8).TCP/IP的網絡模型

1.TCP/IP模型是一系列網絡協議的總稱,這些協議的目的是使得計算機之間能夠進行信息交換,

2.TCP/IP模型四層架構從下到上分別是鏈路層,網絡層,傳輸層,應用層

3.鏈路層的做用是負責創建電路鏈接,是整個網絡的物理基礎,典型的協議包括以太網,ADSL等,

4.網絡層負責分配地址和傳送二進制數據,主要協議是IP協議,

5.傳輸層負責傳送文本數據,主要協議是TCP

7.應用層負責傳送各類最終形態的數據,是直接與用戶信息打交道的層,主要協議是http,ftp等

2.HTTP協議
(1).常見的請求方法
HTTP 1.0

1.GET:從指定的資源請求數據
2.POST:向指定的資源提交要被處理的數據,例如

1.提交表單
2.將消息發佈到公告板,新聞組,郵件列表,博客或相似的文章組;

3.HEAD

1.相似於get請求,只不過返回的響應中沒有具體的內容,只有頭部
2.只請求資源的首部
3.檢查超連接的有效性
4.檢查網頁是否被修改

HTTP1.1

1.PUT:替換或建立指定資源
2.DELETE:對指定資源進行刪除

HTTP2.0

1.OPTIONS: 用於獲取目的資源所支持的通訊選項,好比說服務器支持的請求方式等等。

2.TRACE:實現沿通向目標資源的路徑的消息環回(loop-back)測試 ,提供了一種實用的 debug 機制。

3.CONNECT

1.爲代理服務器準備的

2.在 HTTP 協議中,CONNECT 方法能夠開啓一個客戶端與所請求資源之間的雙向溝通的通道。它能夠用來建立隧道(tunnel)。例如,CONNECT 能夠用來訪問採用了 SSL (HTTPS) 協議的站點。客戶端要求代理服務器將 TCP 鏈接做爲通往目的主機隧道。以後該服務器會代替客戶端與目的主機創建鏈接。鏈接創建好以後,代理服務器會面向客戶端發送或接收 TCP 消息流。

全部通用服務器必須支持GET和HEAD方法。全部其餘方法都是可選的。

1.安全性:在此規範定義的請求方法中,GET,HEAD,OPTIONS和TRACE方法被定義爲安全的
2.冪等性:PUT,DELETE和安全Method是冪等的。
3.可緩存性:GET, HEAD, and POST。但大多數是隻實現GET和HEAD可緩存

1.表示瀏覽器是會自動緩存的,以應用於後續請求。除非response中有相關策略

(2).GET 和 POST 的區別

1.get參數經過url傳遞,post放在request body中。

2.get請求在url中傳遞的參數是有長度限制的,而post沒有。

3.get比post更不安全,由於參數直接暴露在url中,因此不能用來傳遞敏感信息。

4.get請求只能進行url編碼,而post支持多種編碼方式

5.get請求會瀏覽器主動cache,而post支持多種編碼方式。

6.get請求參數會被完整保留在瀏覽歷史記錄裏,而post中的參數不會被保留。

7.GET和POST本質上就是TCP連接,並沒有差異。可是因爲HTTP的規定和瀏覽器/服務器的限制,致使他們在應用過程當中體現出一些不一樣。

(3).HTTP 狀態碼

1xx (Informational): 收到請求,正在處理
2xx (Successful): 該請求已成功收到,理解並接受
3xx (Redirection): 重定向
4xx (Client Error): 該請求包含錯誤的語法或不能爲完成
5xx (Server Error): 服務器錯誤

(4).301 和 302 有什麼具體區別

301:永久移動,請求的網頁已永久移動到新的位置,服務器返回此響應,會自動將請求者轉到新位置

302:歷史移動,服務器目前從不一樣位置的網頁響應請求,但請求者應繼續使用原有位置來繼續之後的請求,

3.操做系統
(1).進程和線程的區別

1.進程,是併發執行的程序在執行過程當中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。

2.線程,是進程的一部分,一個沒有線程的進程能夠被看做是單線程的。線程有時又被稱爲輕權進程或輕量級進程,也是 CPU 調度的一個基本單位。

一個程序至少有一個進程,一個進程至少有一個線程,資源分配給進程,同一個進程下全部線程共享該進程的資源
(2).線程的哪些資源共享,哪些資源不共享

1.共享的資源有

1.堆:因爲堆是在進程空間中開闢出來的,因此它是理所固然地被共享的;所以new出來的都是共享的(16位平臺上分全局堆和局部堆,局部堆是獨享的)
2.全局變量:它是與具體某一函數無關的,因此也與特定線程無關;所以也是共享的
3.靜態變量:雖然對於局部變量來講,它在代碼中是「放」在某一函數中的,可是其存放位置和全局變量同樣,存於堆中開闢的.bss和.data段,是共享的
4.文件等公用資源:這個是共享的,使用這些公共資源的線程必須同步。Win32 提供了幾種同步資源的方式,包括信號、臨界區、事件和互斥體。

2.獨享的資源有

1.棧:棧是獨享的
2寄存器:這個可能會誤解,由於電腦的寄存器是物理的,每一個線程去取值難道不同嗎?其實線程裏存放的是副本,包括程序計數器PC

(3).進程間的通訊方式有哪些

1.無名管道:半雙工的通訊方式,數據只能單向流動且只能在具備親緣關係的進程間使用
2.高級管道:將另外一個程序看成一個新的進程在當前程序進程中啓動,則這個進程算是當前程序的子進程,
3.有名管道,:也是半雙工的通訊方式,可是容許沒有親緣進程之間的通訊
4.消息隊列:消息隊列是有消息的鏈表,存放在內核中,並由消息隊列標識符標識,消息隊列克服了信號傳遞信息少,管道只能承載無格式字節流以及緩衝區大小受限的缺點
5.信號量:信號量是一個計數器,能夠用來控制多個進程對共享資源的訪問,它常做爲一種鎖機制,防止某進程正在訪問共享資源時,其餘進程也訪問該資源,
6.信號:用於通知接受進程某個事件已經發生
7.共享內存:共享內存就是映射一段能被其餘進程所訪問的內存。這段共享內存由一個進程建立,可是多個進程能夠訪問,共享內存是最快的IPC 方式,每每與其餘通訊機制配合使用
8.套接字:可用於不一樣機器之間的進程通訊

6、前端進階
1.VUE
(1).vue的生命週期
Vue實例有一個完整的生命週期,也就是從開始建立、初始化數據、編譯模板、掛載Dom、渲染→更新→渲染、銷燬等一系列過程,咱們稱這是Vue的生命週期。通俗說就是Vue實例從建立到銷燬的過程,就是生命週期。
每個組件或者實例都會經歷一個完整的生命週期,總共分爲三個階段:初始化、運行中、銷燬。
1.實例、組件經過new Vue() 建立出來以後會初始化事件和生命週期,而後就會執行beforeCreate鉤子函數,這個時候,數據尚未掛載呢,只是一個空殼,沒法訪問到數據和真實的dom,通常不作操

2.掛載數據,綁定事件等等,而後執行created函數,這個時候已經能夠使用到數據,也能夠更改數據,在這裏更改數據不會觸發updated函數,在這裏能夠在渲染前倒數第二次更改數據的機會,不會觸發其餘的鉤子函數,通常能夠在這裏作初始數據的獲取

3.接下來開始找實例或者組件對應的模板,編譯模板爲虛擬dom放入到render函數中準備渲染,而後執行beforeMount鉤子函數,在這個函數中虛擬dom已經建立完成,立刻就要渲染,在這裏也能夠更改數據,不會觸發updated,在這裏能夠在渲染前最後一次更改數據的機會,不會觸發其餘的鉤子函數,通常能夠在這裏作初始數據的獲取

4.接下來開始render,渲染出真實dom,而後執行mounted鉤子函數,此時,組件已經出如今頁面中,數據、真實dom都已經處理好了,事件都已經掛載好了,能夠在這裏操做真實dom等事情...

5.當組件或實例的數據更改以後,會當即執行beforeUpdate,而後vue的虛擬dom機制會從新構建虛擬dom與上一次的虛擬dom樹利用diff算法進行對比以後從新渲染,通常不作什麼事兒

6.當更新完成後,執行updated,數據已經更改完成,dom也從新render完成,能夠操做更新後的虛擬dom

7.當通過某種途徑調用$destroy方法後,當即執行beforeDestroy,通常在這裏作一些善後工做,例如清除計時器、清除非指令綁定的事件等

8.組件的數據綁定、監聽...去掉後只剩下dom空殼,這個時候,執行destroyed,在這裏作善後工做也能夠
(2).Vue 雙向綁定原理
vue數據雙向綁定是經過數據劫持結合發佈者-訂閱者模式的方式來實現的。利用了 Object.defineProperty() 這個方法從新定義了對象獲取屬性值(get)和設置屬性值(set)。
2.Webpack
webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個bundle。
(1).webpack 和 gulp 區別
gulp強調的是前端開發的工做流程,咱們能夠經過配置一系列的task,定義task處理的事務(例如文件壓縮合並、雪碧圖、啓動server、版本控制等),而後定義執行順序,來讓gulp執行這些task,從而構建項目的整個前端開發流程。
webpack是一個前端模塊化方案,更側重模塊打包,咱們能夠把開發中的全部資源(圖片、js文件、css文件等)都當作模塊,經過loader(加載器)和plugins(插件)對資源進行處理,打包成符合生產環境部署的前端資源。
3.模塊化
(1).如何理解前端模塊化
前端模塊化就是複雜的文件編程一個一個獨立的模塊,好比js文件等等,分紅獨立的模塊有利於重用(複用性)和維護(版本迭代),這樣會引來模塊之間相互依賴的問題,因此有了commonJS 規範,AMD,CMD規範等等,以及用於js打包(編譯等處理)的工具 webpack
(2).說一下 Commonjs、AMD 和 CMD
一個模塊是能實現特定功能的文件,有了模塊就能夠方便的使用別人的代碼,想要什麼功能就能加載什麼模塊

1.Commonjs:開始於服務器端的模塊化,同步定義的模塊化,每一個模塊都是一個單獨的做用域,模塊輸出,modules.exports,模塊加載require()引入模塊。
2.AMD:中文名異步模塊定義的意思
1.require JS 實現了 AMD 規範
1.主要用於解決下述兩個問題。
1.多個文件有依賴關係,被依賴的文件須要早於依賴它的文件加載到瀏覽器
2.加載的時候瀏覽器會中止頁面渲染,加載文件越多,頁面失去響應的時間越長。

2.語法:requireJS 定義了一個函數 define,它是全局變量,用來定義模塊。

//定義模塊
define(['dependency'], function(){
var name = 'Byron';
function printName(){
console.log(name);
}
return {
printName: printName
};
});

//加載模塊
require(['myModule'], function (my){
my.printName();
}

2.總結 AMD 規範:require()函數在加載依賴函數的時候是異步加載的,這樣瀏覽器不會失去響應,它指定的回調函數,只有前面的模塊加載成功,纔會去執行。由於網頁在加載js的時候會中止渲染,所以咱們能夠經過異步的方式去加載js,而若是須要依賴某些,也是異步去依賴,依賴後再執行某些方法。

4.簡單實現Node的Events模塊
簡介:觀察者模式或者說訂閱模式,它定義了對象間的一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,當一個對象發生改變時,全部依賴於它的對象都將獲得通知。
node中的Events模塊就是經過觀察者模式來實現的:
微信公衆號:世界上有意思的事

var events=require('events');
var eventEmitter=new events.EventEmitter();
eventEmitter.on('say',function(name){
console.log('Hello',name);
})
eventEmitter.emit('say','Jony yu');
這樣,eventEmitter發出say事件,經過On接收,而且輸出結果,這就是一個訂閱模式的實現,下面咱們來簡單的實現一個Events模塊的EventEmitter。

1.實現簡單的Event模塊的emit和on方法
function Events(){
this.on=function(eventName,callBack){
if(!this.handles){
this.handles={};
}
if(!this.handles[eventName]){
this.handles[eventName]=[];
}
this.handles[eventName].push(callBack);
}
this.emit=function(eventName,obj){
if(this.handles[eventName]){
for(var i=0;o<this.handles[eventName].length;i++){
this.handles[eventName]i;
}
}
}
return this;
}

2.這樣咱們就定義了Events,如今咱們能夠開始來調用:
var events=new Events();
events.on('say',function(name){
console.log('Hello',nama)
});
//結果就是經過emit調用以後,輸出了Jony yu
events.emit('say','Jony yu')

3.每一個對象是獨立的
由於是經過new的方式,每次生成的對象都是不相同的,所以:
var event1=new Events();
var event2=new Events();
event1.on('say',function(){
console.log('Jony event1');
});
event2.on('say',function(){
console.log('Jony event2');
})

//event一、event2之間的事件監聽互相不影響
//輸出結果爲'Jony event1' 'Jony event2'
event1.emit('say');
event2.emit('say');

5.性能優化

1.下降請求量:合併資源,減小HTTP 請求數,minify / gzip 壓縮,webP,圖片lazyLoad。

2.加快請求速度:預解析DNS,減小域名數,並行加載,CDN 分發。

3.緩存:HTTP 協議緩存請求,離線緩存 manifest,離線數據緩存localStorage。

4.渲染:JS/CSS優化(避免使用CSS表達式),加載順序(將CSS樣式表放在頂部,把javascript放在底部),服務端渲染,pipeline。

相關文章
相關標籤/搜索