
在線訪問手冊(cè):
https://hanxueqing.github.io/Web-Front-end-Interview-Q-A/
github地址:
https://github.com/Hanxueqing/Web-Front-end-Interview-Q-A
原型鏈
JavaScript原型,原型鏈 ? 有什么特點(diǎn)?

* 原型對(duì)象也是普通的對(duì)象,是對(duì)象一個(gè)自帶隱式的 proto 屬性,原型也有可能有自己的原型,如果一個(gè)原型對(duì)象的原型不為null的話,我們就稱之為原型鏈。
* 原型鏈?zhǔn)怯梢恍┯脕砝^承和共享屬性的對(duì)象組成的(有限的)對(duì)象鏈。
* JavaScript的數(shù)據(jù)對(duì)象有那些屬性值?
writable:這個(gè)屬性的值是否可以改。
configurable:這個(gè)屬性的配置是否可以刪除,修改。
enumerable:這個(gè)屬性是否能在for…in循環(huán)中遍歷出來或在Object.keys中列舉出來。
value:屬性值。
* 當(dāng)我們需要一個(gè)屬性的時(shí),Javascript引擎會(huì)先看當(dāng)前對(duì)象中是否有這個(gè)屬性, 如果沒有的話,就會(huì)查找他的Prototype對(duì)象是否有這個(gè)屬性。
function clone(proto) {
function Dummy() { }
Dummy.prototype = proto;
Dummy.prototype.constructor = Dummy;
return new Dummy(); //等價(jià)于Object.create(Person);
}
function object(old) {
function F() {};
F.prototype = old;
return new F();
}
var newObj = object(oldObject);
Javascript如何實(shí)現(xiàn)繼承?
原型鏈繼承,借用構(gòu)造函數(shù)繼承,組合繼承,寄生式繼承,寄生組合繼承
數(shù)組
for-of和for-in的區(qū)別
遍歷數(shù)組的時(shí)候:
for-of通過key直接拿數(shù)組里面的值
for-in通過key拿數(shù)組的索引
遍歷對(duì)象的時(shí)候:
for-in通過key拿屬性值
for-of不可以遍歷對(duì)象
遍歷對(duì)象的方法,for of為什么不能遍歷對(duì)象?
for-of可以遍歷的:arr/map/string,因?yàn)樗麄冎袑?shí)現(xiàn)了Symbol.iterator屬性
for of如何遍歷對(duì)象
注意:for-of目前js實(shí)現(xiàn)的對(duì)象有array,string,argument以及后面更高級(jí)的set,Map
當(dāng)我們遍歷對(duì)象的時(shí)候可以使用for-in,不過這種遍歷方式會(huì)把原型上的屬性和方法也給遍歷出來,當(dāng)然我們可以通過hasOwnProperty來過濾掉除了實(shí)例對(duì)象的數(shù)據(jù),但是for-of在object對(duì)象上暫時(shí)沒有實(shí)現(xiàn),但是我們可以通過Symbol.iterator給對(duì)象添加這個(gè)屬性,我們就可以使用for-of了,代碼如下:
var p = {
name:'kevin',
age:2,
sex:'male'
}
Object.defineProperty(p,Symbol.iterator,{
enumberable:false,
configurable:false,
writable:false,
value:function(){
var _this = this;
var nowIndex = -1;
var key = Object.keys(_this);
return {
next:function(){
nowIndex++;
return {
value:_this[key[nowIndex]],
done:(nowIndex+1>key.length)
}
}
}
}
})
//這樣的話就可以直接通過for-of來遍歷對(duì)象了
for(var i of p){
console.log(i) //kevin,2,male
}
for-in遍歷數(shù)組存在的問題(for-in更適合遍歷對(duì)象)
var myArray = [1,2,4,5,6,7]
myArray.name = "殺馬特"
Array.prototype.method = function(){
console.log("length",this.length)
}
for(var index in myArray){
console.log("myArray",myArray[index])
}
使用for in 也可以遍歷數(shù)組,但是會(huì)存在以下問題:
index索引為字符串型數(shù)字,不能直接進(jìn)行幾何運(yùn)算
遍歷順序有可能不是按照實(shí)際數(shù)組的內(nèi)部順序
使用for in會(huì)遍歷數(shù)組所有的可枚舉屬性,包括原型。例如上栗的原型方法method和name屬性 所以for in更適合遍歷對(duì)象,不要使用for in遍歷數(shù)組。

使用for-of遍歷數(shù)組:
//for-of遍歷數(shù)組
var myArray = [1, 2, 4, 5, 6, 7]
myArray.name = "殺馬特"
Array.prototype.method = function () {
console.log("length", this.length)
}
for (var index of myArray) {//這里的index輸出的是value而不是索引
console.log("index", index)
}

