本文投稿自一位前端小姐姐——徐漂漂,本文乾貨成噸,全程高能。但願你們多點贊、評論、關注,給小姐姐繼續寫文章的動力!javascript
微信公衆號:世界上有意思的事
function permutate(str) {
var array = str.split('');
function loop(array, pre = []) {
if (array.length == 1) {
return [pre.concat(array).join('')];
}
let res = [];
for (let index = 0; index < array.length; index++) {
var first = array.pop();
res = res.concat(loop(array, [...pre, first]));
array.unshift(first);
}
return res;
}
return Array.from(new Set(loop(array)))
}
複製代碼
微信公衆號:世界上有意思的事
function BinarySearch1 (arr, target) {
return search(arr, target, 0, arr.length - 1)
function search (arr, target, from, to) {
if (from > to) {
return -1
}
const mid = Math.floor((from + to)/2)
if (arr[mid] > target) {
return search(arr, target, from, mid - 1)
} else if (arr[mid] < target) {
return search(arr, target, mid + 1, to)
} else {
return mid
}
}
}
function BinarySearch2 (arr, target) {
let from = 0
let to = arr.length - 1
let mid = Math.floor((from + to)/2)
while (from <= to) {
mid = Math.floor((from + to)/2)
if (arr[mid] > target) {
to = mid - 1
} else if (arr[mid] < target) {
from = mid + 1
} else {
return mid
}
}
return -1
}
複製代碼
微信公衆號:世界上有意思的事
/* 第1次循環肯定最大的 第n次循環肯定第n大的 */
function BubbleSort (arr) {
const length = arr.length
for (let i = 0; i < length; i++) {
for (let j = 1; j < length-i; j++) {
if (arr[j] < arr[j - 1]) {
const temp = arr[j]
arr[j] = arr[j - 1]
arr[j - 1] = temp
}
}
}
return arr
}
複製代碼
微信公衆號:世界上有意思的事
/* 在左邊找大數,在右邊找小數 交換 */
function QuickSort(arr, low, high) {
let left = low
let right = high
let basic = arr[low]
while (left < right) {
while (left < right && arr[right] > basic) {
right--
}
while (left < right && arr[left] <= basic) {
left++
}
if (left < right) {
const temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
} else {
const temp = arr[low]
arr[low] = arr[left]
arr[left] = temp
QuickSort(arr, low, left - 1)
QuickSort(arr, right + 1, high)
}
}
return arr
}
複製代碼
微信公衆號:世界上有意思的事
/* 尋找第i小的數的位置,放到i位置上 */
function SelectionSort (arr) {
const length = arr.length
for (let i = 0; i < length; i++ ) {
let minIndex= i
for (let j = i + 1; j < length; j++) {
minIndex = arr[minIndex] <= arr[j] ? minIndex : j
}
if (minIndex !== i) {
const temp = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
}
return arr
}
複製代碼
微信公衆號:世界上有意思的事
function InsertionSort (arr) {
const length = arr.length
for (let i = 1; i < length; i++) {
const temp = arr[i]
let j
for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
arr[j+1] = arr[j]
}
arr[j+1] = temp
}
return arr
}
複製代碼
插入排序的改進版。對間隔 gap 爲一組的數進行插入排序html
微信公衆號:世界上有意思的事
function ShellSort (arr) {
const length = arr.length
let gap = Math.floor(length)
while (gap) {
for (let i = gap; i < length; i++) {
const temp = arr[i]
let j
for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap) {
arr[j + gap] = arr[j]
}
arr[j + gap] = temp
}
gap = Math.floor(gap / 2)
}
return arr
}
複製代碼
微信公衆號:世界上有意思的事
function MergeSort (arr, low, high) {
const length = arr.length
if (low === high) {
return arr[low]
}
const mid = Math.floor((low + high)/2)
MergeSort(arr, low, mid)
MergeSort(arr, mid + 1, high)
merge(arr, low, high)
return arr
}
function merge (arr, low, high) {
const mid = Math.floor((low + high)/2)
let left = low
let right = mid + 1
const result = []
while (left <= mid && right <= high) {
if (arr[left] <= arr[right]) {
result.push(arr[left++])
} else {
result.push(arr[right++])
}
}
while (left <= mid) {
result.push(arr[left++])
}
while (right <= high) {
result.push(arr[right++])
}
arr.splice(low, high-low+1, ...result)
}
const test = [2, 34, 452,3,5, 785, 32, 345, 567, 322,5]
console.log(MergeSort(test, 0, test.length - 1))
複製代碼
微信公衆號:世界上有意思的事
function HeapSort (arr) {
const length = arr.length
// 調整初始堆,調整完其實也肯定了最大值
// 但此時最大值是在 arr[0] 中
for (let i = Math.floor(length/2) - 1; i >= 0; i--) {
adjustHeap(arr, i, length)
}
// 把 arr[0](最大值)換到後面
for (let i = length - 1; i >=0; i--) {
const temp = arr[0]
arr[0] = arr[i]
arr[i] = temp
adjustHeap(arr, 0, i)
}
return arr
}
// size 是還須要調整的堆的大小
// 隨着一個個最大值的肯定,size 會愈來愈小
function adjustHeap (arr, position, size) {
const left = position * 2 + 1
const right = left + 1
let maxIndex = position
if (left < size && arr[left] > arr[maxIndex]) {
maxIndex = left
}
if (right < size && arr[right] > arr[maxIndex]) {
maxIndex = right
}
if (maxIndex !== position) {
const temp = arr[position]
arr[position] = arr[maxIndex]
arr[maxIndex] = temp
adjustHeap(arr, maxIndex, size)
}
return arr
}
複製代碼
一、原型鏈繼承,將父類的實例做爲子類的原型,他的特色是實例是子類的實例也是父類的實例,父類新增的原型方法/屬性,子類都可以訪問,而且原型鏈繼承簡單易於實現,缺點是來自原型對象的全部屬性被全部實例共享,沒法實現多繼承,沒法向父類構造函數傳參。前端
二、構造繼承,使用父類的構造函數來加強子類實例,即複製父類的實例屬性給子類,構造繼承能夠向父類傳遞參數,能夠實現多繼承,經過call多個父類對象。可是構造繼承只能繼承父類的實例屬性和方法,不能繼承原型屬性和方法,沒法實現函數服用,每一個子類都有父類實例函數的副本,影響性能vue
三、實例繼承,爲父類實例添加新特性,做爲子類實例返回,實例繼承的特色是不限制調用方法,無論是new 子類()仍是子類()返回的對象具備相同的效果,缺點是實例是父類的實例,不是子類的實例,不支持多繼承java
四、拷貝繼承:特色:支持多繼承,缺點:效率較低,內存佔用高(由於要拷貝父類的屬性)沒法獲取父類不可枚舉的方法(不可枚舉方法,不能使用for in 訪問到)node
五、組合繼承:經過調用父類構造,繼承父類的屬性並保留傳參的優勢,而後經過將父類實例做爲子類原型,實現函數複用webpack
六、寄生組合繼承:經過寄生方式,砍掉父類的實例屬性,這樣,在調用兩次父類的構造的時候,就不會初始化兩次實例方法/屬性,避免的組合繼承的缺點css3
1.默認綁定:全局環境中,this默認綁定到window。nginx
2.隱式綁定:通常地,被直接對象所包含的函數調用時,也稱爲方法調用,this隱式綁定到該直接對象。
3.隱式丟失:隱式丟失是指被隱式綁定的函數丟失綁定對象,從而默認綁定到window。顯式綁定:經過call()、apply()、bind()方法把對象綁定到this上,叫作顯式綁定。
4.new綁定:若是函數或者方法調用以前帶有關鍵字new,它就構成構造函數調用。對於this綁定來講,稱爲new綁定。
1.apply:調用一個對象的一個方法,用另外一個對象替換當前對象。例如:B.apply(A, arguments);即A對象應用B對象的方法。
2.call:調用一個對象的一個方法,用另外一個對象替換當前對象。例如:B.call(A, args1,args2);即A對象調用B對象的方法。
3.bind除了返回是函數之外,它的參數和call同樣。
Undefined、Null、Boolean、Number 、String、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.用做對象的屬性名
for...in
、for...of
循環中,也不會被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。Object.getOwnPropertySymbols()
方法,能夠獲取指定對象的全部 Symbol 屬性名。該方法返回一個數組,成員是當前對象的全部用做屬性名的 Symbol 值。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)
!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()
typeof(),instanceof,Object.prototype.toString.call()
1.typeof
操做符
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 等
複製代碼
Array.isArray(arr)
Object.prototype.toString.call(arr) === '[Object Array]'
arr instanceof Array
array.constructor === Array
parseInt(string, radix)
大腦對於事情的計劃方式是線性的、阻塞的、單線程的語義,可是回調錶達異步流 程的方式是非線性的、非順序的,這使得正確推導這樣的代碼難度很大。難於理解的代碼 是壞代碼,會致使壞 bug。咱們須要一種更同步、更順序、更阻塞的的方式來表達異步,就像咱們的大腦同樣。
也是更重要的一點,回調會受到控制反轉的影響,由於回調暗中把控制權交給第三 方(一般是不受你控制的第三方工具!)來調用你代碼中的 continuation。能夠發明一些特定邏輯來解決這些信任問題,可是其難度高於應有的水平,可能會產生更 笨重、更難維護的代碼,而且缺乏足夠的保護,其中的損害要直到你受到 bug 的影響纔會 被發現。
咱們須要一個通用的方案來解決這些信任問題。無論咱們建立多少回調,這一方案都應可 以複用,且沒有重複代碼的開銷。
Promise 的實現能夠看這裏
Promise 這種模式經過可信任的語義把回調做爲參數傳遞,使得這種行爲更可靠更合理。 經過把回調的控制反轉反轉回來,咱們把控制權放在了一個可信任的系統(Promise)中, 這種系統的設計目的就是爲了使異步編碼更清晰。Promise 並無擯棄回調,只是把回調的安排轉交給了一個位於咱們和其餘工具之間的可信任 的中介機制。
調用回調過早;
這個問題主要就是擔憂代碼是否會引入相似 Zalgo 這樣的反作用(參見第 2 章)。在這類問 題中,一個任務有時同步完成,有時異步完成,這可能會致使競態條件。
根據定義,Promise 就沒必要擔憂這種問題,由於即便是當即完成的 Promise(相似於 new Promise(function(resolve){ resolve(42); }))也沒法被同步觀察到。
也就是說,對一個 Promise 調用 then(..) 的時候,即便這個 Promise 已經決議,提供給 then(..) 的回調也總會被異步調用(對此的更多討論,請參見 1.5 節)。
調用回調過晚(或不被調用);
回調未調用
調用回調次數過多;
未能傳遞所需的環境和參數;
Promise 至多隻能有一個決議值(完成或拒絕)。
若是你沒有用任何值顯式決議,那麼這個值就是 undefined,這是 JavaScript 常見的處理方 式。但無論這個值是什麼,不管當前或將來,它都會被傳給全部註冊的(且適當的完成或 拒絕)回調。
吞掉可能出現的錯誤和異常。
promise
generator
async/await
優勢:代碼清晰,不用像 Promise 寫一大堆 then 鏈,處理了回調地獄的問題
缺點:await 將異步代碼改形成同步代碼,若是多個異步操做沒有依賴性而使用 await 會致使性能上的下降。
defer:只支持IE若是您的腳本不會改變文檔的內容,可將 defer 屬性加入到<script>
標籤中,以便加快處理文檔的速度。由於瀏覽器知道它將可以安全地讀取文檔的剩餘部分而不用執行腳本,它將推遲對腳本的解釋,直到文檔已經顯示給用戶爲止。
async:HTML5 屬性,僅適用於外部腳本;而且若是在IE中,同時存在defer和async,那麼defer的優先級比較高;腳本將在頁面完成時執行。
兩種技術的本質:二者的行爲是相反的,一個是提早加載,一個是遲緩甚至不加載。懶加載對服務器前端有必定的緩解壓力做用,預加載則會增長服務器前端壓力。
HTML中與javascript交互是經過事件驅動來實現的,例如鼠標點擊事件onclick、頁面的滾動事件onscroll等等,能夠向文檔或者文檔中的元素添加事件偵聽器來預訂事件。想要知道這些事件是在何時進行調用的,就須要瞭解一下「事件流」的概念。
什麼是事件流:事件流描述的是從頁面中接收事件的順序,DOM2級事件流包括下面幾個階段。
IE只支持事件冒泡。
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 仍然調用了這個函數,客戶端將會忽略它並拋出一個控制檯警告
}
複製代碼
簡介:事件委託指的是,不在事件的發生地(直接dom)上設置監聽函數,而是在其父元素上設置監聽函數,經過事件冒泡,父元素能夠監聽到子元素上事件的觸發,經過判斷事件發生元素DOM的類型,來作出不一樣的響應。
舉例:最經典的就是ul和li標籤的事件監聽,好比咱們在添加事件時候,採用事件委託機制,不會在li標籤上直接添加,而是在ul父元素上添加。
好處:比較合適動態元素的綁定,新添加的子元素也會有監聽函數,也能夠有事件觸發機制。
能夠在父元素層面阻止事件向子元素傳播,也可代替子元素執行某些操做。
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 信息。
微軟在 IE8 中引入了 XDR(XDomainRequest)類型。如下是 XDR 與 XHR 的一些不一樣之 處。
經過 XMLHttpRequest 對象實現了對 CORS 的原生支持
微信公衆號:世界上有意思的事
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);
複製代碼
起一個代理服務器,實現數據的轉發
lovelock.coding.me/javascript/…
只支持到IE8及以上的IE瀏覽器,其餘現代瀏覽器固然沒有問題。
不受同源策略的限制
給接收數據的一方添加事件綁定:addEventListener('message', receiveMessage);
發送數據的一方拿到接收數據一方的window:targetWindow.postMessage("Welcome to unixera.com", "http://iframe1.unixera.com");
有跨域問題,只適合站內不一樣子域間的通訊(設置document.domain爲同一級域名)
這是一個通用的方法,簡單來講是A iframe包含B iframe,在B iframe中調用了相關的接口,完成調用以後獲取到結果,location.href
到和A iframe位於同一個域的C iframe,在C iframe中調用A iframe中定義的方法,將B iframe中獲取的結果做爲參數傳到要跳轉的url後,在C iframe中經過location.search
變量來獲取變量。
window
對象的name
屬性是一個很特殊的屬性,在設定了window.name
以後,執行location.href
跳轉,window.name
屬性仍然不會發生變化,能夠經過這種方式實現變量的傳遞。
微信公衆號:世界上有意思的事
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)
複製代碼
微信公衆號:世界上有意思的事
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)
})
}
複製代碼
找出那些再也不繼續使用的變 量,而後釋放其佔用的內存。爲此,垃圾收集器會按照固定的時間間隔(或代碼執行中預約的收集時間), 週期性地執行這一操做。
先全部都加上標記,再把環境中引用到的變量去除標記。剩下的就是沒用的了
跟蹤記錄每 個值被引用的次數。清除引用次數爲0的變量 ⚠️會有循環引用問題 。循環引用若是大量存在就會致使內存泄露。
eval 方法就像是一個完整的 ECMAScript 解析器,它只接受一個參數,即要執行的 ECMAScript (或JavaScript) 字符串
微信公衆號:世界上有意思的事
Object.defineProperty(user,'name',{
set:function(key,value){
// 這也是 Vue 的原理
}
})
複製代碼
微信公衆號:世界上有意思的事
var user = new Proxy({}, {
set:function(target,key,value,receiver){
}
})
複製代碼
能夠監聽動態增長的屬性。例如 user.id = 1
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();
複製代碼
==
和===
、以及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
===
有如下幾點不一樣:
+0===-0
,Object.is(+0, -0)
爲 falseNaN !== NaN
,Object.is(NaN, NaN)
爲 true用 new 操做符調用構造函數實際上會經歷如下 4 個步驟:
push(),pop(),shift(),unshift(),splice(),sort(),reverse(),map()等
要注意的是對象咋去重
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))
}
複製代碼
在 n 秒內只會執行一次,因此節流會稀釋函數的執行頻率
按最後一次算。好比說「中止輸入5s後才發送請求」
微信公衆號:世界上有意思的事
function flat1 (arr) {
let result = []
arr.forEach(element => {
if (Array.isArray(element)) {
result = result.concat(flat1(element))
} else {
result.push(element)
}
});
return result
}
複製代碼
function flat2 (arr) {
// 有缺陷,toString 後沒法保持以前的類型
return arr.toString().split(',')
}
複製代碼
微信公衆號:世界上有意思的事
function flat3 (arr) {
// 本質和 flat1 同樣的,都是遞歸
return arr.reduce((pre, next) => {
return pre.concat(Array.isArray(next) ? flat3(next) : next)
}, [])
}
複製代碼
微信公衆號:世界上有意思的事
function flat4 (arr) {
while (arr.some(item => Array.isArray(item))) {
// 至關於 [].concat('1', 2, [3, 4])
// concat 方法自己就會把參數中的數組展開
arr = [].concat(...arr);
}
return arr;
}
複製代碼
微信公衆號:世界上有意思的事
function flat5 (arr: any[]) {
// flat() 方法會移除數組中的空項
return arr.flat(Infinity)
}
複製代碼
微信公衆號:世界上有意思的事
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>
複製代碼
注意爲了元素能被拖放,它必須是絕對定位的。
微信公衆號:世界上有意思的事
function once (func) {
var done;
return function () {
if (!done) {
func.apply(null, arguments)
done = true
}
}
}
複製代碼
微信公衆號:世界上有意思的事
function onlyDoOne = once(function() {
console.log('1')
})
複製代碼
Promise
是一個對象,保存着將來將要結束的事件,她有兩個特徵:
1.對象的狀態不受外部影響,Promise
對象表明一個異步操做,有三種狀態,pending進行中,fulfilled已成功,rejected已失敗,只有異步操做的結果,才能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態,這也就是promise名字的由來
2.一旦狀態改變,就不會再變,Promise
對象狀態改變只有兩種可能,從pending改到fulfilled或者從pending改到rejected,只要這兩種狀況發生,狀態就凝固了,不會再改變,這個時候就稱爲定型resolved
用 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')
})
複製代碼
cache.delete(resource)
或者容量超過限制,被瀏覽器所有清空。fetch()
方法繼續獲取資源。這時候,瀏覽器就去 memory cache 或者 disk cache 進行下一次找緩存的工做了。注意:通過 Service Worker 的 fetch()
方法獲取的資源,即使它並無命中 Service Worker 緩存,甚至實際走了網絡請求,也會標註爲 from ServiceWorker
。src
相同的 image
,兩個 href
相同的 link
)都實際只會被請求最多一次,避免浪費。max-age=0
, no-cache
等頭部配置。例如頁面上存在幾個相同 src
的圖片,即使它們可能被設置爲不緩存,但依然會從 memory cache 中讀取。這是由於 memory cache 只是短時間使用,大部分狀況生命週期只有一次瀏覽而已。而 max-age=0
在語義上廣泛被解讀爲「不要在下次瀏覽時使用」,因此和 memory cache 並不衝突。no-store
。存在這個頭部配置的話,即使是 memory cache 也不會存儲,天然也不會從中讀取了。Cache-control
, Pragma
等)決定是否存入 disk cachememory cache 是瀏覽器爲了加快讀取緩存速度而進行的自身的優化行爲,不受開發者控制,也不受 HTTP 協議頭的約束,算是一個黑盒。Service Worker 是由開發者編寫的額外的腳本,且緩存位置獨立,出現也較晚,使用還不算太普遍。因此咱們平時最爲熟悉的實際上是 disk cache,也叫 HTTP cache (由於不像 memory cache,它遵照 HTTP 協議頭中的字段)。平時所說的強制緩存(強緩存),對比緩存(協商緩存),以及
Cache-Control
等,也都歸於此類。
強制緩存直接減小請求數,是提高最大的緩存策略。 它的優化覆蓋了請求、處理、響應三個階段
能夠形成強制緩存的字段是 Cache-control
和 Expires
。
Expires:
Cache-control
HTTP1.1
優先級高
max-age
:即最大有效時間
must-revalidate
:若是超過了 max-age
的時間,瀏覽器必須向服務器發送請求,驗證資源是否還有效。
no-cache
:雖然字面意思是「不要緩存」,但實際上仍是要求客戶端緩存內容的,只是是否使用這個內容由後續的對比來決定。
no-store
: 真正意義上的「不要緩存」。全部內容都不走緩存,包括強制和對比。
public
:全部的內容均可以被緩存 (包括客戶端和代理服務器, 如 CDN)
private
:全部的內容只有客戶端才能夠緩存,代理服務器不能緩存。默認值。
對比緩存在請求數上和沒有緩存是一致的,但若是是 304 的話,返回的僅僅是一個狀態碼而已,並無實際的文件內容,所以 在響應體體積上的節省是它的優化點。
Last-Modified
字段告知客戶端,資源最後一次被修改的時間Last-Modified
的值寫入到請求頭的 If-Modified-Since
字段If-Modified-Since
的值與 Last-Modified
字段進行對比。若是相等,則表示未修改,響應 304;反之,則表示修改了,響應 200 狀態碼,並返回數據。Etag
存儲的是文件的特殊標識(通常都是 hash 生成的),服務器存儲着文件的 Etag
字段。Last-Modified
一致,只是 Last-Modified
字段和它所表示的更新時間改變成了 Etag
字段和它所表示的文件 hash,把 If-Modified-Since
變成了 If-None-Match
。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都會執行這條語句就是不須要保存緩存記錄。
display: none
)的元素不會被生成RenderTree
,咱們就知道了全部節點的樣式,而後計算他們在頁面上的大小和位置(佈局),最後把節點繪製到頁面上(繪製)。Render Tree
的計算一般只須要遍歷一次就能夠完成,但table
及其內部元素除外,他們可能須要屢次計算,一般要花3倍於同等元素的時間,這也是爲何要避免使用table
佈局的緣由之一。因爲節點的幾何屬性發生改變或者因爲樣式發生改變而不會影響佈局的,稱爲重繪,例如outline
, visibility
, color
、background-color
等,重繪的代價是高昂的,由於瀏覽器必須驗證DOM樹上其餘節點元素的可見性。
迴流是佈局或者幾何屬性須要改變就稱爲迴流。迴流是影響瀏覽器性能的關鍵因素,由於其變化涉及到部分頁面(或是整個頁面)的佈局更新。一個元素的迴流可能會致使了其全部子元素以及DOM中緊隨其後的節點、祖先節點元素的隨後的迴流。
現代瀏覽器大多都是經過隊列機制來批量更新佈局,瀏覽器會把修改操做放在隊列中,至少一個瀏覽器刷新(即16.6ms)纔會清空隊列,但當你獲取佈局信息的時候,隊列中可能有會影響這些屬性或方法返回值的操做,即便沒有,瀏覽器也會強制清空隊列,觸發迴流與重繪來確保返回正確的值。
主要包括如下屬性或方法:
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
clientTop
、clientLeft
、clientWidth
、clientHeight
width
、height
getComputedStyle()
getBoundingClientRect()
因此,咱們應該避免頻繁的使用上述的屬性,他們都會強制渲染刷新隊列。
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
style
屬性,或者將樣式列表定義爲class
並一次性更改class
屬性。DOM
,建立一個documentFragment
,在它上面應用全部DOM操做
,最後再把它添加到文檔中。<script>
渲染過程當中,若是遇到 JS 就中止渲染,執行 JS 代碼。
若是 JS 須要操做CSSOM,則會先讓CSSOM構建完,再執行JS,最後構建DOM
<script async>
<script defer>
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就能夠開始傳輸數據。
CDN的全稱是Content Delivery Network,即內容分發網絡。CDN的基本原理是普遍採用各類緩存服務器,將這些緩存服務器分佈到用戶訪問相對集中的地區或網絡中,在用戶訪問網站時,利用全局負載技術將用戶的訪問指向距離最近的工做正常的緩存服務器上,由緩存服務器直接響應
能夠將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 | 一個通常性的警告,表示在實體內容體中可能存在錯誤。 |
應用層:文件傳輸,經常使用協議HTTP,snmp,FTP ,
表示層:數據格式化,代碼轉換,數據加密,
會話層:創建,解除會話
傳輸層:提供端對端的接口,tcp,udp
網絡層:爲數據包選擇路由,IP,icmp
數據鏈路層:傳輸有地址的幀
物理層:二進制的數據形式在物理媒體上傳輸數據
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能夠直接進行轉發與負載均衡,部署簡單多了
1.TCP/IP模型是一系列網絡協議的總稱,這些協議的目的是使得計算機之間能夠進行信息交換,
2.TCP/IP模型四層架構從下到上分別是鏈路層,網絡層,傳輸層,應用層
3.鏈路層的做用是負責創建電路鏈接,是整個網絡的物理基礎,典型的協議包括以太網,ADSL等,
4.網絡層負責分配地址和傳送二進制數據,主要協議是IP協議,
5.傳輸層負責傳送文本數據,主要協議是TCP
7.應用層負責傳送各類最終形態的數據,是直接與用戶信息打交道的層,主要協議是http,ftp等
HTTP 1.0
HTTP1.1
HTTP2.0
1.OPTIONS: 用於獲取目的資源所支持的通訊選項,好比說服務器支持的請求方式等等。
2.TRACE:實現沿通向目標資源的路徑的消息環回(loop-back)測試 ,提供了一種實用的 debug 機制。
3.CONNECT
全部通用服務器必須支持GET和HEAD方法。全部其餘方法都是可選的。
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的規定和瀏覽器/服務器的限制,致使他們在應用過程當中體現出一些不一樣。
301:永久移動,請求的網頁已永久移動到新的位置,服務器返回此響應,會自動將請求者轉到新位置
302:歷史移動,服務器目前從不一樣位置的網頁響應請求,但請求者應繼續使用原有位置來繼續之後的請求,
1.進程,是併發執行的程序在執行過程當中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。
2.線程,是進程的一部分,一個沒有線程的進程能夠被看做是單線程的。線程有時又被稱爲輕權進程或輕量級進程,也是 CPU 調度的一個基本單位。
一個程序至少有一個進程,一個進程至少有一個線程,資源分配給進程,同一個進程下全部線程共享該進程的資源
1.共享的資源有
2.獨享的資源有
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,在這裏作善後工做也能夠
vue數據雙向綁定是經過數據劫持結合發佈者-訂閱者模式的方式來實現的。利用了 Object.defineProperty() 這個方法從新定義了對象獲取屬性值(get)和設置屬性值(set)。
webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個bundle。
gulp強調的是前端開發的工做流程,咱們能夠經過配置一系列的task,定義task處理的事務(例如文件壓縮合並、雪碧圖、啓動server、版本控制等),而後定義執行順序,來讓gulp執行這些task,從而構建項目的整個前端開發流程。
webpack是一個前端模塊化方案,更側重模塊打包,咱們能夠把開發中的全部資源(圖片、js文件、css文件等)都當作模塊,經過loader(加載器)和plugins(插件)對資源進行處理,打包成符合生產環境部署的前端資源。
前端模塊化就是複雜的文件編程一個一個獨立的模塊,好比js文件等等,分紅獨立的模塊有利於重用(複用性)和維護(版本迭代),這樣會引來模塊之間相互依賴的問題,因此有了commonJS 規範,AMD,CMD規範等等,以及用於js打包(編譯等處理)的工具 webpack
一個模塊是能實現特定功能的文件,有了模塊就能夠方便的使用別人的代碼,想要什麼功能就能加載什麼模塊。
1.Commonjs:開始於服務器端的模塊化,同步定義的模塊化,每一個模塊都是一個單獨的做用域,模塊輸出,modules.exports,模塊加載require()引入模塊。
2.AMD:中文名異步模塊定義的意思。
1.require JS 實現了 AMD 規範
1.主要用於解決下述兩個問題。
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,而若是須要依賴某些,也是異步去依賴,依賴後再執行某些方法。
簡介:觀察者模式或者說訂閱模式,它定義了對象間的一種一對多的關係,讓多個觀察者對象同時監聽某一個主題對象,當一個對象發生改變時,全部依賴於它的對象都將獲得通知。
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](obj);
}
}
}
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');
複製代碼
1.下降請求量:合併資源,減小HTTP 請求數,minify / gzip 壓縮,webP,圖片lazyLoad。
2.加快請求速度:預解析DNS,減小域名數,並行加載,CDN 分發。
3.緩存:HTTP 協議緩存請求,離線緩存 manifest,離線數據緩存localStorage。
4.渲染:JS/CSS優化(避免使用CSS表達式),加載順序(將CSS樣式表放在頂部,把javascript放在底部),服務端渲染,pipeline。
看到這裏,是否是驚歎於小姐姐的實力了?趕忙點贊、評論、關注、分享吧!!