1. 實現(xiàn)不同窗體間互相通信
(1) window.postMessage()支持跨文檔消息傳輸(Cross Document Messaging),并且可跨域傳輸信息。
兼容性:IE>=8 支持
消息監(jiān)聽:onmessage,可接受任意發(fā)送過來的消息,應對消息來源(e.origin)進行安全驗證
window.addEventListener('message', fn, false);
消息發(fā)送:postMessage,獲取到要傳送消息的窗體對象,向其發(fā)送消息。
targetWindow.postMessage(message, targetOrigin);
(2) 監(jiān)聽 localStorage 的 storage事件,當localStorage的鍵值改變時,就會觸發(fā)此事件。
window.addEventListener("storage", fn, false);
window.attachEvent("onstorage", fn);
2. CORS
CORS(跨域資源共享)需要瀏覽器和服務器同時支持,除IE<10以外,其他瀏覽器都已支持,而且服務器實現(xiàn)了CORS接口,就可以跨源通信了。通過在服務器端設置 HTTP header 字段,服務器可以聲明哪些網(wǎng)站有權限訪問哪些資源。
瀏覽器將CORS請求分兩類,簡單請求和非簡單請求。
(1) 簡單請求:(不會觸發(fā)CORS預檢請求)
條件:1. 請求方法是HEAD GET 或 POST; 2. HTTP header是對CORS安全的首部字段。
瀏覽器和服務器之間使用 CORS 首部字段來處理跨域權限。瀏覽器在請求頭信息中增加一個Origin字段,用來說明本次請求來自哪個源,服務器根據(jù)這個字段值來決定是否同意這次請求。
如果Origin指定的源不在許可范圍,服務器會返回一個正常的 HTTP 回應,這個回應的頭信息不包含Access-Control-Allow-Origin字段,瀏覽器會拋出一個錯誤,這個錯誤會被 XMLHttpRequest的 onerror 回調函數(shù)捕獲。這個錯誤不能通過狀態(tài)碼識別,因為服務器返回的是一個正常的回應,狀態(tài)碼可能為200。
如果Origin指定的源在許可范圍內,服務器返回的響應會包含幾個頭信息字段:Access-Control-Allow-Origin (Access-Control-Allow-Credentials、Access-Control-Expose-Headers、Content-Type 可選)
(2)非簡單請求:(會進行預檢請求)
條件:對服務器有特殊要求的請求,請求方法是PUT或DELETE,或者Content-Type字段類型是application/json。
非簡單請求會在正式通信前,增加一次HTTP預檢請求。
瀏覽器會先詢問服務器,當前域名是否在服務器許可名單中,以及可以使用哪些HTTP請求方法和頭信息字段,當?shù)玫椒掌骺隙ɑ貞?,會返?code>Access-Control-Allow-Origin字段,表示可以請求數(shù)據(jù)。如果服務器返回否定回應,會觸發(fā)一個錯誤,被XMLHttpRequest 的 onerror 回調函數(shù)捕獲。
3. flex-box
彈性盒子(flexible box)是CSS3的一種新的布局模式,尤其是在屏幕寬度自適應時,彈性布局使得父元素中子元素的排列、對齊、空間分配變得更便捷靈活。對于可伸縮的父元素,子元素隨其擴大或縮小,并能夠合適地填滿父元素空間。彈性布局包括多個CSS3屬性,有設置在父元素上的,有設置在子元素上的。
父元素上的屬性:
display:flex; 讓父元素變成Flex容器。
flex-direction:子元素在Flex容器中放置的方向 (row|row-reverse|column|column-reverse)
flex-wrap:默認子元素都顯示在一行(nowrap),改變此屬性(wrap/wrap-reverse),可以讓子元素多行顯示。
justify-content:在主軸上對齊子元素,在父元素有多余的空間時才會進行空間分配 (flex-start|flex-end|center|space-between|space-around)
align-items:在側軸上對齊子元素(flex-start|flex-end|center|baseline|stretch)
align-content:當多行的Flex容器的側軸還有多余空間時,可用來調節(jié)多行子元素在容器內的對齊方式(flex-start|flex-end|center|space-between|space-around|stretch)
子元素上的屬性:
order:默認子元素是按文檔中的順序顯示排列的,在Flex容器中可通過order屬性改變子元素的顯示順序
flex-grow:可以定義子元素的擴大比例。所有的子元素值相同時,占據(jù)相同的空間。
flex-shrink:可以定義子元素的縮小比例。
flex-basis:子元素的分配Flex容器剩余空間之前的默認尺寸。0表示不考慮子元素周圍額外空間,auto表示額外空間根據(jù)flex-grow值做分配。
flex:flex-grow、flex-shrink和flex-basis三個屬性的縮寫。默認值:0 1 auto。
align-self:單個子元素側軸的對齊方式。
4. 前端優(yōu)化
(1) 加載頁面和靜態(tài)資源方面:
靜態(tài)資源的合并壓縮;使用緩存;使用CDN;后端渲染,數(shù)據(jù)直接輸出到html模版
(2)頁面渲染和頁面操作方面:
減少DOM操作;懶加載;css放在html文檔的前面加載,js在后面加載;事件節(jié)流;盡早執(zhí)行操作(DOMContentLoaded: DOM渲染完即可操作,此時圖片視頻可能還沒加載完;window.onload:頁面全部資源加載完才會執(zhí)行,包括圖片視頻)
5. BFC
塊級格式化上下文(block formatting context),創(chuàng)建了新BFC的盒子是獨立布局的,是一個隔離的獨立容器盒子,里面子元素的樣式不會影響到外面的元素,盒子里的子元素的布局和定位和外部元素互不影響。
作用:
(1) 避免margin重疊。塊級標簽之間豎直方向的margin會重疊??梢杂?code>overflow:hidden;來解決。
(2) 清除浮動。子元素浮動,父元素的高度會塌陷,父元素高度不會被子元素撐開的問題。
觸發(fā)BFC的條件:
(1) 浮動元素:float的值不為none。
(2) position的值為absolute或fixed。
(3) overflow為除了visible以外的值(auto、scroll或hidden)。
(4) display的值為inline-block、table-cell、table-caption。
6. web安全-攻擊及防范
(1) XSS 跨站腳本攻擊(Cross Site Scripting)
攻擊者向web頁面插入惡意html標簽或者js代碼。比如一個看似安全的鏈接,用戶點擊后,攻擊者竊取用戶的cookie;或者攻擊者在網(wǎng)頁中加一個惡意表單,用戶提交表單的時候,把消息傳送到攻擊者的服務器,而不是原本的站點。
防范:對用戶輸入做字符過濾或者轉義;避免在cookie中泄露用戶隱私;最好使用POST提交表單。
(2) CSRF 跨站請求偽造(Cross-site request forgery)
要完成一次CSRF攻擊,關鍵點:登錄受信任的網(wǎng)站A,并在本地生成cookie;在不登出A的情況下,訪問危險網(wǎng)站B。
防范:通過驗證碼或者token檢測用戶;避免全站通用cookie,設置cookie的域;用戶操作最好都使用POST。
7. getElementsByClassName
獲取頁面元素的方法:
getElementById() 返回一個對象
getElementsByTagName() 返回集合
getElementsByClassName() 返回集合
querySelector() 返回一個對象
querySelectorAll() 返回集合
8. 前端路由實現(xiàn)的方式
(1)h5: window.history
兼容性:IE<=9 不支持
window.history.pushState(data, title, url);
window.history.replaceState(data, title, url);
url: 絕對路徑/相對路徑, 同域。
這兩個API可操作瀏覽器的歷史棧,而不會引起頁面刷新。
區(qū)別:pushState會在歷史記錄中增加一條,replaceState會替換當前的歷史記錄。
(2)hash: 當頁面的 hash (window.location.hash) 發(fā)生變化時,會觸發(fā)hashchange事件。
兼容性:IE<=7 不支持
window.onhashchange = fn;
window.addEventListener("hashchange", fn, false);
9. 一個DOM元素綁定既綁定了冒泡事件,又綁定了捕獲事件,執(zhí)行順序是?
綁定在同一個DOM元素上的事件,是按照事件聲明的順序執(zhí)行的,無論是捕獲還是冒泡;
這個DOM元素能夠“感知”到的綁定在其他元素上的事件(比如綁定在其父元素上的事件),會按照先捕獲事件,后冒泡事件的順序執(zhí)行。
var box = document.getElementById('box'); // 容器
var btn = document.getElementById('button'); // 容器內的按鈕
// btn
btn.addEventListener('click', function(){ //1
console.log('bubble','btn');
}, false);
btn.addEventListener('click', function(){ //2
console.log('capture','btn');
}, true);
// box
box.addEventListener('click', function(){ //3
console.log('bubble','box');
}, false);
box.addEventListener('click', function(){ //4
console.log('capture','box');
}, true);
//點擊按鈕,console.log:
"capture" "box" //4
"bubble" "btn" //1
"capture" "btn" //2
"bubble" "box" //3
//調換1、2順序,打印結果順序也會調換
//調換3、4順序,打印結果不調換
10. HTTP及HTTP的緩存機制
11.apply、call、bind的區(qū)別
call、apply和bind都可以改變函數(shù)內部this的指向。第一個參數(shù)都是this要指向的對象。
call和apply都可以改變某個函數(shù)運行的上下文,函數(shù)內部的this指向call/apply的第一個參數(shù)。創(chuàng)建的時候會立即調用函數(shù)。
區(qū)別:函數(shù)的參數(shù)傳入call/apply時,call第二個及其以后的參數(shù)位置依次傳入函數(shù)的參數(shù)。apply的第二個參數(shù)是一個數(shù)組,是函數(shù)參數(shù)的數(shù)組。
bind會創(chuàng)建一個新的函數(shù)(綁定函數(shù)),創(chuàng)建的時候不會立即調用。當調用這個函數(shù)時,綁定函數(shù)會以創(chuàng)建時傳入bind()方法的第一個參數(shù)作為this,第二個及其以后的參數(shù)依次傳入原函數(shù)的參數(shù)。
call/apply:
// 獲取數(shù)組中的最大或最小值
var numbers = [1, 2, 3, 100, -100];
Math.max(...numbers);
Math.max.call(null, ...numbers); // 100
Math.max.apply(null, numbers); // 100
// 判斷是否是數(shù)組
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
bind:
var x = 1;
var module = {
x: 2,
getX: function(){
return this.x;
}
}
var fn = module.getX.bind(module);
fn(); // 2
12. 一個DOM元素注冊多個事件,怎樣做到只執(zhí)行第一個事件,后面事件不執(zhí)行?
(1)設置一個全局變量,同一個事件的所有回調函數(shù)都會判斷這個條件,條件滿足就執(zhí)行,執(zhí)行的時候就將這個條件置為false,這樣就能保證只有第一個回調函數(shù)會執(zhí)行。
var btn = document.getElementById('button');
var flag = true;
btn.addEventListener('click', function(){
console.log(1);
if(flag) console.log('1-inner');
flag = false;
}, false);
btn.addEventListener('click', function(){
console.log(2);
if(flag) console.log('2-inner');
}, false);
btn.addEventListener('click', function(){
console.log(3);
if(flag) console.log('3-inner');
}, false);
// console.log:
1
1-inner
2
3
我覺得,這不是個好方法。因為對于注冊在元素上的多個同名事件,其實每個事件都執(zhí)行了。在執(zhí)行到回調函數(shù)內部再進行條件判斷,并不算真正意義上的只執(zhí)行了第一個事件吧。
(2)執(zhí)行某個回調函數(shù)時取消事件監(jiān)聽
// 同一元素注冊多個事件,同一個回調函數(shù)
var btn = document.getElementById('button');
var handler = function(){
console.log('1')
btn.removeEventListener('click', handler, false);
}
btn.addEventListener('click', handler, false);
btn.addEventListener('click', handler, false);
btn.addEventListener('click', handler, false);
// console.log:
1
這種方法,需要保證多個綁定事件的回調函數(shù)是同一個。如果多個事件綁定多個回調函數(shù),則無效。
(3)利用Promise.race()
Promise.race()方法簡介:
Promise.race函數(shù)返回一個Promise,這個Promise的完成方式,取決于傳遞進去的多個Promise哪個先完成,可以是成功resolve,也可以是失敗reject。
var p1 = new Promise(function(resolve, reject){
setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise(function(resolve, reject){
setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise(function(resolve, reject){
setTimeout(reject, 500, 'three'); // 最快執(zhí)行
});
var p4 = new Promise(function(resolve, reject){
setTimeout(reject, 1000, 'four');
})
Promise.race([p1, p2, p3, p4]).then(function(value){
console.log(value);
}, function(reason){
console.log(reason);
});
// console.log:
'three'
一個DOM元素注冊多個事件,只執(zhí)行第一個事件。利用Promise.race()實現(xiàn):
var btn = document.getElementById('btn');
var handler = function(){
console.log('done');
};
var handler2 = function(){
console.log('done2');
};
var handler3 = function(){
console.log('done3');
};
var p1 = new Promise(function(resolve, reject){
console.log(1);
btn.addEventListener('click', handler, false);
resolve();
});
var p2 = new Promise(function(resolve, reject){
console.log(2);
btn.addEventListener('click', handler2, false);
resolve();
});
var p3 = new Promise(function(resolve, reject){
console.log(3);
btn.addEventListener('click', handler3, false);
resolve();
});
Promise.race([p1, p2, p3]).then(function(){
console.log('race');
btn.removeEventListener('click', handler2, false);
btn.removeEventListener('click', handler3, false);
})
// 點擊按鈕,依次打?。? 1
2
3
race
done