for循環(huán)和forEach循環(huán) 哪個(gè)可以停止
for循環(huán)可以通過break停止,forEach一般是沒有辦法終止跳出。
如何實(shí)現(xiàn)forEach的終止跳出?
思路1 break 不能用,不可行,執(zhí)行后會(huì)報(bào)錯(cuò)!
var arr = ['a','b','c']
arr.forEach((item,index) => {
if(item === 'b') break
console.log(item)
})
思路2 return false 會(huì)跳出當(dāng)前的遍歷執(zhí)行==》a,c
var arr = ['a','b','c']
arr.forEach((item,index) => {
if(item === 'b') return false
console.log(item)
})
思路3 try...catch 語句跳出異常 //a exit done
try {
arr.forEach((item,index) => {
if(item === 'b') throw new Error('exist')
console.log(item)
})
} catch (e) {
if(e.message=='exist'){
console.log(e.message)
throw e
}
} finally {
console.log('done')
}
程序最后可以終止退出循環(huán),所以使用try...catch通過拋出異常的方式來終止程序繼續(xù)執(zhí)行是可行。
arr.map arr.filter arr.reduce的作用分別是?
【arr.map】
遍歷數(shù)組通常使用for循環(huán),ES5的話也可以使用forEach,ES5具有遍歷數(shù)組功能的還有map、filter、some、every、reduce、reduceRight等,只不過他們的返回結(jié)果不一樣。但是使用foreach遍歷數(shù)組的話,使用break不能中斷循環(huán),使用return也不能返回到外層函數(shù)
arr.map循環(huán)遍歷,并且將符合條件的元素放入一個(gè)新數(shù)組返回
var arr = [1,2,3]
let b = [];
//map循環(huán)遍歷,并且返回一個(gè)新數(shù)組
let a = arr.map(item=>{
if(item>2){
b.push(item)
return b
}
})
console.log(b) //返回符合條件的新數(shù)據(jù)[3]
因?yàn)閞eact中沒有v-for指令,所以循環(huán)渲染的時(shí)候需要用到arr.map方法來渲染視圖
【arr.filter】
arr.filter 過濾器 可以過濾掉不符合要求的元素,內(nèi)部返回false就直接過濾掉了,將符合條件的數(shù)據(jù)返回,也是不影響原數(shù)組。
//過濾器
arr = arr.filter(item=>{
return item>2
})
console.log(arr) //過濾出來符合條件的數(shù)據(jù)[3]
【arr.reduce】
reduce為數(shù)組中每一個(gè)元素依次執(zhí)行回調(diào)函數(shù),不包括數(shù)組中被刪除或從未被賦值的元素,接收四個(gè)參數(shù):初始值(或者上一次回調(diào)函數(shù)的返回值),當(dāng)前元素值,當(dāng)前索引,調(diào)用reduce的數(shù)組。reduce方法可以搞定的東西for循環(huán)或者forEach方法有時(shí)候也可以搞定。
var newArr = [4,5,6,7]
newArr.reduce((res,currentValue,index,arr)=>{//第一個(gè)參數(shù)為一個(gè)回調(diào)函數(shù)
console.log(res,currentValue,index) //4,5,1
})
//res:默認(rèn)為第一個(gè)值(4),currentValue:當(dāng)前值(5),index:當(dāng)前值的索引(1)
let result = newArr.reduce((res,currentValue,index,arr)=>{
console.log(res,currentValue) //第一次:4,5;第二次:9,6;第三次:15,7
return res+currentValue//第一次:9;第二次:15;第三次:22;最后結(jié)果為22
},10)//第二個(gè)參數(shù)為一個(gè)初始值,這里給它賦值為10
console.log(result)//現(xiàn)在res就變成了10,currentValue變成了4,依次累加,10+22 初始值+累加值
Filter、forEach、map、reduce之間的區(qū)別聯(lián)系?
作用
every():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)對(duì)每一項(xiàng)都返回 true ,則返回 true。
some():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)對(duì)任一項(xiàng)返回 true ,則返回 true
filter():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),返回該函數(shù)會(huì)返回 true 的項(xiàng)組成的數(shù)組。
forEach():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù)。這個(gè)方法沒有返回值。
map():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),返回每次函數(shù)調(diào)用的結(jié)果組成的數(shù)組。
相同點(diǎn)
他們的參數(shù)都一樣:
- 在每一項(xiàng)上運(yùn)行的函數(shù)(該函數(shù)有三個(gè)參數(shù))
- 函數(shù)第一個(gè)參數(shù):數(shù)組項(xiàng)的值
- 函數(shù)第二個(gè)參數(shù):數(shù)組項(xiàng)的索引
- 函數(shù)第三個(gè)參數(shù):數(shù)組對(duì)象本身
- 運(yùn)行該函數(shù)的作用域?qū)ο蟆绊憈his的值(可選)
區(qū)別
filter()、forEach()、map()、some()、every()都是對(duì)數(shù)組的每一項(xiàng)調(diào)用函數(shù)進(jìn)行處理。
區(qū)別:
– some()、every()的返回值 :true / false
– filter()、map()的返回值 :一個(gè)新數(shù)組
– forEach()無返回值。
使用filter()、forEach()、map()、some()、every()都不改變?cè)瓟?shù)組。
參考:js小記:filter()、forEach()、map()、reduce()、reduceRight()的區(qū)別
https://blog.csdn.net/b954960630/article/details/81432881
splice()
splice(index,len,[item]) 注釋:該方法會(huì)改變?cè)紨?shù)組。
//刪除起始下標(biāo)為1,長度為1的一個(gè)值(len設(shè)置1,如果為0,則數(shù)組不變)
var arr = ['a','b','c','d'];
arr.splice(1,1);
console.log(arr); //['a','c','d'];
//替換起始下標(biāo)為1,長度為1的一個(gè)值為‘ttt’,len設(shè)置的1
var arr = ['a','b','c','d'];
arr.splice(1,1,'ttt');
console.log(arr); //['a','ttt','c','d']
var arr = ['a','b','c','d'];
arr.splice(1,0,'ttt');
console.log(arr); //['a','ttt','b','c','d'] 表示在下標(biāo)為1處添加一項(xiàng)'ttt'
slice()
slice()方法可以基于當(dāng)前數(shù)組獲取指定區(qū)域元素 [start, end)
格式:數(shù)組.slice(start, end);
參數(shù):start和end都是下標(biāo),[start, end),獲取指定范圍內(nèi)的元素,生成新數(shù)組。(原數(shù)組是不會(huì)改變的。)
返回值:提取出來元素組成的新數(shù)組。
split()和join()的區(qū)別
join函數(shù)獲取一批字符串,然后用分隔符字符串將它們連接起來,從而返回一個(gè)字符串。
split()函數(shù)獲取一個(gè)字符串,然后在分隔符處將其斷開,從而返回一批字符串。
但是,這兩個(gè)函數(shù)之間的區(qū)別在于join可以使用任何分割字符串將多個(gè)字符串連接起來,而split()只能使用一個(gè)字符分隔符將字符串?dāng)嚅_。
簡單地說,如果你用split(),是把一串字符串(根據(jù)某個(gè)分隔符)分成若干個(gè)元素存放在一個(gè)數(shù)組里。而join是把數(shù)組中的字符串連接成一個(gè)長串,可以大體上認(rèn)為是split的逆操作。
push、pop、shift、unshift
push()方法可以在數(shù)組的末屬添加一個(gè)或多個(gè)元素shift()方法把數(shù)組中的第一個(gè)元素刪除unshift()方法可以在數(shù)組的前端添加一個(gè)或多個(gè)元素pop()方法把數(shù)組中的最后一個(gè)元素刪除
參考:數(shù)組的push()、pop()、shift()和unshift()方法
https://blog.csdn.net/qwe502763576/article/details/79055682
數(shù)組的方法
解析鏈接www.qq.com?name=jack&age=18&id=100,獲取其中的查詢參數(shù),并格式為js對(duì)象的代碼:如: {name:”jack”,age:18,id:100}
var str = "www.qq.com?name=jack&age=18";
var str2 = str.substring(str.lastIndexOf(“?")+1); //索引從后往前找
var arr = str2.split(“&”);//通過&符號(hào)去切割 切割成如下形式[‘name = jack’,’age=18’,'id=100']
var json = {};
for(var i=0;i<arr.length;i++){ //循環(huán)遍歷數(shù)組的長度
var newArr = arr[i].split(“=“); //拿到每一個(gè)字符串 拿到之后通過split進(jìn)行切割 [‘name’,’jack’]
json[newArr[0]] = newArr[1]; // key名=value
}
考查知識(shí)點(diǎn):數(shù)組截取方法/數(shù)組中字符串操作/對(duì)象的中括號(hào)會(huì)不會(huì)用
對(duì)象的、數(shù)組的、字符串的相關(guān)的使用的比較多的方法
下列代碼的運(yùn)行結(jié)果是:
var array1 = [1,2];
var array2 = array1; // 數(shù)組指向的是同一個(gè)地址
array1[0] = array2[1]; // 把a(bǔ)rray[1]拿出來放在array1[0]的位置變成2,2
array2.push(3);
console.log(array1);// [2,2,3]
console.log(array2);// [2,2,3]
var arr = [1,2]
arr2 = arr;//arr2和arr1指向的是同一塊內(nèi)存空間
arr2.push(3)
console.log(arr) //[1,2,3]
var arr = [1,2]
arr2 = arr
arr = [1,2,3] //arr 指向了新的內(nèi)存空間
arr2.push(4)
console.log(arr)//[1,2,3]
console.log(arr2)//[1,2,4]
var a = {n:1}
var b = a;
a.x = a = {n:2}
console.log(a); //a賦值了一個(gè)新的地址 輸出{n:2}
console.log(b); //b指向的是a原來的地址 輸出{n:1,x:{n:2}}
JS數(shù)組去重
- 利用splice方法
var arr = [10, 20, 30, 40, 50, 40, 30, 20, 20, 20, 20, 10];
function norepeat(arr){
for(var i = 0; i < arr.length - 1; i++){
for(var j = i + 1; j < arr.length; j++){
if(arr[i] === arr[j]){
arr.splice(j,1);
j--
}
}
}
}
alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
norepeat(arr);
alert(arr);//10,20,30,40,50
- 倒序刪除
//倒序刪除
function norepeat(arr){
for(var i = arr.length -1; i > 0; i--){
for(var j = i - 1;j >= 0; j--){
if(arr[i] === arr[j]){
arr.splice(j,1);
}
}
}
}
alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
norepeat(arr);
arr.reverse();
alert(arr);//10,20,30,40,50
- 利用對(duì)象的屬性不會(huì)重復(fù)這一特性,校驗(yàn)數(shù)組元素是否重復(fù)
function unique(arr){
var obj = {};
var result = [];
for(var i=0;i<arr.length;i++){
if(!obj[arr[i]]){
result.push(arr[i]);
obj[arr[i]] = true; //將每個(gè)數(shù)作為key,屬性值值為true,當(dāng)key名相同時(shí),判斷屬性值是否為false,不為false則不push進(jìn)數(shù)組
}
}
return result;
}
-
ES6中新增了數(shù)據(jù)類型set,set的一個(gè)最大的特點(diǎn)就是數(shù)據(jù)不重復(fù)。Set函數(shù)可以接受一個(gè)數(shù)組(或類數(shù)組對(duì)象)作為參數(shù)來初始化,利用該特性也能做到給數(shù)組去重。
Set集合是默認(rèn)去重復(fù)的,但前提是兩個(gè)添加的元素嚴(yán)格相等,所以5和"5"不相等,兩個(gè)new出來的字符串不相等。
下面展示了一種極為精巧的數(shù)組去重的方法
var newarr = [...new Set(array)];
//拓展 固定寫法
alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
//下述寫法的值,就是去重以后的數(shù)組。
arr = [...new Set(arr)];
alert(arr);//10,20,30,40,50
跨域
什么是跨域
由于 Javascript 同源策略的存在使得一個(gè)源中加載來自其它源中資源的行為受到了限制。即會(huì)出現(xiàn)跨域請(qǐng)求禁止。
通俗一點(diǎn)說就是如果存在協(xié)議、域名、端口或者子域名不同服務(wù)端,或一者為IP地址,一者為域名地址(在跨域問題上,域僅僅是通過“ url的首部 ”來識(shí)別而不會(huì)去嘗試判斷相同的IP地址對(duì)應(yīng)著兩個(gè)域或者兩個(gè)域是否同屬同一個(gè)IP),之中任意服務(wù)端旗下的客戶端發(fā)起請(qǐng)求其它服務(wù)端資源的訪問行動(dòng)都是跨域的,而瀏覽器為了安全問題一般都限制了跨域訪問,也就是不允許跨域請(qǐng)求資源。
同源策略
同協(xié)議
同域名
同端口號(hào)
解決跨域問題的主流方案是什么
Jsonp 利用script標(biāo)簽發(fā)起get請(qǐng)求不會(huì)出現(xiàn)跨域禁止的特點(diǎn)實(shí)現(xiàn)
window.name+iframe 借助中介屬性window.name實(shí)現(xiàn)
html5的 postMessage 主要側(cè)重于前端通訊,不同域下頁面之間的數(shù)據(jù)傳遞
Cors需要服務(wù)器設(shè)置header:Access-Control-Allow-Origin
Nginx反向代理 可以不需要目標(biāo)服務(wù)器配合,不過需要Nginx中轉(zhuǎn)服務(wù)器,用于轉(zhuǎn)發(fā)請(qǐng)求(服務(wù)端之間的資源請(qǐng)求不會(huì)有跨域限制)
參考:Nginx反向代理、CORS、JSONP等跨域請(qǐng)求解決方法總結(jié)
https://blog.csdn.net/diudiu5201/article/details/54808142
cors實(shí)現(xiàn)請(qǐng)求跨域
https://blog.csdn.net/badmoonc/article/details/82706246
JQuery跨域方式
- 修改ajax的請(qǐng)求頭(盡量不要用)
改成"Access-control-Allow-Origin"
通過PHP文件作為中轉(zhuǎn)站,進(jìn)行間接跨源(爬蟲)
JSONP跨域
JSONP原理
動(dòng)態(tài)創(chuàng)建script標(biāo)簽,src屬性連接接口地址,callback參數(shù)就是服務(wù)器返回給我們的數(shù)據(jù)。
缺點(diǎn):jsonp只支持get請(qǐng)求方式,post方式不支持。
JSONP跨域的流程
在資源加載進(jìn)來之前定義好一個(gè)函數(shù),這個(gè)函數(shù)接收一個(gè)參數(shù)(數(shù)據(jù)),函數(shù)里面利用這個(gè)參數(shù)做一些事情。
然后需要的時(shí)候通過script標(biāo)簽加載對(duì)應(yīng)的遠(yuǎn)程文件資源。
當(dāng)遠(yuǎn)程文件加載進(jìn)來的時(shí)候,就會(huì)去執(zhí)行我們前面定義好的函數(shù),并且把數(shù)據(jù)當(dāng)做這個(gè)函數(shù)中的參數(shù)傳進(jìn)去。
JSONP跨域的缺點(diǎn)
它只支持GET請(qǐng)求而不支持POST等其它類型的HTTP請(qǐng)求;
它只支持跨域HTTP請(qǐng)求這種情況,不能解決不同域的兩個(gè)頁面之間如何進(jìn)行JavaScript調(diào)用的問題
json文件不能用jsonp方法。
AJAX
什么是AJAX
AJAX是一種用于快速創(chuàng)建動(dòng)態(tài)網(wǎng)頁的技術(shù),通過在后臺(tái)與服務(wù)器進(jìn)行少量數(shù)據(jù)交換,AJAX可以實(shí)現(xiàn)網(wǎng)頁實(shí)現(xiàn)異步更新,這意味著可以在不加載整個(gè)網(wǎng)頁的情況下,對(duì)網(wǎng)頁的某部分進(jìn)行更新。
傳統(tǒng)的網(wǎng)頁(不使用AJAX)如果需要更新內(nèi)容,必須重載整個(gè)網(wǎng)頁。
AJAX的原理
思路:先解釋異步,再解釋ajax如何使用
Ajax的原理簡單來說通過XmlHttpRequest對(duì)象來向服務(wù)器發(fā)異步請(qǐng)求,從服務(wù)器獲得數(shù)據(jù),然后用javascript來操作DOM而更新頁面。這其中最關(guān)鍵的一步就是從服務(wù)器獲得請(qǐng)求數(shù)據(jù)。要清楚這個(gè)過程和原理,我們必須對(duì) XMLHttpRequest有所了解。
XMLHttpRequest是ajax的核心機(jī)制,它是在IE5中首先引入的,是一種支持異步請(qǐng)求的技術(shù)。簡單的說,也就是javascript可以及時(shí)向服務(wù)器提出請(qǐng)求和處理響應(yīng),而不阻塞用戶。達(dá)到無刷新的效果。
AJAX的優(yōu)點(diǎn)
AJAX不是新的編程語言,而是一種使用現(xiàn)有標(biāo)準(zhǔn)的新方法。
AJAX最大的優(yōu)點(diǎn)是在不重新加載整個(gè)頁面的情況下,可以與服務(wù)器交換數(shù)據(jù)并更新部分網(wǎng)頁內(nèi)容。
AJAX不需要任何瀏覽器插件,但需要用戶允許JavaScript在瀏覽器上運(yùn)行。
AJAX的缺點(diǎn)
AJAX干掉了Back和History功能,即對(duì)瀏覽器機(jī)制的破壞。 在動(dòng)態(tài)更新頁面的情況下,用戶無法回到前一個(gè)頁面狀態(tài),因?yàn)闉g覽器僅能記憶歷史記錄中的靜態(tài)頁面。一個(gè)被完整讀入的頁面與一個(gè)已經(jīng)被動(dòng)態(tài)修改過的頁面之間的差別非常微妙;用戶通常會(huì)希望單擊后退按鈕能夠取消他們的前一次操作,但是在Ajax應(yīng)用程序中,這將無法實(shí)現(xiàn)。
安全問題技術(shù)同時(shí)也對(duì)IT企業(yè)帶來了新的安全威脅,ajax技術(shù)就如同對(duì)企業(yè)數(shù)據(jù)建立了一個(gè)直接通道。這使得開發(fā)者在不經(jīng)意間會(huì)暴露比以前更多的數(shù)據(jù)和服務(wù)器邏輯。ajax的邏輯可以對(duì)客戶端的安全掃描技術(shù)隱藏起來,允許黑客從遠(yuǎn)端服務(wù)器上建立新的攻擊。還有ajax也難以避免一些已知的安全弱點(diǎn),諸如跨站點(diǎn)腳步攻擊、SQL注入攻擊和基于credentials的安全漏洞等。
對(duì)搜索引擎的支持比較弱。如果使用不當(dāng),AJAX會(huì)增大網(wǎng)絡(luò)數(shù)據(jù)的流量,從而降低整個(gè)系統(tǒng)的性能。
AJAX狀態(tài)碼說明
1**:請(qǐng)求收到,繼續(xù)處理
2**:操作成功收到,分析、接受
3**:完成此請(qǐng)求必須進(jìn)一步處理
4**:請(qǐng)求包含一個(gè)錯(cuò)誤語法或不能完成
5**:服務(wù)器執(zhí)行一個(gè)完全有效請(qǐng)求失敗
AJAX的流程
- 聲明Ajax對(duì)象xhr,創(chuàng)建 XMLHttpRequest 實(shí)例
IE8以后才有如下聲明的方式
var xhr = new XMLHttpRequest();
XMLHttpRequest()是AJAX的原生對(duì)象
- 一旦新建實(shí)例,就可以使用
open()方法發(fā)出 HTTP 請(qǐng)求。
填寫請(qǐng)求信息,發(fā)出 HTTP 請(qǐng)求
第一個(gè)參數(shù):請(qǐng)求方式:get/post
第二個(gè)參數(shù):url(統(tǒng)一資源定位符)
第三個(gè)參數(shù):true/false true異步,false同步
xhr.open('GET', 'http://www.example.com/page.php', true);
- 向服務(wù)器發(fā)送請(qǐng)求
xhr.send(),正在發(fā)送請(qǐng)求
- 等待數(shù)據(jù)響應(yīng),接收服務(wù)器傳回的數(shù)據(jù)
xhr.onreadystatechange = function(){}
在回調(diào)函數(shù)中進(jìn)行請(qǐng)求狀態(tài)readyState的監(jiān)控
當(dāng) readyState 等于 4 且狀態(tài)為 200 時(shí),表示響應(yīng)內(nèi)容解析完成,可以在客戶端調(diào)用了。
返回的內(nèi)容:
responseText:返回以文本形式存放的內(nèi)容
responseXML:返回XML形式的內(nèi)容
- 更新網(wǎng)頁數(shù)據(jù)
AJAX中的GET和POST請(qǐng)求
get一般用來進(jìn)行查詢操作,url地址有長度限制,請(qǐng)求的參數(shù)都暴露在url地址當(dāng)中,如果傳遞中文參數(shù),需要自己進(jìn)行編碼操作,安全性較低。
post請(qǐng)求方式主要用來提交數(shù)據(jù),沒有數(shù)據(jù)長度的限制,提交的數(shù)據(jù)內(nèi)容存在于http請(qǐng)求體中,數(shù)據(jù)不會(huì)暴漏在url地址中。
GET 還是 POST?
與 POST 相比,GET 更簡單也更快,并且在大部分情況下都能用。
然而,在以下情況中,請(qǐng)使用 POST 請(qǐng)求:
- 無法使用緩存文件(更新服務(wù)器上的文件或數(shù)據(jù)庫)
- 向服務(wù)器發(fā)送大量數(shù)據(jù)(POST 沒有數(shù)據(jù)量限制)
- 發(fā)送包含未知字符的用戶輸入時(shí),POST 比 GET 更穩(wěn)定也更可靠
AJAX如何實(shí)現(xiàn)跨域
理解跨域的概念:協(xié)議、域名、端口都相同才同域,否則都是跨域。
出于安全考慮,服務(wù)器不允許ajax跨域獲取數(shù)據(jù),但是可以跨域獲取文件內(nèi)容,所以基于這一點(diǎn),可以動(dòng)態(tài)創(chuàng)建script標(biāo)簽,使用標(biāo)簽的src屬性訪問js文件的形式獲取js腳本,并且這個(gè)js腳本中的內(nèi)容是函數(shù)調(diào)用,該函數(shù)調(diào)用的參數(shù)是服務(wù)器返回的數(shù)據(jù),為了獲取這里的參數(shù)數(shù)據(jù),需要事先在頁面中定義回調(diào)函數(shù),在回調(diào)函數(shù)中處理服務(wù)器返回的數(shù)據(jù),這就是解決跨域問題的主流解決方案。
一個(gè)頁面從輸入U(xiǎn)RL到頁面加載顯示完成,這個(gè)過程中都發(fā)生了什么?
分為4個(gè)步驟:
當(dāng)發(fā)送一個(gè) URL 請(qǐng)求時(shí),不管這個(gè) URL 是 Web 頁面的 URL 還是 Web 頁面上每個(gè)資源的 URL,瀏覽器都會(huì)開啟一個(gè)線程來處理這個(gè)請(qǐng)求,同時(shí)在遠(yuǎn)程 DNS 服務(wù)器上啟動(dòng)一個(gè) DNS 查詢。這能使瀏覽器獲得請(qǐng)求對(duì)應(yīng)的 IP 地址。
瀏覽器與遠(yuǎn)程 Web 服務(wù)器通過 TCP 三次握手協(xié)商來建立一個(gè) TCP/IP 連接。該握手包括一個(gè)同步報(bào)文,一個(gè)同步-應(yīng)答報(bào)文和一個(gè)應(yīng)答報(bào)文,這三個(gè)報(bào)文在 瀏覽器和服務(wù)器之間傳遞。該握手首先由客戶端嘗試建立起通信,而后服務(wù)器應(yīng)答并接受客戶端的請(qǐng)求,最后由客戶端發(fā)出該請(qǐng)求已經(jīng)被接受的報(bào)文。
一旦 TCP/IP 連接建立,瀏覽器會(huì)通過該連接向遠(yuǎn)程服務(wù)器發(fā)送 HTTP 的 GET 請(qǐng)求。遠(yuǎn)程服務(wù)器找到資源并使用 HTTP 響應(yīng)返回該資源,值為 200 的 HTTP 響應(yīng)狀態(tài)表示一個(gè)正確的響應(yīng)。
此時(shí),Web 服務(wù)器提供資源服務(wù),客戶端開始下載資源。
從用戶輸入U(xiǎn)RL,到瀏覽器呈現(xiàn)給用戶頁面,經(jīng)過了什么過程?
用戶輸入U(xiǎn)RL,瀏覽器獲取到URL
瀏覽器(應(yīng)用層)進(jìn)行DNS解析(如果輸入的是IP地址,此步驟省略)
根據(jù)解析出的IP地址+端口,瀏覽器(應(yīng)用層)發(fā)起HTTP請(qǐng)求,請(qǐng)求中攜帶(請(qǐng)求頭header(也可細(xì)分為請(qǐng)求行和請(qǐng)求頭)、請(qǐng)求體body),
header包含:
請(qǐng)求的方法(get、post、put..)
協(xié)議(http、https、ftp、sftp...)
目標(biāo)url(具體的請(qǐng)求路徑已經(jīng)文件名)
一些必要信息(緩存、cookie之類)
body包含:
請(qǐng)求的內(nèi)容
請(qǐng)求到達(dá)傳輸層,tcp協(xié)議為傳輸報(bào)文提供可靠的字節(jié)流傳輸服務(wù),它通過三次握手等手段來保證傳輸過程中的安全可靠。通過對(duì)大塊數(shù)據(jù)的分割成一個(gè)個(gè)報(bào)文段的方式提供給大量數(shù)據(jù)的便攜傳輸。
到網(wǎng)絡(luò)層, 網(wǎng)絡(luò)層通過ARP尋址得到接收方的Mac地址,IP協(xié)議把在傳輸層被分割成一個(gè)個(gè)數(shù)據(jù)包傳送接收方。
數(shù)據(jù)到達(dá)數(shù)據(jù)鏈路層,請(qǐng)求階段完成
接收方在數(shù)據(jù)鏈路層收到數(shù)據(jù)包之后,層層傳遞到應(yīng)用層,接收方應(yīng)用程序就獲得到請(qǐng)求報(bào)文。
接收方收到發(fā)送方的HTTP請(qǐng)求之后,進(jìn)行請(qǐng)求文件資源(如HTML頁面)的尋找并響應(yīng)報(bào)文。
發(fā)送方收到響應(yīng)報(bào)文后,如果報(bào)文中的狀態(tài)碼表示請(qǐng)求成功,則接受返回的資源(如HTML文件),進(jìn)行頁面渲染。
fetch
什么是fetch?
fetch號(hào)稱是AJAX的替代品,是在ES6出現(xiàn)的,使用了ES6中的promise對(duì)象。Fetch是基于promise設(shè)計(jì)的。Fetch的代碼結(jié)構(gòu)比起ajax簡單多了,參數(shù)有點(diǎn)像jQuery ajax。但是,一定記住fetch不是ajax的進(jìn)一步封裝,而是原生js,沒有使用XMLHttpRequest對(duì)象。
try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e);
}
fetch的優(yōu)點(diǎn)
- 符合關(guān)注分離,沒有將輸入、輸出和用事件來跟蹤的狀態(tài)混雜在一個(gè)對(duì)象里。
- 更好更方便的寫法
fetch的優(yōu)勢(shì)
- 語法簡潔,更加語義化
- 基于標(biāo)準(zhǔn) Promise 實(shí)現(xiàn),支持 async/await
- 同構(gòu)方便,使用 isomorphic-fetch
- 更加底層,提供的API豐富(request, response)
- 脫離了XHR,是ES規(guī)范里新的實(shí)現(xiàn)方式
fetch的劣勢(shì)
- fetch只對(duì)網(wǎng)絡(luò)請(qǐng)求報(bào)錯(cuò),對(duì)400,500都當(dāng)做成功的請(qǐng)求,服務(wù)器返回 400,500 錯(cuò)誤碼時(shí)并不會(huì) reject,只有網(wǎng)絡(luò)錯(cuò)誤這些導(dǎo)致請(qǐng)求不能完成時(shí),fetch 才會(huì)被 reject。
- fetch默認(rèn)不會(huì)帶cookie,需要添加配置項(xiàng): fetch(url, {credentials: 'include'})
- fetch不支持abort,不支持超時(shí)控制,使用setTimeout及Promise.reject的實(shí)現(xiàn)的超時(shí)控制并不能阻止請(qǐng)求過程繼續(xù)在后臺(tái)運(yùn)行,造成了流量的浪費(fèi)
- fetch沒有辦法原生監(jiān)測(cè)請(qǐng)求的進(jìn)度,而XHR可以
axios
axios是什么
Axios 是一個(gè)基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中,主要是用于向后臺(tái)發(fā)起請(qǐng)求的。
axios的用途
(1)向后臺(tái)發(fā)送ajax請(qǐng)求數(shù)據(jù)。
(2) axios可以支持高并發(fā)請(qǐng)求,可以同時(shí)請(qǐng)求多個(gè)接口。
(3) axios可以防止CSRF/XSRF(跨站請(qǐng)求偽造)釣魚網(wǎng)站。
(4)axios提供了攔截器、catch捕獲。
封裝axios
Get
import axios from 'axios'
export default ({url,data})=>{
return axios.get(url,{
params:data
})
}
Post
import axios from 'axios'
import qs from 'querystring'
export default ({url,data})=>{
return axios.post(url,qs.stringify(data))
}
利用axios實(shí)現(xiàn)數(shù)據(jù)請(qǐng)求
引入axios之后就可以在實(shí)例/組件上掛載一個(gè)$http屬性,用這個(gè)就可以進(jìn)行數(shù)據(jù)交互了。
提供了一個(gè)get方法,執(zhí)行this.$http.get().then()可以異步執(zhí)行,拿到promise對(duì)象的值
axios如何防止CSRF(跨站攻擊)
在a頁面登錄注冊(cè)成功后時(shí)候跳轉(zhuǎn)到b頁面,b頁面可以獲取你的cookie信息,把你的用戶信息攔截到,然后惡意向別的網(wǎng)站散發(fā)一些郵件或者其他的東西,當(dāng)你使用axios之后,就可以防止這種攻擊,a頁面存儲(chǔ)的時(shí)候往cookie添加一個(gè)唯一個(gè)key,b頁面如果是別的域名是獲取不到這個(gè)key的,釣魚網(wǎng)站就是把b頁面這個(gè)域名篡改到別的位置去了,所以它無法獲取到key值,也就拿不到用戶信息,這樣可以防止釣魚網(wǎng)站的威脅。
如何在項(xiàng)目中通過路由守衛(wèi)來實(shí)現(xiàn)登錄攔截?講一個(gè)在項(xiàng)目中使用路由守衛(wèi)的案例?
攔截器的工作流程:

