性能上flex容許子元素不設置寬高,而是由算法動態去計算,性能會比設定好寬高的稍慢,但在這個時代大致沒有影響;html
傳統佈局+flex != 全部佈局,除了傳統佈局和flex佈局外還有grid佈局、多列布局、雙飛翼佈局、聖盃佈局等多種方法;vue
不要把盒模型和佈局混淆了,佈局是DOM元素在文檔中的位置排布,而模型指的是DOM元素的寬高大小的計算,模型通常由content-box,border-box,padding-box,-webkit-box等,默認爲content-box;node
flex佈局成爲一個新的W3C標準規範;android
flex在移動端兼容性:ios8不兼容,android4.4不兼容;ios
-webkit-box:在移動端開發中,全部的瀏覽器基本上都支持-webkit-boxweb
文檔流:文檔流指元素在文檔中的位置由元素在html裏的位置決定,塊級元素獨佔一行,自上而下排列;內聯元素從左到右排列;正則表達式
脫離文檔流的方式:算法
1)浮動,經過設置float屬性;json
2)絕對定位:經過設置position:absolute;segmentfault
3)固定定位:經過設置position:fixed;
這個問題初一看是對單頁面路由架構的考察,也是一個很好的引入問題,能夠考察很是多方面。好比說:如何實現頁面切換動畫?A、B、C都是表單的話,如何緩存用戶輸入完成的表單數據?...回到問題,由於history api 提供了push/pop/replace三種操做,不管是其中的任何一種都沒法實現上述的效果。一個路由系統,首先要監聽瀏覽器地址的變化,而後根據變化渲染不一樣的頁面。
1)第一層循環 i 從 0 ~ length -1;
第二層循環 j 從 i ~ length - 1;
這樣的循環裏 就能夠找到全部的子序列了
下一步 咱們是要計算出全部子序列的和
最簡單的辦法 就是 第三層循環從 i ~ j 累加求出和 而後求出來的每一個和 和 maxSum 去比較,若是比maxSum 大 就替換
僞代碼: maxSum = maxSum < currentSum ? currentSum : maxSum;
三層循環結束後 maxSum就是咱們要 求的解
return maxSum便可
這個算法的時間複雜度是O(n^3);
2)簡化解法:咱們在第二層循環中,咱們已經知道 當前的 i/j 以前的方法是在第三層的循環中 計算 i ~ j的和
如今 咱們在第二層中 在進入第二層以前 咱們重置一下currentSum
第一次循環 是 i ~ i 當前咱們就把i的值 記錄到currentSum去跟maxSum 對比 而後 maxSum = maxSum < currentSum ? currentSum : maxSum;
第二次循環 是 i ~ i + 1 咱們就把當前的 i + 1 累加到currentSum 這時候的currentSum就是 i ~ i +1 的值,再去跟maxSum去比 而後 maxSum = maxSum < currentSum ? currentSum : maxSum;
以此類推
第二層的循環中 就能夠計算出 以當前 i 開頭的子序列中 最大的子序列是多少
如今咱們看回到 第一層循環i 的取值 是從 0 ~ length -1 那麼咱們是否是 能夠找到 i 從 0 ~ length -1 全部的子序列中和最大的
僞代碼思路:
第一層 i (0~ length -1)
currentSum = 0;
第二層 j (i ~ length -1)
currentSum 累加
maxSum = maxSum < currentSum ? currentSum : maxSum;
return maxSum;
算法的時間複雜度是O(n^2)
3) demo數組:[-2, 1, -3, 4, -1, 2, 1, -5, 4]
首先咱們能夠簡單的簡化一下這個數組 把相鄰的同 正負的數字合起來,由於同符號的連續數 必定會同時存在在最大子序列裏
好比[-1, -2, -3, 1, 2, 13]那跟[-6, 16]是沒有區別的
[-2, 1, -3, 4, -1, 2, 1, -5, 4] ==> [-2, 1, -3, 4, -1, 3, -5, 4]
而後 咱們從頭開始看
-2 這是第一個元素
那麼 咱們認爲 當前的 最大子序列和 就是 -2
而後 發現了一個正數 1
那咱們能夠肯定 -2 必定不包含在 咱們的最大子序列中
也就是說 數組開頭 若是是負數 能夠忽略過去
如今 咱們的數組 變成了 [1, -3, 4, -1, 3, -5, 4]
同理 結尾的若是是 負數 也不須要考慮
如今咱們的數組 變成了[1, -3, 4, -1, 3, -5, 4]
咱們繼續 如今 第一個元素是 1 最大和 是 1
而後下一個數是 -3
那麼 -3 對 1 這個數,起到了阻斷做用 也就是說 -3 把 前面全部正數 積累的能量都磨平了 甚至還變成了一個負數
那麼 -3 咱們稱之爲 一個阻斷
當前的 最大和 仍是 1
如今 咱們到了 4
那麼如今的最大值 就是4
咱們繼續向下看
下個數字是 -1 以前最大的和 是 4
加起來以後是 3 影響並不大
咱們繼續帶着他 向後看
下一個 是個正數 3
也就是 4 -1 3 這樣的狀況
咱們是否是能夠認爲這個 -1 雖然下降了 和 可是 他鏈接了左右的正數 讓咱們當前的 最大值 變成了 6 更新最大值 繼續看
下一個是 -5
同理 以前的 6 + -5 和 仍是 1 也沒有阻斷 咱們去看看 後邊 有沒有一個大數 拯救咱們
後邊一個數 是 4
加上 咱們剛纔記錄的1 和是 5 最後仍是沒有挑戰成功 因此 最大的和 仍是以前的 6
公式:nums是咱們的源數組 nums[i]就是咱們的當前元素currentMax[i]記錄 咱們以i結尾的子序列裏 最大的一個子序列 那麼 currentMax[i] = max(currentMax[i-1] + nums[i], nums[i])
這個公式被稱之爲 狀態轉移公式 咱們的這種解法 稱之爲 動態規劃解法 簡稱:PD
而後咱們去遍歷currentMax這個數組裏 裏邊的最大值 就是咱們要找的 最大值
僞代碼:
var maxSubArray = function(nums) {
// 初始化源數組,初始化An爲結束的最大值
let A = nums;
let dp = [];
let maxSum = A[0];
dp[0] = A[0];
for (let i = 1; i < A.length; i++) {
// 狀態轉移公式
dp[i] = max(A[i], dp[i-1] + A[i])
maxSum = dp[i] > maxSum ? dp[i] : maxSum;
}
return maxSum;
}
function max(a, b) {
return a > b ? a : b;
}
複製代碼
函數節流場景:
1)例如:實現一個原生的拖拽功能(若是不用H5 Drap和Drop API),咱們就須要一路監聽mousemove事件,在回調中獲取元素當前位置,而後重置dom的位置。不加以控制,每移動必定像素而發出的回調數量是會很是驚人的,回調中又伴隨着DOM操做,繼而引起瀏覽器的重排和重繪,性能差的瀏覽器可能會直接假死。 2)這時,咱們就須要下降觸發回調的頻率,好比讓它500ms觸發一次或者200ms,甚至100ms,這個閥值不能太大,太大了拖拽就會失真,也不能過小,過小了低版本瀏覽器可能會假死,這時的解決方案就是函數節流【throttle】。 3)函數節流的核心就是:讓一個函數不要執行得太頻繁,減小一些過快的調用來節流 函數去抖場景:
underscore節流函數,返回函數連續調用時,func執行頻率限定爲 次/ wait
@param {function} func 回調函數
@param {number} wait 表示時間窗口的間隔
@param {object} options 若是想忽略開始函數的調用,傳入{leading:false}
若是想忽略結尾函數的調用,傳入{trailling: false}
二者不能共存,不然函數不能執行
@return {function} 返回客戶調用函數
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
// 以前的時間戳
var previous = 0;
// 若是 options 沒傳則設爲空對象
if (!options) options = {};
// 定時器回調函數
var later = function(){
// 若是設置了 leading,就將 previous 設爲0
// 用於下面函數的第一個 if 判斷
previous = options.leading === false ? 0 : _.now();
// 置空一是爲了防止內存泄露,二是爲了下面的定時器判斷
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
}
return function() {
// 得到當前時間戳
var now = _.now();
// 首次進入前者確定爲 true
// 若是須要第一次不執行函數,就將上次時間戳設爲當前的
// 這樣在接下來計算 remaining 的值時會大於0
if (!previous && options.leading === false) previous = now;
// 計算剩餘時間
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 若是當前調用已經大於上次調用時間 + wait
// 或者用戶手動調了時間
// 若是設置了 trailling,那麼第一次會進入這個條件
// 還有一點,你可能會以爲開啓了定時器那麼應該不會進入這個if 條件了,其實仍是會進入的,由於定時器的延時並非
// 準確的時間,極可能你設置了2秒,可是他須要2.2秒才觸發
// 這時候就會進入這個條件
if (remaining <= 0 || remaining > wait) {
// 若是存在定時器就清理掉不然會調用二次回調
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
}
previous = now;
result = func.apply(context, args);
if (!timeout) {
context = args = null;
} else if (!timeout && options.trainlling !== false){
// 判斷是否設置了定時器和 trailling
// 沒有的話就開啓一個定時器
// 而且不能同時設置 leading 和 trailling
timeout = setTimeout(later, remaining);
}
return result;
}
}
複製代碼
這實際上是個單點登陸的問題,同一級域名下設置cookie設在一級域名下,同級域名下的cookie皆共享。 缺點:
同一級域名限制
登陸的規則一致,若是不一樣域的話就是跨域問題,跨域問題能夠用jsonp解決
兩者的區別: 1)做用域
相同瀏覽器的不用頁面間能夠共享相同的localStorage(頁面屬於相同域名和端口),可是sessionStorage只能在同源(相同域名相同端口)同學口訪問,可是當sessionStorage在同一窗口下轉到同源頁面仍是能夠訪問的,由於這時候仍是同源同學口,不要單純理解爲兩個不一樣的頁面之間不能訪問相同sessionStorage。好比你在A網頁設置了一個sessionStorage的值,而後你同時在新的窗口下打開B網頁,這時候你嘗試在B網頁獲得A網頁設置的sessionStorage是不能夠的,可是當你在A網頁跳轉到B網頁的時候,這時候你會發現B網頁能夠訪問A網頁中的sessionStorage。因此sessionStorage針對的是同源同學口,不是同源同頁面。 2)生命週期
localStorage生命週期是永久,這意味着除非用戶本身清除localStorage信息或者用戶清除瀏覽器緩存,不然這些信息將永久存在。sessionStorage生命週期爲當前窗口或標籤頁,一旦窗口或者標籤頁被永久關閉了,那麼全部經過sessionStorage存儲的數據也被清空了
cookie與sessionStorage、localStorage的區別
一、cookie能夠在瀏覽器端與服務器端之間通訊,是服務器端獲取用戶信息、保持一種持久客戶端狀態的關鍵。而sessionStorage、localStorage雖然也能夠保存會話數據,可是不能與服務器端進行信息交換。
二、cookie的容量比較小,而sessionStorage、localStorage有較大的容量。
三、試用的瀏覽器範圍不一樣,因爲sessionStorage與localStorage是HTML5標準推出的,因此在IE7及如下的版本不適用,替代方案是採用IE的userData。
複製代碼
三者的異同
一、XSS跨網站指令碼(Cross-site- scripting,簡稱:xss)是一種網站應用程式的安全漏洞攻擊,是代碼注入的一種。它容許惡意使用者將程式碼注入到網頁上,其餘使用者在觀看網頁時就會收到影響。這類攻擊一般包含了HTML以及使用者端腳本語言。
二、XSS分爲三中:反射型,存儲型和DOM-based
三、如何攻擊:
XSS經過修改HTML節點或者執行JS代碼來攻擊網站;eg:經過URL獲取某些參數(反射型攻擊)
另外一種場景:寫了一篇包含攻擊代碼的 文章,那麼可能瀏覽文章的用戶都會被攻擊到。這種攻擊類型是存儲型攻擊,也能夠說是DOM-based攻擊,這種攻擊打擊面更廣。
四、如何防護:最廣泛的作法是 轉義輸入輸出的內容,對於引號、尖括號、斜槓進行轉義,這裏寫圖片描述、內容安全策略(CSP)是一個額外的安全層,用於檢測並削弱某些特定類型的攻擊,包括跨站腳本(XSS)和數據注入攻擊等。不管是數據盜取、網站內容污染仍是散發惡意軟件,這些攻擊都是主要的手段,咱們能夠經過CSP來儘可能減小XSS攻擊。CSP本質上也是創建白名單,規定了瀏覽器只可以執行特定來源的代碼。經過能夠經過HTTP Header中的Content-Security-Policy來開啓CSP,只容許加載本站資源,只容許加載HTTPS協議圖片,容許加載任何來源框架
複製代碼
CSRF概念:CSRF跨站點請求僞造(Cross-Site Request Forgery),跟XSS攻擊同樣,存在巨大的危害性,參考文章
一、本身的電腦訪問cdn地址,排除是否cdn問題;
二、讓用戶換瀏覽器再試,排除用戶瀏覽器的問題(禁用全部插件、清除緩存);
三、已經排除cdn和用戶瀏覽器的問題,那就是用戶的機器(或者所在的網絡)有問題,有多是用戶所在的公司網絡禁止下載某些資源。
四、推薦一個[本地網絡診斷的工具](https://cdn.dns-detect.alicdn.com/https/doc.html),能夠檢查dns和本地ip地址是否正常
複製代碼
一、資源加載,採用預加載,優先加載到內存中,作到無縫切換,使用原生loading
二、離線加載靜態資源,不走網絡請求;
三、儘可能作到局部更新,動畫使用transform,will-change來提升性能;
四、使用webkit over scrolling加速滾動條的滾動,去掉user selection,去掉高亮,手勢交互原生實現,複雜交互如日期選擇器等調用原生組件
五、遵循APP UI設計規範;
六、考慮文件diff更新,或主動後臺請示,在某個時刻更新,兩個版本直接的兼容問題;
七、APP方法加上安全性的考慮。
複製代碼
document 往事件觸發處傳播,遇到註冊的捕獲事件會觸發 傳播到事件觸發處時觸發註冊的事件,從事件觸發處往document傳播,遇到註冊的冒泡事件會觸發,事件觸發通常來講會按照上面的順序進行,可是也有特例,若是給一個目標節點同時註冊冒泡和捕獲事件,事件觸發會按照註冊的順序執行。
// 如下會先打印冒泡而後是捕獲
node.addEventListener('click', (event) => {
console.log('冒泡')
}, false);
node.addEventListener('click', (event) => {
console.log('捕獲');
}, true)
複製代碼
** 註冊事件**
一般咱們使用addEventListener註冊事件,該函數的第三個參數能夠是布爾值,也能夠是對象。對於布爾值 useCapture參數來講,該參數默認值是 false,useCapture決定了註冊的事件是捕獲事件仍是冒泡事件。對於對象參數來講,可使用如下幾個屬性:
通常來講,咱們只但願事件只觸發在目標上,這時候能夠用stopPropagation來阻止事件的進一步傳播。一般咱們認爲stopPropagation是用來阻止事件冒泡的,其實該函數也能夠阻止捕獲事件。stopImmediatePropagation一樣也能實現阻止事件,可是還能阻止該事件目標執行別的註冊事件。
node.addEventListener('click', (event) => {
event.stopImmediatePropagation();
console.log('冒泡');
}, false);
// 點擊 node 只會執行上面的函數,該函數不會執行
node.addEventListener('click', (event) => {
console.log('捕獲');
})
複製代碼
事件代理: 若是一個節點中的子節點是動態生成的,那麼子節點須要註冊事件的話應該註冊在父節點上
ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let ul = document.querySelector('##ul')
ul.addEventListener('click', (event) => {
console.log(event.target);
})
</script>
複製代碼
事件代理的方式相對於直接給目標註冊事件來講,有如下優勢 節省內存 不須要給子節點註銷事件 點擊查看詳解