1:閉包javascript
2:遊覽器渲染原理php
3:原型鏈css
4:thishtml
5:數據類型前端
6:繼承vue
7:數組方法java
8:call、apply、bindnode
9:promise 異步react
10:深淺拷貝jquery
11:函數節流
12:事件代理
13:let const var
14:ES6新特性
15:CSS佈局
16:rem、px、em單位
17:定位
18:rem適配
19:http協議
20:http緩存
21:從遊覽器到一個頁面展現經歷了什麼?
22:3次握手、4次揮手
23:Vue組件通訊、Vue生命週期
24:Vue雙向綁定原理
25:遊覽器緩存
26:本地存儲
27:webpackage
28:冒泡 、快排
29:數組去重
30:跨域3中方式
31:post和get區別
32:同步任務、微任務、宏任務
1:閉包的定義:
函數A中,有一個函數B,函數B中能夠訪問函數A中定義的變量或者數據,此時就行程了閉包。
<body> <script> /* 閉包 * 閉包的概念:函數A中,有一個函數B,函數B中能夠訪問函數A中定義的變量或者數據,此時就行程了閉包。 * 閉包的模式:函數閉包和對象閉包 * 閉包的做用:緩存數據,延長做用域鏈 * 閉包的優勢和缺點:緩存數據 * 閉包的應用: * ****/ // 1:函數式閉包 function f4() { var num4 = 40; return function () { console.log(num4); // 40 return num4; }; } var ff1 = f4(); var result = ff1(); console.log(result); // 40 // 2:對象是閉包 function f5() { var num5 = 50; var obj = { age : num5, } return obj; } var resultObj = f5(); var result5 = resultObj.age; console.log(result5); // 50 </script> <script> function fun(n,o) { console.log(o); return { fun:function(m) { return fun(m,n); } }; } console.log("***** 111111 *****") // 經典案例一: undefined 0 0 0 // fun(0,undefined).fun(1);==>>fun(1,0) var a = fun(0); // ---- undefined a.fun(1); // fun(1,0); 對象中的方法 ---- 0 a.fun(2); // fun(2,0); 對象中的方法 ---- 0 a.fun(3); // fun(3,0); 對象中的方法 ---- 0 console.log("***** 222222 *****") // 經典案例二: undefined 0 1 2 var b = fun(0) // fun(0,undefined) ---- undefined .fun(1) // fun(0,undefined).fun(1);==>>fun(1,0) ---- 0 .fun(2) // fun(1,0).fun(2);==>>fun(2,1) ---- 1 .fun(3); // fun(1,0).fun(2);==>>fun(2,1) ---- 2 console.log("***** 3333333 *****") // 經典案例三: undefined 0 1 1 var c = fun(0).fun(1); // fun(0, undefined).f(1)==>>fun(1,0) ----undefined ----0 c.fun(2); // fun(1, 0).f(2)==>>fun(2,1) ----1 c.fun(3); // fun(1, 0).f(3)==>>fun(3,1) ----1 </script> <script> // 經典案例四 console.log("***** 444444 *****") var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ console.log(this); // {name: "My Object", getNameFunc: ƒ} console.log(this.name); // My Object return function(){ console.log(this); // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} console.log(this.name); // The Window return this.name; }; } }; console.log(object.getNameFunc()());//The Window // 經典案例五 console.log("***** 555555 *****") var name1 = "The Window"; var object = { name1 : "My Object", getNameFunc : function(){ var that = this; console.log(this); // {name: "My Object", getNameFunc: ƒ} console.log(this.name1); // My Object return function(){ console.log(this); // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …} console.log(this.name1); // The Window console.log(that); // {name: "My Object", getNameFunc: ƒ} console.log(that.name1); // My Object return that.name1; }; } }; console.log(object.getNameFunc()());//My Object </script> </body>
可是常見的瀏覽器內核能夠分這四種:
Trident:(IE遊覽器) [ˈtraidnt] 三叉戟(舊時武器);
Gecko:(火狐遊覽器) [ˈɡekoʊ] 壁虎
Blink: (google) [bliŋk] v.眨眼睛; 閃爍;
Webkit:(safari)
渲染引擎首先經過網絡得到所請求文檔的內容,一般以8K分塊的方式完成。下面是渲染引擎在取得內容以後的基本流程:
解析html以構建dom樹 -> 構建render樹 -> 佈局render樹 -> 繪製render樹
這裏先解釋一下幾個概念,方便你們理解:
1:DOM Tree:瀏覽器將HTML解析成樹形的數據結構。
2:CSS Rule Tree:瀏覽器將CSS解析成樹形的數據結構。
3:Render Tree: DOM和CSSDOM合併後生成Render Tree。
4:layout: 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關係,從而去計算出每一個節點在屏幕中的位置。
5:painting: 按照算出來的規則,經過顯卡,把內容畫到屏幕上。
reflow(迴流):當瀏覽器發現某個部分發生了點變化影響了佈局,須要倒回去從新渲染,內行稱這個回退的過程叫 reflow。reflow 會從 <html> 這個 root frame 開始遞歸往下,依次計算全部的結點幾何尺寸和位置。reflow 幾乎是沒法避免的。如今界面上流行的一些效果,好比樹狀目錄的摺疊、展開(實質上是元素的顯 示與隱藏)等,都將引發瀏覽器的 reflow。鼠標滑過、點擊……只要這些行爲引發了頁面上某些元素的佔位面積、定位方式、邊距等屬性的變化,都會引發它內部、周圍甚至整個頁面的從新渲 染。一般咱們都沒法預估瀏覽器到底會 reflow 哪一部分的代碼,它們都彼此相互影響着。
repaint(重繪):改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部佈局的屬性時,屏幕的一部分要重畫,可是元素的幾何尺寸沒有變。
注意:(1)display:none 的節點不會被加入Render Tree,而visibility: hidden 則會,因此,若是某個節點最開始是不顯示的,設爲display:none是更優的。
(2)display:none 會觸發 reflow,而 visibility:hidden 只會觸發 repaint,由於沒有發現位置變化。
(3)有些狀況下,好比修改了元素的樣式,瀏覽器並不會馬上reflow 或 repaint 一次,而是會把這樣的操做積攢一批,而後作一次 reflow,這又叫異步 reflow 或增量異步 reflow。可是在有些狀況下,好比resize 窗口,改變了頁面默認的字體等。對於這些操做,瀏覽器會立刻進行 reflow。
來看看webkit的主要流程:
再來看看Geoko的主要流程:
Gecko 裏把格式化好的可視元素稱作「幀樹」(Frame tree)。每一個元素就是一個幀(frame)。 webkit 則使用」渲染樹」這個術語,渲染樹由」渲染對象」組成。webkit 裏使用」layout」表示元素的佈局,Gecko則稱爲」reflow」。Webkit使用」Attachment」來鏈接DOM節點與可視化信息以構建渲染樹。一個非語義上的小差異是Gecko在HTML與DOM樹之間有一個附加的層 ,稱做」content sink」,是建立DOM對象的工廠。
儘管Webkit與Gecko使用略微不一樣的術語,這個過程仍是基本相同的,以下:
1. 瀏覽器會將HTML解析成一個DOM樹,DOM 樹的構建過程是一個深度遍歷過程:當前節點的全部子節點都構建好後纔會去構建當前節點的下一個兄弟節點。
2. 將CSS解析成 CSS Rule Tree 。
3. 根據DOM樹和CSSOM來構造 Rendering Tree。注意:Rendering Tree 渲染樹並不等同於 DOM 樹,由於一些像Header或display:none的東西就不必放在渲染樹中了。
4. 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關係。下一步操做稱之爲layout,顧名思義就是計算出每一個節點在屏幕中的位置。
5. 再下一步就是繪製painting,即遍歷render樹,並使用UI後端層繪製每一個節點。
注意:上述這個過程是逐步完成的,爲了更好的用戶體驗,渲染引擎將會盡量早的將內容呈現到屏幕上,並不會等到全部的html都解析完成以後再去構建和佈局render樹。它是解析完一部份內容就顯示一部份內容,同時,可能還在經過網絡下載其他內容。
3: 原型鏈
在JavaScript中萬物都是對象,對象和對象之間也有關係,並非孤立存在的。
對象之間的繼承關係,在JavaScript中是經過構造函數prototype原型對象對象指向父類實例對象,直到指向Object對象爲止,這樣就造成了一個原型指向的鏈條
當咱們訪問對象的一個屬性或方法時,它會先在對象自身中尋找,若是有則直接使用,若是沒有則會去原型對象中尋找,若是找到則直接使用。若是沒有則去原型的原
型中尋找,直到找到Object對象的原型,Object對象的原型沒有原型,若是在Object原型中依然沒有找到,則返回undefined。
原型鏈是實例對象之間的關係,實例對象中__proto__之間的關係。
繼承是經過修改 構造函數的原型對象指向父類的實例對象。父類實例對象中的__proto__依次往上找、
1:普通函數中的this是誰?-----window
2:定時器方法中的this是誰?----window
3:對象.方法中的this是誰?----當前的實例對象
4:構造函數中的this是誰?-----實例對象
5:原型對象方法中的this是誰?---實例對象
<script> /* * * 函數中的this的指向 * * * 普通函數中的this是誰?-----window * 對象.方法中的this是誰?----當前的實例對象 * 定時器方法中的this是誰?----window * 構造函數中的this是誰?-----實例對象 * 原型對象方法中的this是誰?---實例對象 * * * */ //嚴格模式: // "use strict";//嚴格模式 // function f1() { // console.log(this);//window // } // f1(); //普通函數 // function f1() { // console.log(this); // } // f1(); //定時器中的this // setInterval(function () { // console.log(this); // },1000); //構造函數 // function Person() { // console.log(this); //對象的方法 // this.sayHi=function () { // console.log(this); // }; // } //原型中的方法 // Person.prototype.eat=function () { // console.log(this); // }; // var per=new Person(); // console.log(per); // per.sayHi(); // per.eat(); //BOM:中頂級對象是window,瀏覽器中全部的東西都是window的 </script>
1:js中的原始數據類型: number,string,boolean,undefined,null,object null和undefined數據是沒有太大意義的,null是頗有意義的---對象的位置講 NaN----不是一個數字,不是一個數字和一個數字計算--->結果就是NaN isNaN()--->判斷這個變量或者這個值是否是 不是一個數字---若是不是一個數字結果是true,若是是一個數字結果false 2:基礎類型
number數據類型----不管是整數仍是小數都是number數據類型的 string數據類型----字符串,獲取字符串的長度:變量名.length boolean數據類型---兩個值,true,false null----只有一個,null undefined----只有一個,undefined,一個變量聲明瞭,沒有賦值 object---對象-----面向對象的時候講解
3:是使用typeof 獲取變量的類型
console.log(typeof nll);//不是null, object
console.log(typeof undef);//undefined
var num = 10; var str = "小白"; var flag = true; var nll = null; var undef; var obj = new Object(); //是使用typeof 獲取變量的類型 console.log(typeof num);//number console.log(typeof str);//string console.log(typeof flag);//boolean console.log(String(nll));//是null console.log(typeof nll);//不是null, object
console.log(typeof undef);//undefined console.log(typeof obj);//object console.log(typeof(num)); //
1:原型鏈繼承:一是字面量重寫原型會中斷關係,使用引用類型的原型,而且子類型還沒法給超類型傳遞參數。
2:借用構造函數:沒法繼承屬性
3:組合繼承:其背後的思路是 使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。這樣,既經過在原型上定義方法實現了函數複用,又保證每一個實例都有它本身的屬性。
4:拷貝繼承:把一個對象中的屬性或者方法直接複製到另外一個對象中
<script> /* * * Array.isArray(對象)---->判斷這個對象是否是數組 * instanceof關鍵字 * .concat(數組,數組,數組,...) 組合一個新的數組 * .every(函數)--返回值是布爾類型,函數做爲參數使用,函數中有三個參數,第一個參數是元素的值,第二個參數是索引值,第三個參數是原來的數組(沒用) * 若是這個數組中的每一個元素的值都符合條件,最後才返回的是true * * .filter(函數);返回的是數組中每個元素都複合條件的元素,組成了一個新的數組 * * .push(值);--->把值追加到數組中,加到最後了---返回值也是追加數據以後的數組長度 * .pop();--->刪除數組中最後一個元素,返回值就是刪除的這個值 * .shift();--->刪除數組中第一個元素,返回值就是刪除的這個值 shift: 挪動,轉移, * .unshift();--->向數組的第一個元素前面插入一個新的元素,----返回值是插入後的程度 * .forEach(函數)方法---遍歷數組用---至關於for循環 * .indexOf(元素值);返回的是索引,沒有則是-1 * .join("字符串");----返回的是一個字符串 * .map(函數);--->數組中的每一個元素都要執行這個函數,把執行後的結果從新的所有的放在一個新的數組中 * .reverse();----->反轉數組 * .sort();---排序的,可能不穩定,若是不穩定,請寫MDN中的那個固定的代碼 * .arr.slice(開始的索引,結束的索引);把截取的數組的值放在一個新的數組中,可是不包含結束的索引對應的元素值 * .splice(開始的位置,要刪除的個數,替換的元素的值);通常是用於刪除數組中的元素,或者是替換元素,或者是插入元素 * * * */ //構造函數 // var arr1=new Array(); // //字面量的方式 // var arr2=[]; //對象是否是數組類型:兩種 //1 instanceof // var obj=[]; // console.log(obj instanceof Array);//false // // //2 使用數組的 // console.log(Array.isArray(obj));// // var arr=["a","b","c"]; // var newArr=Array.from(arr); // console.log(newArr); // var arr1=[10,20,30]; // var arr2=[40,50,60]; // console.log(arr1.concat(arr2)); // var arr=[1000,2000,3000]; // //a----: 元素的值 // //b----: 索引的值 // //c----:誰調用了這個方法,那麼c就是誰---->arr // var flag= arr.every(function (a,b) { // //console.log(a+"==="+b+"===="+c); // return a>2000;//數組中的每一個元素的值都要大於2000的狀況,最後才返回true // }); // var arr=["小明明lkko","小曹操674","小白白bd","笑眯眯a"]; // var flag=arr.every(function (ele,index) { // //數組中的每一個元素的長度是否是大於4 // return ele.length>4; // }); //console.log(flag); // var arr=[10,20,30,40,50,60,70,80]; // var newArr=arr.filter(function (ele) {//ele---每一個元素 // return ele>40; // }); // console.log(newArr); // var arr=[10,0,20,0,40,0,60,100]; // var newArr=arr.filter(function (ele) { // return ele!=0; // }); // console.log(newArr); // var arr=[10,20,30,40,50]; // var result=arr.unshift(100); // console.log(result); // console.log(arr); // // var arr = [10, 20, 30, 40]; // arr.forEach(function (ele,index) { // console.log(ele+'======'+index); // }); // var arr=[10,20,30,40]; // var index=arr.indexOf(300); // console.log(index); // var arr=["小白","小黑","小紅","小芳","小綠","小蘇"]; // var str=arr.join("|"); // console.log(str); // var numbers = [1, 4, 9]; // var roots = numbers.map(Math.sqrt); // console.log(roots); // var arr=[10,20,30,40,50]; // arr.reverse();//反轉 // console.log(arr); // var arr=[1,40,20,10,100]; // //a---arr[j] // //b---arr[j+1] // arr.sort(function (a,b) { // if(a>b){ // return 1; // }else if(a==b){ // return 0; // }else{ // return -1; // } // }); // console.log(arr); // // var arr=[10,20,30,40,50,60,70,80,90,100]; // var newArr= arr.slice(3,7); // console.log(newArr); var myFish = ['angel', 'clown', 'mandarin', 'sturgeon']; // myFish.splice(2, 0, 'drum'); // 在索引爲2的位置插入'drum' // myFish 變爲 ["angel", "clown", "drum", "mandarin", "sturgeon"] myFish.splice(2, 1); // 從索引爲2的位置刪除一項(也就是'drum'這一項) console.log(myFish); // myFish 變爲 ["angel", "clown", "mandarin", "sturgeon"] </script>
1: apply和call均可以讓函數或者方法來調用,傳入參數和函數本身調用的寫法不同,可是效果是同樣的,
apply傳入的是數組, call,傳入的是參數
2: apply和call能夠改變this的指向,
3: apply和call方法實際上並不在函數這個實例對象中,而是在Function的prototype,原型對象中。
4: bind方法是複製的意思,能夠在複製的時候傳遞參數,也能夠在複製以後傳遞參數
5: apply 和 call 是調用的時候改變this的指向
bind是 複製的時候修改this的指向,bind的時候沒有調用
1:什麼是Promise
Promise 是異步編程的一種解決方案,實際上是一個構造函數,
本身身上有all、reject、resolve這幾個方法,
原型上有then、catch等方法。
2: Promise對象有如下兩個特色。
(1)對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:pending(進行中)、fulfilled(已成功,完成)和rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變。
(2)一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved(已定型)。若是改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。
3:注意!我只是new了一個對象,並無調用它,咱們傳進去的函數就已經執行了,這是須要注意的一個細節。
// 1. 建立promise實例,在實例中執行異步操做(好比發送網絡請求) // 2. 異步操做成功時,調用reslove函數傳遞數據 // 3. 異步操做失敗時,調用reject函數傳遞錯誤信息 const promise = new Promise(function(resolve, reject) { // 異步操做 if (/* 異步操做成功 */){ resolve(value); } else { reject(error); } }); // 使用promise實例then方法接收reslove或reject返回的數據 promise.then(function(value) { // 此處數據即爲reslove回來的數據 // success }, function(error) { // 此處數據即爲reject回來的數據 // failure });
4: .then,方法裏面有兩個回調,一個處理成功的回調,一個處理失敗的回調。
5:.catch用法和.then中的rejected同樣
與then同級的另外一個方法,all方法,該方法提供了並行執行異步操做的能力,而且在全部異步操做執行完後而且執行結果都是成功的時候才執行回調。
all是等全部的異步操做都執行完了再執行then方法,那麼race方法就是相反的,誰先執行完成就先執行回調。
javaScritp的數據類型有:數值類型、字符串類型、布爾類型、null、undefined、對象(數組、正則表達式、日期、函數),大體分紅兩種:基本數據類型和引用數據類型,
其中:
(1)基本數據類型:數值、字符串、布爾、null、undefined (值類型)
(2)複雜(複合)數據類型:對象 (引用類型)
基本數據類型保存在棧內存,引用類型保存在堆內存中。根本緣由在於保存在棧內存的必須是大小固定的數據,引用類型的大小不固定,只能保存在堆內存中,可是能夠把它的地址寫在棧內存中以供咱們訪問
若是是基本數據類型,則按值訪問,操做的就是變量保存的值;若是是引用類型的值,咱們只是經過保存在變量中的引用類型的地址來操做實際對象。
2:基本類型的賦值
<script> // 1:基本類型的賦值 // 賦值的時候,在棧內存中從新開闢內存,存放變量b,因此在棧內存中分別存放着變量a、b各自的值,修改時互不影響 console.log("****** 111111 ******"); var a = 1; var b = a; //賦值 console.log(b)//1 a = 2; // 改變a的值 console.log(b)//1 console.log(a)//2 </script>
3:引用類型的複製
<script> // 2:引用類型的複製 // 所以,對於引用類型的複製,簡單賦值無用,須要拷貝。拷貝存在兩種類型:深拷貝與淺拷貝 console.log("****** 222222 ******"); var color1 = ['red','green']; var color2 = color1;//賦值 console.log(color2)//['red','green']; color1.push('black') ;//改變color1的值 console.log(color2)//['red','green','black'] console.log(color1)//['red','green','black'] </script>
淺拷貝只複製指向某個對象的指針,而不復制對象自己,新舊對象仍是共享同一塊內存。但深拷貝會另外創造一個如出一轍的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象
一、淺拷貝
淺拷貝只是拷貝基本類型的數據,若是父對象的屬性等於數組或另外一個對象,那麼實際上,子對象得到的只是一個內存地址,所以存在父對象被篡改的可能,淺拷貝只複製指向某個對象的指針,而不復制對象自己,新舊對象仍是共享同一塊內存
<body> <script> // 1:基本類型的賦值 // 賦值的時候,在棧內存中從新開闢內存,存放變量b,因此在棧內存中分別存放着變量a、b各自的值,修改時互不影響 console.log("****** 111111 ******"); var a = 1; var b = a; //賦值 console.log(b)//1 a = 2; // 改變a的值 console.log(b)//1 console.log(a)//2 </script> <script> // 2:引用類型的複製 // 所以,對於引用類型的複製,簡單賦值無用,須要拷貝。拷貝存在兩種類型:深拷貝與淺拷貝 console.log("****** 222222 ******"); var color1 = ['red','green']; var color2 = color1;//賦值 console.log(color2)//['red','green']; color1.push('black') ;//改變color1的值 console.log(color2)//['red','green','black'] console.log(color1)//['red','green','black'] </script> <script> console.log("****** 333333 ******"); var Nation = { nation: '中國' }; function extendCopy(p) { var c = {}; for (var i in p) { c[i] = p[i]; } return c; } var Doctor = extendCopy(Nation); Doctor.career = '醫生'; Doctor.nation = '美國'; console.log(Doctor.nation); // 美國 console.log(Nation.nation); // 中國 console.log(Doctor.career); // 醫生 console.log(Doctor.__proto_ === Nation.__proto_) // true // 這裏涉及到使用拷貝父對象的屬性實現繼承 console.log("****** 444444 淺拷貝 ******"); function simpleClone(initalObj) { var obj = {}; for ( var i in initalObj) { obj[i] = initalObj[i]; } return obj; } var obj = { a: "hello", b:{ a: "world", b: 21 }, c:["Bob", "Tom", "Jenny"], d:function() { alert("hello world"); } } var obj1 = simpleClone(obj); console.log('obj1=>>>',obj1); // 一、 obj1.c = ['mm', "Tom", "Jenny"]; // 一層,做爲總體,重寫,全改變;改變屬性值,不改變原對象 console.log('obj=>>>',obj); //obj.c => ["Bob", "Tom", "Jenny"] console.log('obj1=>>>',obj1); //obj1.c => ['mm', "Tom", "Jenny"] // 二、 obj1.c[0] = 'mm'; // 淺拷貝時,改變屬性的屬性值,改變原對象 console.log('obj=>>>',obj); //obj.c => ["Bob", "Tom", "Jenny"] console.log('obj1=>>>',obj1); //obj.c => ["mm", "Tom", "Jenny"] </script> </body>
2: 深拷貝
// 只要不是基本類型,好比數組和對象的話,就須要從新開始開闢一塊內存,來存儲。以後把地址給這個屬性。
<script> //深拷貝:拷貝仍是複製,深:把一個對象中全部的屬性或者方法,一個一個的找到.而且在另外一個對象中開闢相應的空間,一個一個的存儲到另外一個對象中 var obj1={ age:10, sex:"男", car:["奔馳","寶馬","特斯拉","奧拓"], dog:{ name:"大黃", age:5, color:"黑白色" } }; var obj2={};//空對象 //經過函數實現,把對象a中的全部的數據深拷貝到對象b中 function extend(a,b) { for(var key in a){ //先獲取a對象中每一個屬性的值 var item=a[key]; //判斷這個屬性的值是否是數組 if(item instanceof Array){ //若是是數組,那麼在b對象中添加一個新的屬性,而且這個屬性值也是數組 b[key]=[]; //調用這個方法,把a對象中這個數組的屬性值一個一個的複製到b對象的這個數組屬性中 extend(item,b[key]); }else if(item instanceof Object){//判斷這個值是否是對象類型的 //若是是對象類型的,那麼在b對象中添加一個屬性,是一個空對象 b[key]={}; //再次調用這個函數,把a對象中的屬性對象的值一個一個的複製到b對象的這個屬性對象中 extend(item,b[key]); }else{ //若是值是普通的數據,直接複製到b對象的這個屬性中 b[key]=item; } } } extend(obj1,obj2); console.dir(obj1); console.dir(obj2); </script>
深拷貝示意圖
函數節流: 指定時間間隔內只會執行一次任務;( 函數調用n秒後纔會執行,若是函數在n秒內再被調用的話則上次調用的函數不執行且從新計算執行時間 )
函數防抖: 任務頻繁觸發的狀況下,只有任務觸發的間隔超過指定間隔的時候,任務纔會執行。
函數節流和函數防抖都是經過減小實際邏輯處理過程的執行來提升事件處理函數運行性能的手段,並無實質上減小事件的觸發次數。
// 函數節流 var closure = true // 初始化執行狀態 document.getElementById("two").onscroll = () => { if (!closure) return // 若是開關關閉,說明上次執行沒有完成,則return closure = false // 關閉開關,開始執行 setTimeout(() => { console.log('節流執行函數') closure = true // 打開開關 }, 1000) // 延遲1秒執行函數,並打開開關,能夠進行下一次執行 } // 函數防抖 var shake // 聲明變量接受 document.getElementById("three").onscroll = () => { clearTimeout(shake) // 每執行一下清除延時器,迴歸初始狀態 shake = setTimeout(() => { console.log('函數防抖') // 執行函數 }, 1000) }
1:DOM事件流的三個階段:
事件捕獲
處於目標階段
事件冒泡
這裏再詳細介紹一下瀏覽器處理DOM事件的過程。對於事件的捕獲和處理,不一樣的瀏覽器廠商有不一樣的處理機制,這裏咱們主要介紹W3C對DOM2.0定義的標準事件。
DOM2.0模型將事件處理流程分爲三個階段:1、事件捕獲階段,2、事件目標階段,3、事件起泡階段。如圖:
事件捕獲:當某個元素觸發某個事件(如onclick),頂層對象document就會發出一個事件流,隨着DOM樹的節點向目標元素節點流去,直到到達事件真正發生的目標元素。在這個過程當中,事件相應的監聽函數是不會被觸發的。
事件目標:當到達目標元素以後,執行目標元素該事件相應的處理函數。若是沒有綁定監聽函數,那就不執行。
事件起泡:從目標元素開始,往頂層元素傳播。途中若是有節點綁定了相應的事件處理函數,這些函數都會被一次觸發。若是想阻止事件起泡,能夠使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)來組織事件的冒泡傳播。
2:事件委託:
事件委託是利用了事件的冒泡原理實現的,子元素的事件經過冒泡形式委託父元素執行
3:優勢:
第一個好處是效率高,好比,不用for循環爲子元素添加事件了
第二個好處是,js新生成的子元素也不用新爲其添加事件了,程序邏輯上比較方便
1.管理的函數變少了。不須要爲每一個元素都添加監聽函數。對於同一個父節點下面相似的子元素,能夠經過委託給父元素的監聽函數來處理事件。
2.能夠方便地動態添加和修改元素,不須要由於元素的改動而修改事件綁定。
3.JavaScript和DOM節點之間的關聯變少了,這樣也就減小了因循環引用而帶來的內存泄漏發生的機率。
缺點:
潛在的問題也許並不那麼明顯,可是一旦你注意到這些問題,你就能夠輕鬆地避免它們:
你的事件管理代碼有成爲性能瓶頸的風險,因此儘可能使它可以短小精悍。
不是全部的事件都能冒泡的。blur、focus、load和unload不能像其它事件同樣冒泡。事實上blur和focus能夠用事件捕獲而非事件冒泡的方法得到(在IE以外的其它瀏覽器中)。
在管理鼠標事件的時候有些須要注意的地方。若是你的代碼處理mousemove事件的話你趕上性能瓶頸的風險可就大了,由於mousemove事件觸發很是頻繁。而mouseout則由於其怪異的表現而變得很難用事件代理來管理。
JS中做用域有:全局做用域、函數做用域。沒有塊做用域的概念。ECMAScript 6(簡稱ES6)中新增了塊級做用域。塊做用域由 { } 包括,if語句和for語句裏面的{ }也屬於塊做用域。
es5:var es6:let 、const
1.var定義變量,沒有塊的概念,能夠跨塊訪問,不能跨函數訪問,不初始化出現undefined,不會報錯。
2.let定義變量,只能在塊做用域裏訪問,也不能跨函數訪問,對函數外部無影響。
3.const定義常量,只能在塊做用域裏訪問,也不能跨函數訪問,使用時必須初始化(即必須賦值),並且不能修改。
<script type="text/javascript"> // 塊做用域 { var a = 1; let b = 2; const c = 3; // c = 4; // 報錯,const不能修改 var aa; let bb; // const cc; // 報錯,必須初始化(即必須賦值) console.log(a); // 1 console.log(b); // 2 console.log(c); // 3 } console.log(a); // 1 // console.log(b); // 報錯,只能在塊做用域裏訪問 // console.log(c); // 報錯,只能在塊做用域裏訪問 // 函數做用域 (function A() { var d = 5; let e = 6; const f = 7; console.log(d); // 5 console.log(e); // 6 (在同一個{ }中,也屬於同一個塊,能夠正常訪問到) console.log(f); // 7 (在同一個{ }中,也屬於同一個塊,能夠正常訪問到) })(); // console.log(d); // 報錯,不能跨函數訪問 // console.log(e); // 報錯,不能跨函數訪問 // console.log(f); // 報錯,不能跨函數訪問 </script>
1:默認參數
var link = function (height, color, url) { var height = height || 50; var color = color || 'red'; var url = url || 'http://azat.co'; ... }
一切工做都是正常的,直到參數值是0後,就有問題了,由於在JavaScript中,0表示fasly,它是默認被hard-coded的值,而不能變成參數自己的值。固然,若是你非要用0做爲值,咱們能夠忽略這一缺陷而且使用邏輯OR就好了!但在ES6,咱們能夠直接把默認值放在函數申明裏:
var link = function(height = 50, color = 'red', url = 'http://azat.co') { ... }
2:模板對象
在其它語言中,使用模板和插入值是在字符串裏面輸出變量的一種方式。所以,在ES5,咱們能夠這樣組合一個字符串:
var name = 'Your name is ' + first + ' ' + last + '.'; var url = 'http://localhost:3000/api/messages/' + id;
幸運的是,在ES6中,咱們能夠使用新的語法$ {NAME},並把它放在反引號裏:
var name = `Your name is ${first} ${last}. `; var url = `http://localhost:3000/api/messages/${id}`;
在ES5中,咱們不得不使用如下方法來表示多行字符串:
var roadPoem = 'Then took the other, as just as fair,nt' + 'And having perhaps the better claimnt' + 'Because it was grassy and wanted wear,nt' + 'Though as for that the passing therent' + 'Had worn them really about the same,nt'; var fourAgreements = 'You have the right to be you.n You can only be you when you do your best.';
然而在ES6中,僅僅用反引號就能夠解決了:
var roadPoem = `Then took the other, as just as fair,
And having perhaps the better claim
Because it was grassy and wanted wear,
Though as for that the passing there
Had worn them really about the same,`;
var fourAgreements = `You have the right to be you.
You can only be you when you do your best.`;
4: 解構賦值
var data = $('body').data(), // data has properties house and mouse house = data.house, mouse = data.mouse;
以及在node.js中用ES5是這樣:
var jsonMiddleware = require('body-parser').jsonMiddleware ; var body = req.body, // body has username and password username = body.username, password = body.password;
在ES6,咱們能夠使用這些語句代替上面的ES5代碼:
var { house, mouse} = $('body').data(); // we'll get house and mouse variables var {jsonMiddleware} = require('body-parser'); var {username, password} = req.body;
5:加強的對象字面量
6:箭頭函數<script> // 箭頭函數至關於匿名函數,箭頭是標識。沒有語義,箭頭前面是參數,後面是函數體 // 1: 箭頭函數 沒有參數 () => {函數聲明} console.log("****** 1: 箭頭函數 沒有參數 *******"); var f10 = () => { return 10; } var result10 = f10(); console.log(result10); // 2: 箭頭函數 一個參數 // 方式1:(單一參數) => {函數聲明} console.log("****** 2: 箭頭函數 一個參數 方式1: *******"); var f20 = (a) => { return a; } var result20 = f20(20); console.log(result20); // 方式2:單一參數 => {函數聲明} console.log("****** 2: 箭頭函數 一個參數 方式2: *******"); var f21 = a => { return a; } var result21 = f21(21); console.log(result21); // 3. 多個參數須要用到小括號,參數間逗號間隔 (參數1, 參數2, …, 參數N) => { 函數聲明 } console.log("****** 3. 多個參數須要用到小括號,參數間逗號間隔 *******"); var f30 = (a, b, c) => { return a + b + c; } var result30 = f30(10, 20, 30); console.log(result30); // 4. 函數體一條語句 // (參數1, 參數2, …, 參數N) => 表達式(單一) // //至關於:(參數1, 參數2, …, 參數N) =>{ return 表達式; } console.log("****** 4. 函數體一條語句 方式1: *******"); var f40 = (a, b, c) => a + b + c; var result40 = f40(10, 20, 30); console.log(result40); // 5:多條語句 (參數1, 參數2, …, 參數N) => { 函數聲明 } console.log("****** 5:多條語句 *******"); var add = (a, b) => { if (typeof a == 'number' && typeof b == 'number') { return a + b } else { return 0 } } var result50 = add(10, 20); console.log(result50); // 6:只有一個return // 當箭頭函數的函數體只有一個 `return` 語句時,能夠省略 `return` 關鍵字和方法體的花括號 console.log("****** 6:只有一個返回值 *******"); var f60 = (a, b, c) => { a + b + c; } var result60 = f60(10, 20, 30); console.log(result60); // undefined var f61 = (a, b, c) => a + b + c; var result61 = f61(10, 20, 30); console.log(result61); // 60 var f62 = (a, b, c) => { return a + b + c; } var result62 = f62(10, 20, 30); console.log(result62); // 60 // 7. 返回對象時須要用小括號包起來,由於大括號被佔用解釋爲代碼塊了 console.log("****** 7. 返回對象時須要用小括號包起來 *******"); var f70 = (a, b) => { var c = a + b; return ({ name : "張三", age : 10, }); } var obj = f70(); console.log(obj); // 8: this console.log("****** 8: this *******"); var obj = { birth: 1990, getAge: function () { console.log(this); var b = this.birth; // 1990 var fn = function () { console.log(this); return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn(); } }; console.log(obj.getAge()) ; // NaN var obj1 = { birth: 1990, getAge: function () { var b = this.birth; // 1990 console.log(this); var fn = () => { console.log(this); return new Date().getFullYear() - this.birth; // this指向obj對象 } return fn(); } }; console.log(obj1.getAge()) ; // 29 // 9:經過 call 或 apply 調用 因爲 箭頭函數沒有本身的this指針,經過 call() 或 apply() 方法調用一個函數時,只能傳遞參數 // 注意 // 10: typeof運算符和普通的function同樣 console.log("****** 10: typeof運算符和普通的function同樣 *******"); var func = a => a console.log(typeof func); // "function" // instanceof也返回true,代表也是Function的實例 console.log(func instanceof Function); // true // 11. 不能使用argument console.log("****** 11. 不能使用argument *******"); var func = () => { console.log(arguments) } func(55) // Uncaught ReferenceError: arguments is not defined // 12: 箭頭函數不能用做構造器 箭頭函數也沒有原型 </script>
var wait1000 = new Promise(function(resolve, reject) { setTimeout(resolve, 1000); }).then(function() { console.log('Yay!'); });
用ES6的箭頭函數:
var wait1000 = new Promise((resolve, reject)=> { setTimeout(resolve, 1000); }).then(()=> { console.log('Yay!'); });
8:塊做用域
JS中做用域有:全局做用域、函數做用域。沒有塊做用域的概念。ECMAScript 6(簡稱ES6)中新增了塊級做用域。塊做用域由 { } 包括,if語句和for語句裏面的{ }也屬於塊做用域。
es5:var es6:let 、const
1.var定義變量,沒有塊的概念,能夠跨塊訪問,不能跨函數訪問,不初始化出現undefined,不會報錯。
2.let定義變量,只能在塊做用域裏訪問,也不能跨函數訪問,對函數外部無影響。
3.const定義常量,只能在塊做用域裏訪問,也不能跨函數訪問,使用時必須初始化(即必須賦值),並且不能修改。
一、ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,做爲對象的模板。經過class
關鍵字,能夠定義類。
//定義類 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } }
二、構造函數的prototype
屬性,在ES6的「類」上面繼續存在。事實上,類的全部方法都定義在類的prototype
屬性上面。
1:浮動佈局
2:絕對定位佈局
px、em、rem都是計量單位,都能表示尺寸,可是有有所不一樣,並且其各有各的優缺點。
Px表示「絕對尺寸」(並不是真正的絕對),實際上就是css中定義的像素(此像素與設備的物理像素有必定的區別,後續詳細說明見文末說明1),利用px設置字體大小及元素寬高等比較穩定和精確。Px的缺點是其不能適應瀏覽器縮放時產生的變化,所以通常不用於響應式網站。
em表示相對尺寸,其相對於當前對象內文本的font-size(若是當前對象內文本的font-size計量單位也是em,則當前對象內文本的font-size的參考對象爲父元素文本font-size)。使用em能夠較好的相應設備屏幕尺寸的變化,可是在進行元素設置時都須要知道父元素文本的font-size及當前對象內文本的font-size,若有遺漏可能會致使錯誤。
rem也表示相對尺寸,其參考對象爲根元素<html>的font-size,所以只須要肯定這一個font-size。
| 邊偏移屬性 | 描述 |
| ------ | ----------------------- |
| top | 頂端偏移量,定義元素相對於其父元素上邊線的距離 |
| bottom | 底部偏移量,定義元素相對於其父元素下邊線的距離 |
| left | 左側偏移量,定義元素相對於其父元素左邊線的距離 |
| right | 右側偏移量,定義元素相對於其父元素右邊線的距離 |
| 值 | 描述 |
| -------- | ------------------------ |
| static | 自動定位(默認定位方式) |
| relative | 相對定位,相對於其原文檔流的位置進行定位 |
| absolute | 絕對定位,相對於其上一個已經定位的父元素進行定位 |
| fixed | 固定定位,相對於瀏覽器窗口進行定位 |
靜態定位是全部元素的默認定位方式,當position屬性的取值爲static時,能夠將元素定位於靜態位置。 所謂靜態位置就是各個元素在HTML文檔流中默認的位置。
上面的話翻譯成白話: 就是網頁中全部元素都默認的是靜態定位哦! 其實就是標準流的特性。
在靜態定位狀態下,沒法經過邊偏移屬性(top、bottom、left或right)來改變元素的位置。
PS: 靜態定位其實沒啥可說的。
靜態定位惟一的用處: 就是 取消定位。 position: static;
相對定位是將元素相對於它在標準流中的位置進行定位,當position屬性的取值爲relative時,能夠將元素定位於相對位置。
對元素設置相對定位後,能夠經過邊偏移屬性改變元素的位置,可是它在文檔流中的位置仍然保留。以下圖所示,便是一個相對定位的效果展現:
注意:
1. 相對定位最重要的一點是,脫離標準流。它能夠經過邊偏移移動位置,可是原來的所佔的位置,繼續佔有。
2. 其次,每次移動的位置,是以本身的左上角爲基點移動(相對於本身來移動位置)
就是說,相對定位的盒子仍在標準流中,它後面的盒子仍以標準流方式對待它。(相對定位不脫標)
若是說浮動的主要目的是 讓多個塊級元素一行顯示,
那麼定位的主要價值就是 移動位置, 讓盒子到咱們想要的位置上去。
[注意] 若是文檔可滾動,絕對定位元素會隨着它滾動,由於元素最終會相對於正常流的某一部分定位。
當position屬性的取值爲absolute時,能夠將元素的定位模式設置爲絕對定位。
注意:
絕對定位最重要的一點是,它能夠經過邊偏移移動位置,可是它徹底脫標,徹底不佔位置。
若全部父元素都沒有定位,以瀏覽器當前屏幕爲準對齊(document文檔)。
絕對定位是將元素依據最近的已經定位(絕對、固定或相對定位)的父元素(祖先)進行定位。
固定定位是絕對定位的一種特殊形式,相似於 正方形是一個特殊的 矩形。它以瀏覽器窗口做爲參照物來定義網頁元素。當position屬性的取值爲fixed時,便可將元素的定位模式設置爲固定定位。
當對元素設置固定定位後,它將脫離標準文檔流的控制,始終依據瀏覽器窗口來定義本身的顯示位置。無論瀏覽器滾動條如何滾動也無論瀏覽器窗口的大小如何變化,該元素都會始終顯示在瀏覽器窗口的固定位置。
固定定位有兩點:
1. 固定定位的元素跟父親沒有任何關係,只認瀏覽器。
2. 固定定位徹底脫標,不佔有位置,不隨着滾動條滾動。
記憶法: 就相似於孫猴子, 無父無母,好不容易找到一個可靠的師傅(瀏覽器),就聽的師傅的,別的都不聽。
ie6等低版本瀏覽器不支持固定定位。
跟 浮動 同樣, 元素添加了 絕對定位和固定定位以後, 元素模式也會發生轉換, 都轉換爲 行內塊模式
行內塊 的寬度和高度 跟內容有關係
** 所以 好比 行內元素 若是添加了 絕對定位或者 固定定位後 浮動後,能夠不用轉換模式,直接給高度和寬度就能夠了。**
| 定位模式 | 是否脫標占有位置 | 是否能夠使用邊偏移 | 移動位置基準 |
| ------------ | ---------- | --------- | ---------------- |
| 靜態static | 不脫標,正常模式 | 不能夠 | 正常模式 |
| 相對定位relative | 脫標,佔有位置 | 能夠 | 相對自身位置移動(自戀型) |
| 絕對定位absolute | 徹底脫標,不佔有位置 | 能夠 | 相對於定位父級移動位置(拼爹型) |
| 固定定位fixed | 徹底脫標,不佔有位置 | 能夠 | 相對於瀏覽器移動位置(認死理型) |
1:頁面內的全部有關尺寸大小的設定均可以用rem來代替,如用rem代替px的使用,能夠作到屏幕大內容按比例放大,
2:任何一個設備 1rem= 屏幕尺寸/750*32px。 這是任意設備一個字符的高度。
HTTP協議一般承載於TCP協議之上,在HTTP和TCP之間添加一個安全協議層(SSL或TSL),這個時候,就成了咱們常說的HTTPS。
默認HTTP的端口號爲80,HTTPS的端口號爲443。
1.Cache-Control
請求/響應頭,緩存控制字段,能夠說是控制http緩存的最高指令,要不要緩存也是它說了算。
它有如下經常使用值
1.1 no-store:全部內容都不緩存
1.2 no-cache:緩存,可是瀏覽器使用緩存前,都會請求服務器判斷緩存資源是不是最新,它是個比較高貴的存在,由於它只用不過時的緩存。
1.3 緩存過時機制
max-age=x(單位秒) 請求緩存後的X秒再也不發起請求,屬於http1.1屬性,與下方Expires(http1.0屬性)相似,但優先級要比Expires高。
1.4 s-maxage=x(單位秒) 代理服務器請求源站緩存後的X秒再也不發起請求,只對CDN緩存有效(這個在後面會細說)
1.5 public 客戶端和代理服務器(CDN)均可緩存
1.6 private 只有客戶端能夠緩存
2.Expires
響應頭,表明資源過時時間,由服務器返回提供,GMT格式日期,是http1.0的屬性,在與max-age(http1.1)共存的狀況下,優先級要低。
3.Last-Modified
響應頭,資源最新修改時間,由服務器告訴瀏覽器。
4.if-Modified-Since
請求頭,資源最新修改時間,由瀏覽器告訴服務器(其實就是上次服務器給的Last-Modified,請求又還給服務器對比),和Last-Modified是一對,它兩會進行對比。
過時時間,最後更新時間。
二、http緩存方案
1.md5/hash緩存
經過不緩存html,爲靜態文件添加MD5或者hash標識,解決瀏覽器沒法跳過緩存過時時間主動感知文件變化的問題。
一、清楚一些最重要的術語:
客戶端: 簡言之就是瀏覽器,如Chrome或Firefox。其主要做用是進行用戶交互,並將其轉換爲對另外一臺稱爲Web服務器的計算機的請求。雖然咱們一般使用瀏覽器訪問網絡,但您能夠將整個計算機視爲客戶端 - 服務器模型的「客戶端」。每一個客戶端計算機都有一個惟一的地址,稱爲IP地址,其餘計算機能夠用來識別它。
服務器: 鏈接到互聯網且具備IP地址的機器。服務器等待來自其餘機器(例如客戶機)的請求並對其進行響應。不一樣於您的計算機(即客戶端),服務器也具備IP地址並安裝運行特殊的服務器軟件,肯定如何響應來自瀏覽器的請求。 Web服務器的主要功能是將網頁存儲,處理和傳送給客戶端。有許多類型的服務器,包括Web服務器(在這篇文章中,咱們在談論Web服務器),數據庫服務器,文件服務器,應用程序服務器等。
IP地址: 互聯網協議地址。 TCP / IP網絡上的設備(計算機,服務器,打印機,路由器等)的數字標識符。互聯網上的每臺計算機都有一個IP地址,用於識別和與其餘計算機通訊。 IP地址有四組數字,以小數點分隔(例如244.155.65.2)。這被稱爲「邏輯地址」。爲了在網絡中定位設備,經過TCP / IP協議軟件將邏輯IP地址轉換爲物理地址。這個物理地址(即MAC地址)內置在您的硬件中。
TCP / IP: 傳輸控制協議/互聯網協議。最普遍使用的通訊協議。 「協議」是一些標準的規則。TCP / IP被用做經過網絡傳輸數據的標準。
ISP: 互聯網服務提供商。 ISP是客戶端和服務器之間的中間人。典型的ISP一般是「有線電視公司」。當您的瀏覽器收請求www.itheima.com, 時,它不會知道在哪裏尋找www.itheima.com, 所以,ISP的工做是進行DNS(域名系統)查找,以詢問查找的網站的IP地址。
DNS: 域名系統。跟蹤計算機的域名及其在互聯網上相應IP地址的分佈式數據庫。不要擔憂「分佈式數據庫」如何工做:只須要知道輸入www.github.com, 而不是IP地址就好了。
域名: 用於標識一個或多個IP地址。用戶使用域名(例如www.github.com, )訪問互聯網上的網站。當您在瀏覽器中鍵入域名時,DNS使用它來查找該給定網站的IP地址。
端口號: 一個16位整數,用於標識服務器上的特定端口,並始終與IP地址相關聯。它能夠用來識別服務器上能夠轉發網絡請求的特定進程。
主機: 鏈接到網絡的計算機 - 它能夠是客戶端,服務器或任何其餘類型的設備。每一個主機都有惟一的IP地址。對於www.google.com, 等網站,主機能夠是爲該網站的網頁提供服務的網絡服務器。主機和服務器概念常常混合,可是它們是兩個不一樣的東西。服務器是一種主機,但另外一方面,提供託管服務來維護多個Web服務器的機器能夠稱做主機。在這個意義上,您能夠從主機運行服務器。
HTTP: 超文本傳輸協議。 Web瀏覽器和Web服務器用於經過互聯網進行通訊的協議。
URL: 統一資源定位符。 URL識別特定的Web資源。一個簡單的例子是https://itheima.com/someone. URL指定協議(「https」),主機名(itheima.com)和文件名(某人的我的資料頁面)。用戶能夠從域名爲itheima.com的網絡主機經過HTTP獲取該URL所標識的Web資源。(很繞口嗎?)
URL:統一資源定位符,用於定位互聯網上的資源。
url對應有協議、域名、端口號。
協議是從該計算機獲取資源的方式,常見的協議有:http,https,ftp,file;不一樣協議有不一樣的通信內容格式;
首先,瀏覽器會解析輸入URL的域名去查找對應的IP地址
域名解析流程:
首先搜索瀏覽器自身的DNS緩存(緩存時間比較短,大概只有1分鐘,且只能容納1000條緩存),看自身的緩存中是否有與輸入域名對應的條目,並且沒有過時,若是有且沒有過時則解析到此結束。
若是瀏覽器自身的緩存裏面沒有找到對應的條目,那麼瀏覽器會搜索操做系統自身的DNS緩存,若是找到且沒有過時則中止搜索解析到此結束.
若是在系統DNS緩存也沒有找到,那麼嘗試讀取系統hosts文件(位於C:\Windows\System32\drivers\etc),看看這裏面有沒有該域名對應的IP地址,若是有則解析成功。
若是在hosts文件中也沒有找到對應的條目,瀏覽器就會發起一個DNS的系統調用,就會向
本地配置的首選DNS服務器(通常是電信運營商提供的)發起域名解析請求,(經過的是UDP協議向DNS的53端口發起請求,這個請求是遞歸的請求,也就是運營商的DNS服務器必須得提供給咱們該域名的IP地址),運營商的DNS服務器首先查找自身的緩存,找到對應的條目,且沒有過時,則解析成功。
1.首先 是找到根域的DNS的IP地址,向其發起請求:(請問www.xxxxxx.com這個域名的IP地址是多少啊?),根域發現這是一個頂級域com域的一個域名,因而就告訴運營商的DNS我不知道這個域名的IP地址,可是我知道com域的IP地址,你去找它去,因而運營商的DNS就獲得了com域的IP地址;
2.而後 com域的IP地址發起了請求:(請問www.xxxxxx.com這個域名的IP地址是多少啊?),com域這臺服務器告訴運營商的DNS我不知道www.xxxxxx.com這個域名的IP地址,可是我知道xxxxxx.com這個域的DNS地址,你去找它要IP地址去;
3.因而運營商的DNS又向xxxxxx.com這個域名的DNS地址(這個通常就是由域名註冊商提供的,像萬網,新網等)發起請求:(請問www.xxxxxx.com這個域名的IP地址是多少啊?),這個時候xxxxxx.com域的DNS服務器一查,誒,果然在我這裏,因而就把找到的結果發送給運營商的DNS服務器;
這個時候運營商的DNS服務器就拿到了www.xxxxxx.com這個域名對應的IP地址,並返回給操做系統內核,內核又把結果返回給瀏覽器,終於瀏覽器拿到了www.xxxxxx.com對應的IP地址,進行下一步操做。
拿到域名對應的IP地址以後,User-Agent(通常是指瀏覽器)向WEB服務器發起TCP的鏈接請求:發起TCP的3次握手。
通過TCP 3次握手以後,瀏覽器會經過tcp鏈接向遠程服務器發送 HTTP 的 GET請求。
服務器端WEB程序接收到http請求之後,就開始處理該請求,處理以後就返回給瀏覽器html文件。
瀏覽器拿到index.html文件後,就開始解析其中的html代碼,遇到js/css/image等靜態資源時,就向服務器端去請求下載(會使用多線程下載,每一個瀏覽器的線程數不同),這個時候就用上keep-alive特性了,創建一次HTTP鏈接,能夠請求多個資源,下載資源的順序就是按照代碼裏的順序。
HTML字符串被瀏覽器接受後,就會被一句句讀取解析
解析到link標籤後,從新發送請求獲取css
解析到script標籤後發送請求獲取js
解析到img標籤後發送請求獲取圖片資源等
瀏覽器根據html和css計算獲得渲染樹,繪製到屏幕上,js會被執行。
4次揮手斷開
其中,步驟2的具體過程是:
瀏覽器緩存:瀏覽器會記錄DNS一段時間,所以,只是第一個地方解析DNS請求;
操做系統緩存:若是在瀏覽器緩存中不包含這個記錄,則會使系統調用操做系統,獲取操做系統的記錄(保存最近的DNS查詢緩存);
路由器緩存:若是上述兩個步驟均不能成功獲取DNS記錄,繼續搜索路由器緩存;
ISP緩存:若上述均失敗,繼續向ISP搜索。
1.爲何創建鏈接協議是三次握手,而關閉鏈接倒是四次握手呢?
這是由於服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它能夠把ACK和SYN(ACK起應答做用,而SYN起同步做用)放在一個報文裏來發送。但關閉鏈接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你全部的數據都所有發送給對方了,因此你不能夠立刻關閉SOCKET,也即你可能還須要發送一些數據給對方以後,再發送FIN報文給對方來表示你贊成如今能夠關閉鏈接了,因此它這裏的ACK報文和FIN報文多數狀況下都是分開發送的。
位碼即tcp標誌位,有6種標示:
SYN(synchronous創建聯機)
ACK(acknowledgement 確認)
PSH(push傳送)
FIN(finish結束)
RST(reset重置)
URG(urgent緊急)
Sequence number(順序號碼)
Acknowledge number(確認號碼)
1.1:父組件聲明數據
1.2:子組件聲明屬性
1.3:子組件引用的使用綁定屬性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script> </head> <body> <div id="app"> <!-- 3:父組件,能夠在引用子組件的時候, 經過 屬性綁定(v-bind:) 的形式, 把 須要傳遞給 子組件的數據,以屬性綁定的形式,傳遞到子組件內部,供子組件使用 --> <com1 v-bind:parentmsg="msg"></com1> </div> <script> // 建立 Vue 實例,獲得 ViewModel var vm = new Vue({ el: '#app', data: { // 1:父組件聲明屬性 msg msg: '123 啊-父組件中的數據' }, methods: {}, components: { // 結論:通過演示,發現,子組件中,默認沒法訪問到 父組件中的 data 上的數據 和 methods 中的方法 com1: { data() { // 注意: 子組件中的 data 數據,並非經過 父組件傳遞過來的,而是子組件自身私有的,好比: 子組件經過 Ajax ,請求回來的數據,均可以放到 data 身上; // data 上的數據,都是可讀可寫的; return { title: '123', content: 'qqq' } }, template: '<h1 @click="change">這是子組件 --- {{ parentmsg }}</h1>', // 2:子組件聲明傳過來的屬性 parentmsg // 注意: 組件中的 全部 props 中的數據,都是經過 父組件傳遞給子組件的 // props 中的數據,都是隻讀的,沒法從新賦值 props: ['parentmsg'], // 把父組件傳遞過來的 parentmsg 屬性,先在 props 數組中,定義一下,這樣,才能使用這個數據 directives: {}, filters: {}, components: {}, methods: { change() { this.parentmsg = '被修改了' } } } } }); </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./lib/vue-2.4.0.js"></script> </head> <body> <div id="app"> <!-- 2:父組件向子組件 傳遞 方法,使用的是 事件綁定機制; v-on, 當咱們自定義了 一個 事件屬性以後,那麼,子組件就可以,經過某些方式,來調用 傳遞進去的 這個 方法了 --> <com2 @func="show"></com2> </div> <template id="tmpl"> <div> <h1>這是 子組件</h1> <input type="button" value="這是子組件中的按鈕 - 點擊它,觸發 父組件傳遞過來的 func 方法" @click="myclick"> </div> </template> <script> // 定義了一個字面量類型的 組件模板對象 var com2 = { template: '#tmpl', // 經過指定了一個 Id, 表示 說,要去加載 這個指定Id的 template 元素中的內容,看成 組件的HTML結構 data() { return { sonmsg: { name: '小頭兒子', age: 6 } } }, methods: { myclick() { //3: 當點擊子組件的按鈕的時候,如何 拿到 父組件傳遞過來的 func 方法,並調用這個方法??? // emit 英文原意: 是觸發,調用、發射的意思 // this.$emit('func123', 123, 456) this.$emit('func', this.sonmsg) } } } // 建立 Vue 實例,獲得 ViewModel var vm = new Vue({ el: '#app', data: { datamsgFormSon: null }, methods: { // 1:聲明方法 show(data) { // console.log('調用了父組件身上的 show 方法: --- ' + data) // console.log(data); this.datamsgFormSon = data; } }, components: { com2 // com2: com2 } }); </script> </body> </html>
1:v-model指令、數據視圖同步更新、使用的是ES5提供的Object.defineProperty()這個方法實現數據劫持
2:數據如何從模型同步到視圖?當模型中數據發生變化時會觸發Object.defineProperty的set方法,在這個方法內部可以劫持到數據的改變,而後就能夠在該方法內部通知視圖更新
3:視圖中的數據如何同步到模型中?(v-model指令是怎麼實現改變了元素中的數據同步到模型中)監聽表單元素的change事件,在change事件中能夠拿到用戶輸入的數據,而後
給模型中的數據賦值
緩存能夠說是性能優化中簡單高效的一種優化方式了。一個優秀的緩存策略能夠縮短網頁請求資源的距離,減小延遲,而且因爲緩存文件能夠重複利用,還能夠減小帶寬,下降網絡負荷。
對於一個數據請求來講,能夠分爲發起網絡請求、後端處理、瀏覽器響應三個步驟。瀏覽器緩存能夠幫助咱們在第一和第三步驟中優化性能。好比說直接使用緩存而不發起請求,或者發起了請求但後端存儲的數據和前端一致,那麼就沒有必要再將數據回傳回來,這樣就減小了響應數據。
接下來的內容中咱們將經過緩存位置、緩存策略以及實際場景應用緩存策略來探討瀏覽器緩存機制。
如需獲取思惟導圖或想閱讀更多優質文章請猛戳GitHub博客
從緩存位置上來講分爲四種,而且各自有優先級,當依次查找緩存且都沒有命中的時候,纔會去請求網絡。
Service Worker 是運行在瀏覽器背後的獨立線程,通常能夠用來實現緩存功能。使用 Service Worker的話,傳輸協議必須爲 HTTPS。由於 Service Worker 中涉及到請求攔截,因此必須使用 HTTPS 協議來保障安全。Service Worker 的緩存與瀏覽器其餘內建的緩存機制不一樣,它可讓咱們自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,而且緩存是持續性的。
Service Worker 實現緩存功能通常分爲三個步驟:首先須要先註冊 Service Worker,而後監聽到 install 事件之後就能夠緩存須要的文件,那麼在下次用戶訪問的時候就能夠經過攔截請求的方式查詢是否存在緩存,存在緩存的話就能夠直接讀取緩存文件,不然就去請求數據。
當 Service Worker 沒有命中緩存的時候,咱們須要去調用 fetch 函數獲取數據。也就是說,若是咱們沒有在 Service Worker 命中緩存的話,會根據緩存查找優先級去查找數據。可是無論咱們是從 Memory Cache 中仍是從網絡請求中獲取的數據,瀏覽器都會顯示咱們是從 Service Worker 中獲取的內容。
Memory Cache 也就是內存中的緩存,主要包含的是當前中頁面中已經抓取到的資源,例如頁面上已經下載的樣式、腳本、圖片等。讀取內存中的數據確定比磁盤快,內存緩存雖然讀取高效,但是緩存持續性很短,會隨着進程的釋放而釋放。 一旦咱們關閉 Tab 頁面,內存中的緩存也就被釋放了。
那麼既然內存緩存這麼高效,咱們是否是能讓數據都存放在內存中呢?
這是不可能的。計算機中的內存必定比硬盤容量小得多,操做系統須要精打細算內存的使用,因此能讓咱們使用的內存必然很少。
當咱們訪問過頁面之後,再次刷新頁面,能夠發現不少數據都來自於內存緩存
內存緩存中有一塊重要的緩存資源是preloader相關指令(例如<link rel="prefetch">
)下載的資源。總所周知preloader的相關指令已是頁面優化的常見手段之一,它能夠一邊解析js/css文件,一邊網絡請求下一個資源。
須要注意的事情是,內存緩存在緩存資源時並不關心返回資源的HTTP緩存頭Cache-Control是什麼值,同時資源的匹配也並不是僅僅是對URL作匹配,還可能會對Content-Type,CORS等其餘特徵作校驗。
Disk Cache 也就是存儲在硬盤中的緩存,讀取速度慢點,可是什麼都能存儲到磁盤中,比之 Memory Cache 勝在容量和存儲時效性上。
在全部瀏覽器緩存中,Disk Cache 覆蓋面基本是最大的。它會根據 HTTP Herder 中的字段判斷哪些資源須要緩存,哪些資源能夠不請求直接使用,哪些資源已通過期須要從新請求。而且即便在跨站點的狀況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數據。絕大部分的緩存都來自 Disk Cache,關於 HTTP 的協議頭中的緩存字段,咱們會在下文進行詳細介紹。
瀏覽器會把哪些文件丟進內存中?哪些丟進硬盤中?
關於這點,網上說法不一,不過如下觀點比較靠得住:
Push Cache(推送緩存)是 HTTP/2 中的內容,當以上三種緩存都沒有命中時,它纔會被使用。它只在會話(Session)中存在,一旦會話結束就被釋放,而且緩存時間也很短暫,在Chrome瀏覽器中只有5分鐘左右,同時它也並不是嚴格執行HTTP頭中的緩存指令。
Push Cache 在國內可以查到的資料不多,也是由於 HTTP/2 在國內不夠普及。這裏推薦閱讀Jake Archibald
的 HTTP/2 push is tougher than I thought 這篇文章,文章中的幾個結論:
若是以上四種緩存都沒有命中的話,那麼只能發起請求來獲取資源了。
那麼爲了性能上的考慮,大部分的接口都應該選擇好緩存策略,一般瀏覽器緩存策略分爲兩種:強緩存和協商緩存,而且緩存策略都是經過設置 HTTP Header 來實現的。
瀏覽器與服務器通訊的方式爲應答模式,便是:瀏覽器發起HTTP請求 – 服務器響應該請求,那麼瀏覽器怎麼肯定一個資源該不應緩存,如何去緩存呢?瀏覽器第一次向服務器發起該請求後拿到請求結果後,將請求結果和緩存標識存入瀏覽器緩存,瀏覽器對於緩存的處理是根據第一次請求資源時返回的響應頭來肯定的。具體過程以下圖:
由上圖咱們能夠知道:
瀏覽器每次發起請求,都會先在瀏覽器緩存中查找該請求的結果以及緩存標識
瀏覽器每次拿到返回的請求結果都會將該結果和緩存標識存入瀏覽器緩存中
以上兩點結論就是瀏覽器緩存機制的關鍵,它確保了每一個請求的緩存存入與讀取,只要咱們再理解瀏覽器緩存的使用規則,那麼全部的問題就迎刃而解了,本文也將圍繞着這點進行詳細分析。爲了方便你們理解,這裏咱們根據是否須要向服務器從新發起HTTP請求將緩存過程分爲兩個部分,分別是強緩存和協商緩存。
強緩存:不會向服務器發送請求,直接從緩存中讀取資源,在chrome控制檯的Network選項中能夠看到該請求返回200的狀態碼,而且Size顯示from disk cache或from memory cache。強緩存能夠經過設置兩種 HTTP Header 實現:Expires 和 Cache-Control。
緩存過時時間,用來指定資源到期的時間,是服務器端的具體的時間點。也就是說,Expires=max-age + 請求時間,須要和Last-modified結合使用。Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過時時間前瀏覽器能夠直接從瀏覽器緩存取數據,而無需再次請求。
Expires 是 HTTP/1 的產物,受限於本地時間,若是修改了本地時間,可能會形成緩存失效。Expires: Wed, 22 Oct 2018 08:41:00 GMT
表示資源會在 Wed, 22 Oct 2018 08:41:00 GMT 後過時,須要再次請求。
在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁緩存。好比當Cache-Control:max-age=300
時,則表明在這個請求正確返回時間(瀏覽器也會記錄下來)的5分鐘內再次加載資源,就會命中強緩存。
Cache-Control 能夠在請求頭或者響應頭中設置,而且能夠組合使用多種指令:
public:全部內容都將被緩存(客戶端和代理服務器均可緩存)。具體來講響應可被任何中間節點緩存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中間的proxy能夠緩存資源,好比下次再請求同一資源proxy1直接把本身緩存的東西給 Browser 而再也不向proxy2要。
private:全部內容只有客戶端能夠緩存,Cache-Control的默認取值。具體來講,表示中間節點不容許緩存,對於Browser <-- proxy1 <-- proxy2 <-- Server,proxy 會老老實實把Server 返回的數據發送給proxy1,本身不緩存任何數據。當下次Browser再次請求時proxy會作好請求轉發而不是自做主張給本身緩存的數據。
no-cache:客戶端緩存內容,是否使用緩存則須要通過協商緩存來驗證決定。表示不使用 Cache-Control的緩存控制方式作前置驗證,而是使用 Etag 或者Last-Modified字段來控制緩存。須要注意的是,no-cache這個名字有一點誤導。設置了no-cache以後,並非說瀏覽器就再也不緩存數據,只是瀏覽器在使用緩存數據時,須要先確認一下數據是否還跟服務器保持一致。
no-store:全部內容都不會被緩存,即不使用強制緩存,也不使用協商緩存
max-age:max-age=xxx (xxx is numeric)表示緩存內容將在xxx秒後失效
s-maxage(單位爲s):同max-age做用同樣,只在代理服務器中生效(好比CDN緩存)。好比當s-maxage=60時,在這60秒中,即便更新了CDN的內容,瀏覽器也不會進行請求。max-age用於普通緩存,而s-maxage用於代理緩存。s-maxage的優先級高於max-age。若是存在s-maxage,則會覆蓋掉max-age和Expires header。
max-stale:能容忍的最大過時時間。max-stale指令標示了客戶端願意接收一個已通過期了的響應。若是指定了max-stale的值,則最大容忍時間爲對應的秒數。若是沒有指定,那麼說明瀏覽器願意接收任何age的響應(age表示響應由源站生成或確認的時間與當前時間的差值)。
min-fresh:可以容忍的最小新鮮度。min-fresh標示了客戶端不肯意接受新鮮度很少於當前的age加上min-fresh設定的時間之和的響應。
從圖中咱們能夠看到,咱們能夠將多個指令配合起來一塊兒使用,達到多個目的。好比說咱們但願資源能被緩存下來,而且是客戶端和代理服務器都能緩存,還能設置緩存失效時間等等。
其實這二者差異不大,區別就在於 Expires 是http1.0的產物,Cache-Control是http1.1的產物,二者同時存在的話,Cache-Control優先級高於Expires;在某些不支持HTTP1.1的環境下,Expires就會發揮用處。因此Expires實際上是過期的產物,現階段它的存在只是一種兼容性的寫法。
強緩存判斷是否緩存的依據來自因而否超出某個時間或者某個時間段,而不關心服務器端文件是否已經更新,這可能會致使加載文件不是服務器端最新的內容,那咱們如何獲知服務器端內容是否已經發生了更新呢?此時咱們須要用到協商緩存策略。
協商緩存就是強制緩存失效後,瀏覽器攜帶緩存標識向服務器發起請求,由服務器根據緩存標識決定是否使用緩存的過程,主要有如下兩種狀況:
協商緩存生效,返回304和Not Modified
協商緩存失效,返回200和請求結果
協商緩存能夠經過設置兩種 HTTP Header 實現:Last-Modified 和 ETag 。
瀏覽器在第一次訪問資源時,服務器返回資源的同時,在response header中添加 Last-Modified的header,值是這個資源在服務器上的最後修改時間,瀏覽器接收後緩存文件和header;
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
瀏覽器下一次請求這個資源,瀏覽器檢測到有 Last-Modified這個header,因而添加If-Modified-Since這個header,值就是Last-Modified中的值;服務器再次收到這個資源請求,會根據 If-Modified-Since 中的值與服務器中這個資源的最後修改時間對比,若是沒有變化,返回304和空的響應體,直接從緩存讀取,若是If-Modified-Since的時間小於服務器中這個資源的最後修改時間,說明文件有更新,因而返回新的資源文件和200
既然根據文件修改時間來決定是否緩存尚有不足,可否能夠直接根據文件內容是否修改來決定緩存策略?因此在 HTTP / 1.1 出現了 ETag
和If-None-Match
Etag是服務器響應請求時,返回當前資源文件的一個惟一標識(由服務器生成),只要資源有變化,Etag就會從新生成。瀏覽器在下一次加載資源向服務器發送請求時,會將上一次返回的Etag值放到request header裏的If-None-Match裏,服務器只須要比較客戶端傳來的If-None-Match跟本身服務器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。若是服務器發現ETag匹配不上,那麼直接以常規GET 200回包形式將新的資源(固然也包括了新的ETag)發給客戶端;若是ETag是一致的,則直接返回304知會客戶端直接使用本地緩存便可。
Last-Modified的時間單位是秒,若是某個文件在1秒內改變了屢次,那麼他們的Last-Modified其實並無體現出來修改,可是Etag每次都會改變確保了精度;若是是負載均衡的服務器,各個服務器生成的Last-Modified也有可能不一致。
強制緩存優先於協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match),協商緩存由服務器決定是否使用緩存,若協商緩存失效,那麼表明該請求的緩存失效,返回200,從新返回資源和緩存標識,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存。具體流程圖以下:
看到這裏,不知道你是否存在這樣一個疑問:若是什麼緩存策略都沒設置,那麼瀏覽器會怎麼處理?
對於這種狀況,瀏覽器會採用一個啓發式的算法,一般會取響應頭中的 Date 減去 Last-Modified 值的 10% 做爲緩存時間。
Cache-Control: no-cache
對於頻繁變更的資源,首先須要使用Cache-Control: no-cache
使瀏覽器每次都請求服務器,而後配合 ETag 或者 Last-Modified 來驗證資源是否有效。這樣的作法雖然不能節省請求數量,可是能顯著減小響應數據大小。
Cache-Control: max-age=31536000
一般在處理這類資源時,給它們的 Cache-Control 配置一個很大的 max-age=31536000
(一年),這樣瀏覽器以後請求相同的 URL 會命中強制緩存。而爲了解決更新的問題,就須要在文件名(或者路徑)中添加 hash, 版本號等動態字符,以後更改動態字符,從而達到更改引用 URL 的目的,讓以前的強制緩存失效 (其實並未當即失效,只是再也不使用了而已)。
在線提供的類庫 (如 jquery-3.3.1.min.js
, lodash.min.js
等) 均採用這個模式。
所謂用戶行爲對瀏覽器緩存的影響,指的就是用戶在瀏覽器如何操做時,會觸發怎樣的緩存策略。主要有 3 種:
Cache-control: no-cache
(爲了兼容,還帶了 Pragma: no-cache
),服務器直接返回 200 和最新內容。
<script> /* 1: 對瀏覽器來講,使用 Web Storage 存儲鍵值對比存儲 Cookie 方式更直觀,並且容量更大,它包含兩種:localStorage 和 sessionStorage sessionStorage(臨時存儲) :爲每個數據源維持一個存儲區域,在瀏覽器打開期間存在,包括頁面從新加載 localStorage(長期存儲) :與 sessionStorage 同樣,可是瀏覽器關閉後,數據依然會一直存在 */ /* 2. 保存數據到本地 sessionStorage.setItem('key', JSON.stringify(info)) */ const info = { name: 'Lee', age: 20, id: '001' }; localStorage.setItem("key1", "11"); localStorage.setItem("key1", "22"); sessionStorage.setItem('key', JSON.stringify(info)); localStorage.setItem('key', JSON.stringify(info)); /* 3: 從本地存儲獲取數據 */ var data1 = JSON.parse(sessionStorage.getItem('key')); var data2 = JSON.parse(localStorage.getItem('key')); console.log(data1); console.log(data2); /* 4: 本地存儲中刪除某個保存的數據 */ // sessionStorage.removeItem('key'); // localStorage.removeItem('key'); /* 5: 刪除全部保存的數據 */ // sessionStorage.clear(); // localStorage.clear(); window.addEventListener('storage', function (e) { console.log('key', e.key); console.log('oldValue', e.oldValue); console.log('newValue', e.newValue); console.log('url', e.url); }); </script>
開發是用到 -D
安裝babel插件
1: 運行 cnpm i @babel/core babel-loader @babel/plugin-transform-runtime -D 轉換工具
2: 運行 cnpm i @babel/preset-env @babel/preset-stage-0 -D 語法
3: 安裝可以識別轉換jsx語法的包 babel-preset-react
運行 cnpm i @babel/preset-react -D
4: 執行命令:cnpm i @babel/plugin-proposal-class-properties -D
5: 執行命令:cnpm i @babel/runtime -D
9: Vue組件
npm i vue-loader vue-template-compiler -D
10: vue-router
npm i vue-router -S
(1)對於評述算法優劣術語的說明
穩定:若是a本來在b前面,而a=b,排序以後a仍然在b的前面;
不穩定:若是a本來在b的前面,而a=b,排序以後a可能會出如今b的後面;
內排序:全部排序操做都在內存中完成;
外排序:因爲數據太大,所以把數據放在磁盤中,而排序經過磁盤和內存的數據傳輸才能進行;
時間複雜度: 一個算法執行所耗費的時間。
空間複雜度: 運行完一個程序所需內存的大小。
(2)排序算法圖片總結:
1: 冒泡排序
1.比較相鄰的兩個元素,若是前一個比後一個大,則交換位置, 一直到最後一個元素位置。 2.第一輪的時候最後一個元素應該是最大的一個。 3.按照步驟一的方法進行相鄰兩個元素的比較,這個時候因爲倒數第二個一個元素已是最大的了, 因此最後一個元素不用比較。
<script> // 1.冒泡排序: /* 1.冒泡排序: 1.比較相鄰的兩個元素,若是前一個比後一個大,則交換位置, 一直到最後一個元素位置。 2.第一輪的時候最後一個元素應該是最大的一個。 3.按照步驟一的方法進行相鄰兩個元素的比較,這個時候因爲倒數第二個一個元素已是最大的了, 因此最後一個元素不用比較。 */ console.log("****** 1:冒泡排序 ******") function sort(elements) { // 1: 外循環是輪數 for (var i = 0; i < elements.length - 1; i++) { // 2:內循環是第一步驟 for (var j = 0; j < elements.length - 1 - i; j ++){ if (elements[j] > elements[j + 1]){ var temp = elements[j]; elements[j] = elements[j + 1]; elements[j + 1] = temp; } } } } var elements = [3, 1, 5, 7, 2, 4, 9, 6, 10, 8]; console.log('before-- ' + elements); sort(elements); console.log('after-- ' + elements); </script>
2:快速排序
<script> // 2:快速排序 /* 1.以一個數爲基準(中間的數),比基準小的放到左邊,比基準大的放到右邊 2.再按此方法對這兩部分數據分別進行快速排序(遞歸進行) 3.不能再分後退出遞歸,並從新將數組合並 */ console.log("****** 2:快速排序 ******") function quickSort(elements) { if(elements.length <= 1){ return elements; } // pivot [ˈpɪvət] 中心點 // floor 向下取整 var pivotIndex = Math.floor(elements.length/2); // 刪除元素:array.splice(pivotIndex, 1); // 添加元素: array.splice(2, 0, "three"); 拼接函數(索引位置, 要刪除元素的數量, 元素) // splice() 方法可刪除從 index 處開始的零個或多個元素,而且用參數列表中聲明的一個或多個值來替換那些被刪除的元素。 若是從 arrayObject 中刪除了元素,則返回的是含有被刪除的元素的數組。 var arr = elements.splice(pivotIndex, 1); var pivot = arr[0]; var left = []; var right = []; for (var i = 0; i < elements.length; i ++ ){ if(elements[i] < pivot){ left.push(elements[i]); }else{ right.push(elements[i]); } } return quickSort(left).concat([pivot], quickSort(right)); } var arr = [49, 38, 65, 97, 13, 27, 49]; console.log(quickSort(arr)); </script>
<script> // indexof去重 function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return } var array = []; for (var i = 0; i < arr.length; i++) { if (array .indexOf(arr[i]) === -1) { array .push(arr[i]) } } return array; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)); // 2: filter去重 function unique1(arr) { return arr.filter(function(item, index, arr) { //當前元素,在原始數組中的第一個索引==當前索引值,不然返回當前元素 return arr.indexOf(item, 0) === index; }); } var arr1 = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique1(arr1)) //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}] </script>
1、什麼是跨域
JavaScript出於安全方面的考慮,不容許跨域調用其餘頁面的對象。那什麼是跨域呢,簡單地理解就是由於JavaScript同源策略的限制,a.com域名下的js沒法操做b.com或是c.a.com域名下的對象。
當協議、子域名、主域名、端口號中任意一個不相同時,都算做不一樣域。不一樣域之間相互請求資源,就算做「跨域」。
例如:http://www.abc.com/index.html 請求 http://www.efg.com/service.php。
有一點必需要注意:跨域並非請求發不出去,請求能發出去,服務端能收到請求並正常返回結果,只是結果被瀏覽器攔截了。之因此會跨域,是由於受到了同源策略的限制,同源策略要求源相同才能正常進行通訊,即協議、域名、端口號都徹底一致。
你們能夠參照下圖,有助於深刻理解跨域。
特別說明兩點:
第一:若是是協議和端口形成的跨域問題「前臺」是無能爲力的。
第二:在跨域問題上,域僅僅是經過「URL的首部」來識別而不會根據域名對應的IP地址是否相同來判斷。「URL的首部」能夠理解爲「協議, 域名和端口必須匹配」。
2、什麼是同源策略及其限制
同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。它的存在能夠保護用戶隱私信息,防止身份僞造等(讀取Cookie)。
同源策略限制內容有:
Cookie、LocalStorage、IndexedDB 等存儲性內容
DOM 節點
AJAX 請求不能發送
可是有三個標籤是容許跨域加載資源:
img
link
script
3、處理跨域方法一——JSONP
利用 <script>
元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的 JSON 數據。JSONP請求必定須要對方的服務器作支持才能夠。
JSONP和AJAX相同,都是客戶端向服務器端發送請求,從服務器端獲取數據的方式。但AJAX屬於同源策略,JSONP屬於非同源策略(跨域請求)
JSONP優勢是兼容性好,可用於解決主流瀏覽器的跨域數據訪問的問題。缺點是僅支持get方法具備侷限性。
聲明一個回調函數,其函數名(如fn)當作參數值,要傳遞給跨域請求數據的服務器,函數形參爲要獲取目標數據(服務器返回的data)。
建立一個<script>
標籤,把那個跨域的API數據接口地址,賦值給script的src,還要在這個地址中向服務器傳遞該函數名(能夠經過問號傳參:?callback=fn)。
服務器接收到請求後,須要進行特殊的處理:把傳遞進來的函數名和它須要給你的數據拼接成一個字符串,例如:傳遞進去的函數名是fn,它準備好的數據是fn([{"name":"jianshu"}])。
最後服務器把準備的數據經過HTTP協議返回給客戶端,客戶端再調用執行以前聲明的回調函數(fn),對返回的數據進行操做。
4、處理跨域方法二——CORS
整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。所以,實現CORS通訊的關鍵是服務器。只要服務器實現了CORS接口,就能夠跨源通訊。
JSONP的優缺點
JSONP的優勢是:它不像XMLHttpRequest
對象實現的Ajax請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中均可以運行,不須要XMLHttpRequest或ActiveX的支持;而且在請求完畢後能夠經過調用callback的方式回傳結果。
JSONP的缺點則是:它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種狀況,不能解決不一樣域的兩個頁面之間如何進行JavaScript
調用的問題。
CORS和JSONP對比
CORS與JSONP相比,無疑更爲先進、方便和可靠。
一、 JSONP只能實現GET請求,而CORS支持全部類型的HTTP請求。 二、 使用CORS,開發者能夠使用普通的XMLHttpRequest發起請求和得到數據,比起JSONP有更好的錯誤處理。 三、 JSONP主要被老的瀏覽器支持,它們每每不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS)。
方向代理跨域
經過訪問第三方服務器,讓第三方服務器幫咱們發送請求.
最爲常見的客戶端傳遞參數方式有兩種:
瀏覽器地址欄直接輸入:必定是GET請求;
超連接:必定是GET請求;
表單:能夠是GET,也能夠是POST,這取決與<form>的method屬性值;
GET請求和POST請求的區別:
- 緩存是針對URL來進行緩存的,GET請求因爲其參數是直接加在URL上-的,一種參數組合就有一種URL的緩存,能夠根據參數來進行一一對應,重複請求是冪等的(不論請求多少次,結果都同樣);
- 而POST請求的URL沒有參數,每次請求的URL都相同,數據體(HTTPBody)可能不一樣,沒法一一對應,因此緩存沒有意義
POST的安全是相對的,對於普通用戶來講他們看不到明文,數據封裝對他們來講就是屏障。可是對於專業人士,它們會抓包會分析,沒有加密的數據包對他們來講也是小case。因此POST僅僅是相對安全,惟有對數據進行加密纔會更安全。固然加密也有被破解的可能性,理論上全部的加密方式均可以破解,只是時間長短的問題。而加密算法要作的就是使得破解須要的時間儘可能長,越長越安全。因爲咱們也須要解密,加密算法太過複雜也並不是好事,這就要結合使用狀況進行折中或者足夠實際使用便可。繞的有點遠,具體的話,我將在後續的文章之中介說起,並介紹一些經常使用的加密算法。
HTTP協議中均沒有對GET和POST請求的數據大小進行限制,可是實際應用中它們一般受限於軟硬件平臺的設計和性能。