第一步:路由攔截
首先在定義路由的時(shí)候就需要多添加一個(gè)自定義字段requireAuth,用于判斷該路由的訪問是否需要登錄。如果用戶已經(jīng)登錄,則順利進(jìn)入路由, 否則就進(jìn)入登錄頁面。
const routes = [
{
path: '/',
name: '/',
component: Index
},
{
path: '/repository',
name: 'repository',
meta: {
requireAuth: true, // 添加該字段,表示進(jìn)入這個(gè)路由是需要登錄的
},
component: Repository
},
{
path: '/login',
name: 'login',
component: Login
}
];
定義完路由后,我們主要是利用vue-router提供的鉤子函數(shù)beforeEach()對(duì)路由進(jìn)行判斷。
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) { // 判斷該路由是否需要登錄權(quán)限
if (store.state.token) { // 通過vuex state獲取當(dāng)前的token是否存在
next();
}
else {
next({
path: '/login',
query: {redirect: to.fullPath} // 將跳轉(zhuǎn)的路由path作為參數(shù),登錄成功后跳轉(zhuǎn)到該路由
})
}
}
else {
next();
}
})
其中,to.meta中是我們自定義的數(shù)據(jù),其中就包括我們剛剛定義的requireAuth字段。通過這個(gè)字段來判斷該路由是否需要登錄權(quán)限。需要的話,同時(shí)當(dāng)前應(yīng)用不存在token,則跳轉(zhuǎn)到登錄頁面,進(jìn)行登錄。登錄成功后跳轉(zhuǎn)到目標(biāo)路由。
登錄攔截到這里就結(jié)束了嗎?并沒有。這種方式只是簡單的前端路由控制,并不能真正阻止用戶訪問需要登錄權(quán)限的路由。還有一種情況便是:當(dāng)前token失效了,但是token依然保存在本地。這時(shí)候你去訪問需要登錄權(quán)限的路由時(shí),實(shí)際上應(yīng)該讓用戶重新登錄。
這時(shí)候就需要結(jié)合 http 攔截器 + 后端接口返回的http 狀態(tài)碼來判斷。(具體方法見下一題目)
axios+vue如何在前端實(shí)現(xiàn)登錄攔截?(路由攔截、http攔截)
登錄流程控制中,根據(jù)本地是否存在token判斷用戶的登錄情況,但是即使token存在,也有可能token是過期的,所以在每次的請(qǐng)求頭中攜帶token,后臺(tái)根據(jù)攜帶的token判斷用戶的登錄情況,并返回給我們對(duì)應(yīng)的狀態(tài)碼,而后我們可以在響應(yīng)攔截器中,根據(jù)狀態(tài)碼進(jìn)行一些統(tǒng)一的操作。
// 先導(dǎo)入vuex,因?yàn)槲覀円褂玫嚼锩娴臓顟B(tài)對(duì)象
// vuex的路徑根據(jù)自己的路徑去寫
import store from '@/store/index';
// 請(qǐng)求攔截器axios.interceptors.request.use( //axios的一個(gè)方法,用于請(qǐng)求之前攔截
config => {
// 每次發(fā)送請(qǐng)求之前判斷vuex中是否存在token
// 如果存在,則統(tǒng)一在http請(qǐng)求的header都加上token,這樣后臺(tái)根據(jù)token判斷你的登錄情況
// 即使本地存在token,也有可能token是過期的,所以在響應(yīng)攔截器中要對(duì)返回狀態(tài)進(jìn)行判斷
const token = store.state.token;
token && (config.headers.Authorization = token); (Authorization是后端自定義的名字) // 判斷vuex中是否存在token,如果存在的話,則在每個(gè)http請(qǐng)求時(shí)header都加上token
return config; //再把這個(gè)請(qǐng)求發(fā)送給我們的后臺(tái)
},
error => {
return Promise.error(error); //如果有錯(cuò)誤直接返回報(bào)錯(cuò)的相關(guān)內(nèi)容
})
如果后臺(tái)傳遞過來的狀態(tài)碼是失效狀態(tài),就需要在response響應(yīng)攔截器中清除本地token和清空vuex中token對(duì)象,通過commit觸發(fā)mutations方法來同步更改vuex中的狀態(tài),清空token之后跳轉(zhuǎn)到登錄頁面,讓用戶重新登錄。
// 清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
// 跳轉(zhuǎn)登錄頁面,并將要瀏覽的頁面fullPath傳過去,登錄成功后跳轉(zhuǎn)需要訪問的頁面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
完整方法見:vue中Axios的封裝和API接口的管理
https://juejin.im/post/5b55c118f265da0f6f1aa354
vue+axios 前端實(shí)現(xiàn)登錄攔截(路由攔截、http攔截)
https://www.cnblogs.com/guoxianglei/p/7084506.html
一個(gè)axios的簡單教程
http://m.itdecent.cn/p/13cf01cdb81f
axios與fetch、ajax區(qū)別?
(1)fetch和axios差不多,都是基于promise的,fetch沒有封裝xhr(ajax的原生對(duì)象),是新的語法,默認(rèn)不傳cookie,也監(jiān)聽不到請(qǐng)求的進(jìn)度,axios可以監(jiān)聽到請(qǐng)求的進(jìn)度。
(2)axios返回一個(gè)promise對(duì)象,拿到對(duì)象之后再去.then可以拿到resolve之后的內(nèi)容。
axios.get(url,{params:{}}).then(res=>{}).catch(err=>{});
axios.post(url,{}).then(res=>).catch(err=>{});
(3)ajax主要是利用callback回調(diào)函數(shù)的形式。
JQuery中的ajax:$.ajax({url,data,success(){}}) 在回調(diào)函數(shù)獲取數(shù)據(jù)
axios 和 ajax 的使用方法基本一樣,只有個(gè)別參數(shù)不同;
axios({
url: 'http://jsonplaceholder.typicode.com/users',
method: 'get',
responseType: 'json', // 默認(rèn)的
data: {
//'a': 1,
//'b': 2,
}
}).then(function (response) {
console.log(response);
console.log(response.data);
}).catch(function (error) {
console.log(error);
})
$.ajax({
url: 'http://jsonplaceholder.typicode.com/users',
type: 'get',
dataType: 'json',
data: {
//'a': 1,
//'b': 2,
},
success: function (response) {
console.log(response);
}
})
參考:ajax和axios、fetch的區(qū)別
http://m.itdecent.cn/p/8bc48f8fde75
JSON
什么是 JSON ?
- JSON 指的是 JavaScript 對(duì)象表示法(JavaScript Object Notation)
- JSON 是輕量級(jí)的文本數(shù)據(jù)交換格式
- JSON 獨(dú)立于語言:JSON 使用 Javascript語法來描述數(shù)據(jù)對(duì)象,但是 JSON 仍然獨(dú)立于語言和平臺(tái)。JSON 解析器和 JSON 庫支持許多不同的編程語言。 目前非常多的動(dòng)態(tài)(PHP,JSP,.NET)編程語言都支持JSON。
- JSON 具有自我描述性,更易理解
與 XML 相同之處
- JSON 是純文本
- JSON 具有"自我描述性"(人類可讀)
- JSON 具有層級(jí)結(jié)構(gòu)(值中存在值)
- JSON 可通過 JavaScript 進(jìn)行解析
- JSON 數(shù)據(jù)可使用 AJAX 進(jìn)行傳輸
與 XML 不同之處
- 沒有結(jié)束標(biāo)簽
- 更短
- 讀寫的速度更快
- 能夠使用內(nèi)建的 JavaScript eval() 方法進(jìn)行解析
- 使用數(shù)組
- 不使用保留字
為什么使用 JSON?
對(duì)于 AJAX 應(yīng)用程序來說,JSON 比 XML 更快更易使用:
使用 XML
- 讀取 XML 文檔
- 使用 XML DOM 來循環(huán)遍歷文檔
- 讀取值并存儲(chǔ)在變量中
使用 JSON
- 讀取 JSON 字符串
- 用 eval() 處理 JSON 字符串
js中想要把json字符串轉(zhuǎn)化為js對(duì)象的方式
JSON.parse()
eval()
假設(shè)我們有一個(gè)json字符串
var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
'{"name":"李華","age":18,"sex":"男"}]' +
'}';
使用 JSON.parse()方法來轉(zhuǎn)化
var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
'{"name":"李華","age":18,"sex":"男"}]' +
'}';
JSON.parse(str);
使用 eval()方法傳化:
var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
'{"name":"李華","age":18,"sex":"男"}]' +
'}';
eval('('+str+')');
注意點(diǎn): 通過 eval 來轉(zhuǎn)化,如果返回的字符串內(nèi)容是一個(gè)數(shù)組,可以直接轉(zhuǎn)化,如果返回的字符串內(nèi)容是一個(gè)對(duì)象,必須在字符串的前后加上()
當(dāng)字符串內(nèi)容是一個(gè)數(shù)組時(shí) ,eval()的轉(zhuǎn)化方式:
var arr = '[{"name":"唐老鴨","sex":"男"},{"name":"紅太狼","sex":"女"}]';
eval(arr);
JSON.parse() 和 eval() 的區(qū)別
eval方法不會(huì)去檢查給的字符串是否符合json的格式,而JSON.parse解析不滿足json格式的字符串時(shí),會(huì)報(bào)錯(cuò)。
如果給的字符串中存在js代碼eval也會(huì)一并執(zhí)行,比如下面的代碼段:
var str1 = '{"log":alert("我被會(huì)執(zhí)行的")}';
eval("("+str1+")");
執(zhí)行該代碼片段,會(huì)將 alert 語句作為js代碼來執(zhí)行,如果我們?cè)陂_發(fā)中建議使用JSON.parse來轉(zhuǎn)化,這樣可以避免很多隱患,比如,我們?cè)L問第三方提供的接口,返回的串中包含 window.location.href這樣的內(nèi)容,那么執(zhí)行了就會(huì)跳轉(zhuǎn)到不明網(wǎng)站,好危險(xiǎn),所以最好還是使用JSON.parse()來解析。
JSON.parse()
JSON 通常用于與服務(wù)端交換數(shù)據(jù)。
在接收服務(wù)器數(shù)據(jù)時(shí)一般是字符串。
我們可以使用 JSON.parse() 方法將數(shù)據(jù)轉(zhuǎn)換為 JavaScript 對(duì)象。
JSON.parse(text[, reviver])
JSON.stringify()
JSON 通常用于與服務(wù)端交換數(shù)據(jù)。
在向服務(wù)器發(fā)送數(shù)據(jù)時(shí)一般是字符串。
我們可以使用 JSON.stringify() 方法將 JavaScript 對(duì)象轉(zhuǎn)換為字符串。
JSON.stringify(value[, replacer[, space]])
Promise
同步異步函數(shù)的區(qū)別
同步:阻塞,當(dāng)前程序必須等前面一個(gè)程序執(zhí)行完畢以后,才能夠執(zhí)行。
異步:非阻塞,前一個(gè)程序是否執(zhí)行完畢,不影響后面程序的執(zhí)行。
promise、async、await
async函數(shù)返回的是一個(gè) Promise 對(duì)象,可以使用 then 方法添加回調(diào)函數(shù),async 函數(shù)內(nèi)部 return 語句返回的值,會(huì)成為 then 方法回調(diào)函數(shù)的參數(shù)。當(dāng)函數(shù)執(zhí)行的時(shí)候,一旦遇到await就會(huì)先返回,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語句。
await只能用在異步函數(shù)中,使用await必須要先使用async,命令后面返回的是Promise對(duì)象,運(yùn)行結(jié)果可能是rejected,所以最好把a(bǔ)wait命令放在try...catch代碼塊中。
Promise的概念
我理解的Promise就是一套為處理異步情況的方法。先創(chuàng)建一個(gè)promise對(duì)象來注冊(cè)一個(gè)委托,其中包括委托成功及失敗后的處理函數(shù)。然后基于這種表述方式,來將promise應(yīng)用到各種異步處理的情況中。
promise寫法案例
var promise = getAsyncPromise('fileA.txt');
promise.then( function(result){
// 成功時(shí)的處理辦法
}).catch( function(error){
// 失敗時(shí)的處理辦法
})
// 返回一個(gè)promise對(duì)象
promise三個(gè)狀態(tài)
pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失?。?/p>
Promise 實(shí)際就是一個(gè)對(duì)象, 從它可以獲得異步操作的消息,Promise 對(duì)象有三種狀態(tài),pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失?。?。Promise 的狀態(tài)一旦改變之后,就不會(huì)在發(fā)生任何變化,將回調(diào)函數(shù)變成了鏈?zhǔn)秸{(diào)用。
Promise的設(shè)計(jì)思想是,所有異步任務(wù)都返回一個(gè)Promise實(shí)例。Promise實(shí)例有一個(gè)then方法,用來指定下一步的回調(diào)函數(shù)。
總的來說,傳統(tǒng)的回調(diào)函數(shù)寫法使得代碼混成一團(tuán),變得橫向發(fā)展而不是向下發(fā)展。Promise就是解決這個(gè)問題,使得異步流程可以寫成同步流程。
Promise構(gòu)造函數(shù)
JavaScript 提供原生的Promise構(gòu)造函數(shù),用來生成 Promise 實(shí)例。
var promise = new Promise(function (resolve, reject) {
// ...
if (/* 異步操作成功 */){
resolve(value);
} else { /* 異步操作失敗 */
reject(new Error());
}
});
上面代碼中,Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是resolve和reject。它們是兩個(gè)函數(shù),由JavaScript引擎提供,不用自己實(shí)現(xiàn)。
resolve函數(shù)的作用是,將Promise實(shí)例的狀態(tài)從"未完成"變?yōu)?成功"(即從pending變?yōu)閒ulfilled),在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去。reject函數(shù)的作用是,將Promise實(shí)例的狀態(tài)從"未完成"變?yōu)?失敗"(即從pending變?yōu)閞ejected),在異步失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。
Promise的優(yōu)缺點(diǎn)
Promise 的優(yōu)點(diǎn)在于,讓回調(diào)函數(shù)變成了規(guī)范的鏈?zhǔn)綄懛?,程序流程可以看得很清楚。它有一整套接口,可以?shí)現(xiàn)許多強(qiáng)大的功能,比如同時(shí)執(zhí)行多個(gè)異步操作,等到它們的狀態(tài)都改變以后,再執(zhí)行一個(gè)回調(diào)函數(shù);再比如,為多個(gè)回調(diào)函數(shù)中拋出的錯(cuò)誤,統(tǒng)一指定處理方法等等。
而且,Promise 還有一個(gè)傳統(tǒng)寫法沒有的好處:它的狀態(tài)一旦改變,無論何時(shí)查詢,都能得到這個(gè)狀態(tài)。這意味著,無論何時(shí)為 Promise 實(shí)例添加回調(diào)函數(shù),該函數(shù)都能正確執(zhí)行。所以,你不用擔(dān)心是否錯(cuò)過了某個(gè)事件或信號(hào)。如果是傳統(tǒng)寫法,通過監(jiān)聽事件來執(zhí)行回調(diào)函數(shù),一旦錯(cuò)過了事件,再添加回調(diào)函數(shù)是不會(huì)執(zhí)行的。
Promise 的缺點(diǎn)是,編寫的難度比傳統(tǒng)寫法高,而且閱讀代碼也不是一眼可以看懂。你只會(huì)看到一堆then,必須自己在then的回調(diào)函數(shù)里面理清邏輯。
Promise的優(yōu)點(diǎn)
promise優(yōu)點(diǎn)主要解決回調(diào)地獄問題,使得原本的多層級(jí)的嵌套代碼,變成了鏈?zhǔn)秸{(diào)用,讓代碼更清晰,減少嵌套數(shù)。
Promise的缺點(diǎn)
首先,無法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無法中途取消。其次,如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反應(yīng)到外部。當(dāng)處于Pending狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段(剛剛開始還是即將完成)。
Promise的all方法
all方法是promise是類上自帶的方法,并發(fā)讀取,失敗一個(gè)都失敗了,時(shí)間只是一個(gè)讀取的時(shí)間
第一個(gè)參數(shù) 傳遞的是數(shù)組,數(shù)組裝的是一個(gè)個(gè)promise對(duì)象
調(diào)用后會(huì)再次返回一個(gè)promise實(shí)例
最好的寫法
Promise.all([read('./name.txt'),read('./age.txt')]).then(([name,age])=>{
//data就是promise執(zhí)行成功的結(jié)果類型是數(shù)組
console.log({name,age});
}).catch((err)=>{
console.log(err)
})
Promise的race方法
Promise.race ([promise1,promise2..])
當(dāng)參數(shù)里的任意一個(gè)promise成功或失敗后,該函數(shù)就會(huì)返回,并使用這個(gè)promise對(duì)象的值進(jìn)行resolve或reject
let promise1 = ajax({method:'GET',url:'/x.json'});
let promise2 = ajax({method:'GET',url:'/y.json'});
Promise.all([promise1,promise2]).then(function(){
console.log('兩個(gè)promise都執(zhí)行完成了')
});
Promise.race([promise1,promise2]).then(function(){
console.log('有一個(gè)promise先執(zhí)行完成了')
})
參考:簡單理解Promise
http://m.itdecent.cn/p/c8f9fba03df9
存儲(chǔ)
本地存儲(chǔ)(Local Storage )和cookies(儲(chǔ)存在用戶本地終端上的數(shù)據(jù))之間的區(qū)別是什么?
Cookies:服務(wù)器和客戶端都可以訪問;大小只有4KB左右;有有效期,過期后將會(huì)刪除;
本地存儲(chǔ):只有本地瀏覽器端可訪問數(shù)據(jù),服務(wù)器不能訪問本地存儲(chǔ),直到故意通過POST或者GET的通道發(fā)送到服務(wù)器;每個(gè)域5MB;沒有過期數(shù)據(jù),它將保留直到用戶從瀏覽器清除或者使用Javascript代碼移除。
cookie 4kb 隨http請(qǐng)求發(fā)送到服務(wù)端 后端可以幫助前端設(shè)置cookie
session 放在服務(wù)端 一般存放用戶比較重要的信息
(token 令牌 token ==> cookie /localstorage) vuex
localStorage 本地存儲(chǔ)(h5的新特性 draggable canvas svg)
5M 純粹在本地客戶端 多個(gè)標(biāo)簽頁共享數(shù)據(jù)
sessionStorage 會(huì)話級(jí)別的存儲(chǔ)
往本地頁面中存值的方法(localStorage.setItem(key,value))
講一下cookie、sessionstorage、localstorage
相同點(diǎn):都存儲(chǔ)在客戶端
不同點(diǎn):
1.存儲(chǔ)大小
- cookie數(shù)據(jù)大小不能超過4k。
- sessionStorage和localStorage 雖然也有存儲(chǔ)大小的限制,但比cookie大得多,可以達(dá)到5M或更大。
2.有效時(shí)間
- localStorage 存儲(chǔ)持久數(shù)據(jù),瀏覽器關(guān)閉后數(shù)據(jù)不丟失除非主動(dòng)刪除數(shù)據(jù);
- sessionStorage 數(shù)據(jù)在當(dāng)前瀏覽器窗口關(guān)閉后自動(dòng)刪除。
- cookie 設(shè)置的cookie過期時(shí)間之前一直有效,即使窗口或?yàn)g覽器關(guān)閉
3.數(shù)據(jù)與服務(wù)器之間的交互方式
- cookie的數(shù)據(jù)會(huì)自動(dòng)的傳遞到服務(wù)器,服務(wù)器端也可以寫cookie到客戶端
- sessionStorage和localStorage不會(huì)自動(dòng)把數(shù)據(jù)發(fā)給服務(wù)器,僅在本地保存。
定義一個(gè)對(duì)象,里面包含用戶名、電話,然后將其存入localStorage的代碼
var json = {username:"張三",phone:17650246248}
for(var key in json){
localStorage.setItem(key,json[key]);
}
cookie和session的區(qū)別
保持狀態(tài):cookie保存在瀏覽器端,session保存在服務(wù)器端
使用方式:如果不在瀏覽器中設(shè)置過期時(shí)間,cookie被保存在內(nèi)存中,生命周期隨瀏覽器的關(guān)閉而結(jié)束,這種cookie簡稱會(huì)話cookie。如果在瀏覽器中設(shè)置了cookie的過期時(shí)間,cookie被保存在硬盤中,關(guān)閉瀏覽器后,cookie數(shù)據(jù)仍然存在,直到過期時(shí)間結(jié)束才消失。
存儲(chǔ)內(nèi)容:cookie只能保存字符串類型,以文本的方式;session通過類似與Hashtable的數(shù)據(jù)結(jié)構(gòu)來保存,能支持任何類型的對(duì)象(session中可含有多個(gè)對(duì)象)
存儲(chǔ)的大小:cookie:單個(gè)cookie保存的數(shù)據(jù)不能超過4kb;session大小沒有限制。
安全性:cookie:針對(duì)cookie所存在的攻擊:Cookie欺騙,Cookie截獲;session的安全性大于cookie。
應(yīng)用場(chǎng)景:
cookie:(1)判斷用戶是否登陸過網(wǎng)站,以便下次登錄時(shí)能夠?qū)崿F(xiàn)自動(dòng)登錄(或者記住密碼)。如果我們刪除cookie,則每次登錄必須從新填寫登錄的相關(guān)信息。
(2)保存上次登錄的時(shí)間等信息。
(3)保存上次查看的頁面
(4)瀏覽計(jì)數(shù)
session:Session用于保存每個(gè)用戶的專用信息,變量的值保存在服務(wù)器端,通過SessionID來區(qū)分不同的客戶。
(1)網(wǎng)上商城中的購物車
(2)保存用戶登錄信息
(3)將某些數(shù)據(jù)放入session中,供同一用戶的不同頁面使用
(4)防止用戶非法登錄
-
缺點(diǎn):
cookie:
(1)大小受限
(2)用戶可以操作(禁用)cookie,使功能受限
(3)安全性較低
(4)有些狀態(tài)不可能保存在客戶端。
(5)每次訪問都要傳送cookie給服務(wù)器,浪費(fèi)帶寬。
(6)cookie數(shù)據(jù)有路徑(path)的概念,可以限制cookie只屬于某個(gè)路徑。
session:
(1)Session保存的東西越多,就越占用服務(wù)器內(nèi)存,對(duì)于用戶在線人數(shù)較多的網(wǎng)站,服務(wù)器的內(nèi)存壓力會(huì)比較大。
(2)依賴于cookie(sessionID保存在cookie),如果禁用cookie,則要使用URL重寫,不安全
(3)創(chuàng)建Session變量有很大的隨意性,可隨時(shí)調(diào)用,不需要開發(fā)者做精確地處理,所以,過度使用session變量將會(huì)導(dǎo)致代碼不可讀而且不好維護(hù)。
WebStorage
WebStorage的目的是克服由cookie所帶來的一些限制,當(dāng)數(shù)據(jù)需要被嚴(yán)格控制在客戶端時(shí),不需要持續(xù)的將數(shù)據(jù)發(fā)回服務(wù)器。
WebStorage兩個(gè)主要目標(biāo):
(1)提供一種在cookie之外存儲(chǔ)會(huì)話數(shù)據(jù)的路徑。
(2)提供一種存儲(chǔ)大量可以跨會(huì)話存在的數(shù)據(jù)的機(jī)制。
Localstroage與SessionStorage存儲(chǔ)的區(qū)別
HTML5的WebStorage提供了兩種API:localStorage(本地存儲(chǔ))和sessionStorage(會(huì)話存儲(chǔ))。
- 生命周期:localStorage:localStorage的生命周期是永久的,關(guān)閉頁面或?yàn)g覽器之后localStorage中的數(shù)據(jù)也不會(huì)消失。
localStorage除非主動(dòng)刪除數(shù)據(jù),否則數(shù)據(jù)永遠(yuǎn)不會(huì)消失。
sessionStorage的生命周期是在僅在當(dāng)前會(huì)話下有效。sessionStorage引入了一個(gè)“瀏覽器窗口”的概念,sessionStorage是在同源的窗口中始終存在的數(shù)據(jù)。只要這個(gè)瀏覽器窗口沒有關(guān)閉,即使刷新頁面或者進(jìn)入同源另一個(gè)頁面,數(shù)據(jù)依然存在。但是sessionStorage在關(guān)閉了瀏覽器窗口后就會(huì)被銷毀。同時(shí)獨(dú)立的打開同一個(gè)窗口同一個(gè)頁面,sessionStorage也是不一樣的。
存儲(chǔ)大?。簂ocalStorage和sessionStorage的存儲(chǔ)數(shù)據(jù)大小一般都是:5MB
存儲(chǔ)位置:localStorage和sessionStorage都保存在客戶端,不與服務(wù)器進(jìn)行交互通信。
存儲(chǔ)內(nèi)容類型:localStorage和sessionStorage只能存儲(chǔ)字符串類型,對(duì)于復(fù)雜的對(duì)象可以使用ECMAScript提供的JSON對(duì)象的stringify和parse來處理
獲取方式:localStorage:window.localStorage;;sessionStorage:window.sessionStorage;。
應(yīng)用場(chǎng)景:localStoragese:常用于長期登錄(+判斷用戶是否已登錄),適合長期保存在本地的數(shù)據(jù)。sessionStorage:敏感賬號(hào)一次性登錄;
WebStorage的優(yōu)點(diǎn)
存儲(chǔ)空間更大:cookie為4KB,而WebStorage是5MB;
節(jié)省網(wǎng)絡(luò)流量:WebStorage不會(huì)傳送到服務(wù)器,存儲(chǔ)在本地的數(shù)據(jù)可以直接獲取,也不會(huì)像cookie一樣美詞請(qǐng)求都會(huì)傳送到服務(wù)器,所以減少了客戶端和服務(wù)器端的交互,節(jié)省了網(wǎng)絡(luò)流量;
對(duì)于那種只需要在用戶瀏覽一組頁面期間保存而關(guān)閉瀏覽器后就可以丟棄的數(shù)據(jù),sessionStorage會(huì)非常方便;
快速顯示:有的數(shù)據(jù)存儲(chǔ)在WebStorage上,再加上瀏覽器本身的緩存。獲取數(shù)據(jù)時(shí)可以從本地獲取會(huì)比從服務(wù)器端獲取快得多,所以速度更快;
安全性:WebStorage不會(huì)隨著HTTP header發(fā)送到服務(wù)器端,所以安全性相對(duì)于cookie來說比較高一些,不會(huì)擔(dān)心截獲,但是仍然存在偽造問題;
WebStorage提供了一些方法,數(shù)據(jù)操作比cookie方便;
WebStorage的方法
setItem (key, value) —— 保存數(shù)據(jù),以鍵值對(duì)的方式儲(chǔ)存信息。
getItem (key) —— 獲取數(shù)據(jù),將鍵值傳入,即可獲取到對(duì)應(yīng)的value值。
removeItem (key) —— 刪除單個(gè)數(shù)據(jù),根據(jù)鍵值移除對(duì)應(yīng)的信息。
clear () —— 刪除所有的數(shù)據(jù)
key (index) —— 獲取某個(gè)索引的key
Token
token是"令牌"的意思,服務(wù)端生成一串字符串,作為客戶端請(qǐng)求的一段標(biāo)識(shí)。用戶登錄的時(shí)候生成一個(gè)token,并將token返回客戶端,客戶端將收到的token放在cookie里,下次用戶向服務(wù)端發(fā)送請(qǐng)求的時(shí)候,服務(wù)端只需要對(duì)比token。
作用:進(jìn)行身份驗(yàn)證,避免表單重復(fù)提交。
在ajax請(qǐng)求后臺(tái)時(shí)token添加到哪里
在ajax請(qǐng)求的標(biāo)頭中加Token
1 function GetDateForServiceCustomer(userId) {
2 $.ajax({
3 url: 'http://*******/api/orders',
4 data: {
5 currUserId: userId,
6 type: 1
7 },
8 beforeSend: function(request) {
9 request.setRequestHeader("Authorization", token);
10 },
11 dataType: 'JSON',
12 async: false,//請(qǐng)求是否異步,默認(rèn)為異步
13 type: 'GET',
14 success: function (list) {
15 },
16 error: function () {
17 }
18 });
19 }

參考:在ajax請(qǐng)求后臺(tái)時(shí)在請(qǐng)求標(biāo)頭RequestHeader加token
https://www.cnblogs.com/zfdcp-028/p/6374632.html
協(xié)議
HTTPS與HTTP的一些區(qū)別
- HTTPS協(xié)議需要到CA申請(qǐng)證書,一般免費(fèi)證書很少,需要交費(fèi)。
- HTTP協(xié)議運(yùn)行在TCP之上,所有傳輸?shù)膬?nèi)容都是明文,HTTPS運(yùn)行在SSL/TLS之上,SSL/TLS運(yùn)行在TCP之上,所有傳輸?shù)膬?nèi)容都經(jīng)過加密的。
- HTTP和HTTPS使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
- HTTPS可以有效的防止運(yùn)營商劫持,解決了防劫持的一個(gè)大問題。
HTTP1.0和HTTP2.0有什么區(qū)別
新的二進(jìn)制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本協(xié)議的格式解析存在天然缺陷,文本的表現(xiàn)形式有多樣性,要做到健壯性考慮的場(chǎng)景必然很多,二進(jìn)制則不同,只認(rèn)0和1的組合?;谶@種考慮HTTP2.0的協(xié)議解析決定采用二進(jìn)制格式,實(shí)現(xiàn)方便且健壯。
多路復(fù)用(MultiPlexing),即連接共享,即每一個(gè)request都是是用作連接共享機(jī)制的。一個(gè)request對(duì)應(yīng)一個(gè)id,這樣一個(gè)連接上可以有多個(gè)request,每個(gè)連接的request可以隨機(jī)的混雜在一起,接收方可以根據(jù)request的 id將request再歸屬到各自不同的服務(wù)端請(qǐng)求里面。
header壓縮,如上文中所言,對(duì)前面提到過HTTP1.x的header帶有大量信息,而且每次都要重復(fù)發(fā)送,HTTP2.0使用encoder來減少需要傳輸?shù)膆eader大小,通訊雙方各自cache一份header fields表,既避免了重復(fù)header的傳輸,又減小了需要傳輸?shù)拇笮 ?/p>
服務(wù)端推送(server push),同SPDY一樣,HTTP2.0也具有server push功能。
參考:HTTP1.0、HTTP1.1 和 HTTP2.0 的區(qū)別
https://www.cnblogs.com/heluan/p/8620312.html
Websocket
WebSocket 是 HTML5 開始提供的一種在單個(gè) TCP 連接上進(jìn)行全雙工通訊的協(xié)議。
WebSocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。在 WebSocket API 中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。
在 WebSocket API 中,瀏覽器和服務(wù)器只需要做一個(gè)握手的動(dòng)作,然后,瀏覽器和服務(wù)器之間就形成了一條快速通道。兩者之間就直接可以數(shù)據(jù)互相傳送。
現(xiàn)在,很多網(wǎng)站為了實(shí)現(xiàn)推送技術(shù),所用的技術(shù)都是 Ajax 輪詢。輪詢是在特定的的時(shí)間間隔(如每1秒),由瀏覽器對(duì)服務(wù)器發(fā)出HTTP請(qǐng)求,然后由服務(wù)器返回最新的數(shù)據(jù)給客戶端的瀏覽器。這種傳統(tǒng)的模式帶來很明顯的缺點(diǎn),即瀏覽器需要不斷的向服務(wù)器發(fā)出請(qǐng)求,然而HTTP請(qǐng)求可能包含較長的頭部,其中真正有效的數(shù)據(jù)可能只是很小的一部分,顯然這樣會(huì)浪費(fèi)很多的帶寬等資源。
HTML5 定義的 WebSocket 協(xié)議,能更好的節(jié)省服務(wù)器資源和帶寬,并且能夠更實(shí)時(shí)地進(jìn)行通訊。
WebSocket特點(diǎn)
(1)建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易。
(2)與 HTTP 協(xié)議有著良好的兼容性。默認(rèn)端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過各種 HTTP 代理服務(wù)器。
(3)數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。
(4)可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。
(5)沒有同源限制,客戶端可以與任意服務(wù)器通信。
(6)協(xié)議標(biāo)識(shí)符是ws(如果加密,則為wss),服務(wù)器網(wǎng)址就是 URL。
scoket套接字編程
網(wǎng)絡(luò)上的兩個(gè)程序通過一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換,這個(gè)連接的一端稱為一個(gè)socket。
如果將http比作轎車的話,那么socket就相當(dāng)于發(fā)動(dòng)機(jī)。
websocket跟 socket 的區(qū)別
軟件通信有七層結(jié)構(gòu),下三層結(jié)構(gòu)偏向與數(shù)據(jù)通信,上三層更偏向于數(shù)據(jù)處理,中間的傳輸層則是連接上三層與下三層之間的橋梁,每一層都做不同的工作,上層協(xié)議依賴與下層協(xié)議。基于這個(gè)通信結(jié)構(gòu)的概念。
Socket 其實(shí)并不是一個(gè)協(xié)議,是應(yīng)用層與 TCP/IP 協(xié)議族通信的中間軟件抽象層,它是一組接口。當(dāng)兩臺(tái)主機(jī)通信時(shí),讓 Socket 去組織數(shù)據(jù),以符合指定的協(xié)議。TCP 連接則更依靠于底層的 IP 協(xié)議,IP 協(xié)議的連接則依賴于鏈路層等更低層次。
WebSocket 則是一個(gè)典型的應(yīng)用層協(xié)議。
總的來說:Socket 是傳輸控制層協(xié)議,WebSocket 是應(yīng)用層協(xié)議。
webScoket如何兼容低瀏覽器
- Adobe Flash Socket
- ActiveX HTMLFile (IE)
- 基于 multipart 編碼發(fā)送 XHR
- 基于長輪詢的 XHR
參考:WebSocket解釋及如何兼容低版本瀏覽器
https://www.cnblogs.com/pengc/p/8718380.html
模塊化開發(fā)
模塊、函數(shù)、組件分別是什么?
模塊:在webpack中,通過import引入的文件叫做模塊。(js/css/png)
函數(shù):是一些功能的合集。
組件:指的是頁面的某一部分。
類是什么?類被編譯成什么?
類:
class Banner extends React.Component{ //es6寫法
}
ES5:通過模塊定義類 ES6中定義的類也被編譯成這樣
function Banner(){ //構(gòu)造函數(shù)
}
組件化與模塊化的區(qū)別,或者說怎么實(shí)現(xiàn)組件化開發(fā),模塊化開發(fā)
組件化:針對(duì)的是頁面中的整個(gè)完整的功能模塊,劃分成瀏覽器可以識(shí)別的每個(gè)模塊,例如頭部Header、底部Footer、Banner。優(yōu)點(diǎn):代碼復(fù)用、便于維護(hù)。
模塊化:就是系統(tǒng)功能分離或獨(dú)立的功能部分的方法,一般指的是單一的某個(gè)東西,例如:js、css
模塊化開發(fā)的規(guī)范
(1)commonJS:自上而下同步進(jìn)行 會(huì)阻塞 用在服務(wù)端
使用模塊:const 變量名 = require("包名字")
聲明模塊:module.exports = {
對(duì)外的名字:函數(shù)
}
(2)AMD:異步加載文件 不會(huì)阻塞 用在瀏覽器端
define方法用于定義模塊,RequireJS要求每個(gè)模塊放在一個(gè)單獨(dú)的文件里。
使用模塊:require("模塊名",function(模塊對(duì)象)){
return 模塊對(duì)象.函數(shù)();//回調(diào)函數(shù)
}
(3)CMD規(guī)范 中國人發(fā)明的,ECMA6來了就廢棄了。
amd和cmd的區(qū)別
amd是require.js上的一個(gè)規(guī)范,cmd是sea.js的一個(gè)規(guī)范。
其實(shí)CMD與AMD規(guī)范并沒什么本質(zhì)的區(qū)別,區(qū)別在于他們對(duì)依賴模塊的執(zhí)行時(shí)機(jī)處理不同。雖然兩者都是異步加載模塊,但是AMD依賴前置,js可以方便知道依賴模塊是誰,要依賴什么js那就先加載進(jìn)來,至于你要依賴這些js來干嗎得先等著,等我加載完了資源再商量;而CMD就近依賴,需要使用這個(gè)依賴模塊時(shí),我再加載進(jìn)來用。
這就好比什么呢?就好像我今晚要看5集三國演義。AMD是先打開五個(gè)窗口,分別是1~5集,都緩沖著先,反正等下每集我都要看的;CMD則是先打開第一集的窗口,等到我第一集看完了,想看第二集了,就再跳轉(zhuǎn)到第二集。
現(xiàn)在使用頻率最高的,也是大家公認(rèn)的好的模塊化規(guī)范,是CommonJS。 后端(node.js)
require.js
實(shí)現(xiàn)了AMD規(guī)范的JavaScript工具庫
RequireJS的基本思想是,通過define方法,將代碼定義為模塊;通過require方法,實(shí)現(xiàn)代碼的模塊加載。
require.js的誕生,就是為了解決這兩個(gè)問題:
(1)實(shí)現(xiàn)了js文件的異步加載,避免網(wǎng)頁失去響應(yīng)
(2)管理模塊之間的依賴性,便于代碼的編寫和維護(hù)
common.js
CommonJS就很簡單了,一個(gè)js文件要輸出去,只需使用module.export={xxx:你要輸出的內(nèi)容},而在另外一個(gè)js中,你要引用什么,就通過var xxxx=require("xxxx")引用進(jìn)來就行了,這玩意并不是異步加載模塊,而是同步一次性加載出來。
common.js與require.js與sea.js的區(qū)別:


配置模塊化開發(fā)
<1>在頭部引入
<script scr = "js/require.js" defer async = "true" data-main = "js/main"></script>
main.js管理當(dāng)前頁面的js文件,每一個(gè)頁面都有自己的不同名字的main.js文件.
路徑以dist文件為主
在外部修改文件,不允許修改dist中的文件,gulp會(huì)自動(dòng)同步修改.
<2>在main中通過require.config配置當(dāng)前頁面需要的模塊路徑
配置模塊的依賴路徑:
shim:{
//由于jquery-cookie是基于jquery庫封裝的,所以要先引入jquery再引入jquery-cookie
"jquery-cookie":['jquery'],
//聲明一下,不是AMD規(guī)范的模塊
"parabola":{
exports:"_"
}
}
<3>編寫CSS樣式/JS代碼
編寫JS代碼:
先封裝成函數(shù)
再通過對(duì)象的形式對(duì)外暴露
define(["jquery"],function($){
function slide(){
$(function(){
function show(){
console.log("hello world");
}
return{
slide:slide,
show:show
}
})
<4>在main.js中加載
//配置當(dāng)前整個(gè)項(xiàng)目所有模塊的路徑
require.config({
paths:{
"slide":"slide"
},
require(["slide"],function(slide){
slide.show();
slide.slide();
})