在這裏記錄着天天本身遇到的一道印象深入的前端問題,以及一道生活中隨處可見的小問題。前端
強迫本身造成積累的習慣,鞭撻本身不斷前行,共同窗習。vue
Github 地址git
遍歷數組元素,而後將當前元素與之後隨機位置的元素進行交換。es6
function shuffle(a) { for (let i = a.length; i; i--) { let j = Math.floor(Math.random() * i); // es6語法 [a[i - 1], a[j]] = [a[j], a[i - 1]]; } return a; }
惰性函數就是返回一個重寫函數。For example:github
var foo = function() { var t = new Date(); foo = function() { return t; }; return foo(); }; foo();
靜態做用域 —— 函數的做用域基於函數建立的位置。數組
動態做用域 —— 函數的做用域基於函數的使用位置。閉包
var value = 1; function foo() { console.log(value); } function bar() { var value = 2; foo(); } bar(); // 輸出 1 。JavaScript 採用的是詞法做用域,也稱爲靜態做用域。相同的,動態做用域此代碼應該輸出 2
Function.prototype.call2 = function(context, ...args) { // 由於傳進來的 context 有多是 null context = context || window; // Function.prototype this 爲當前運行的函數 // 讓 fn 的上下文爲 context context.fn = this; const result = context.fn(...args); delete context.fn; return result; };
組件在全局用 Vue.component()
註冊時,全局 ID 自動做爲組件的 name。app
指定 name 選項的另外一個好處是便於調試。有名字的組件有更友好的警告信息。另外,當在有 vue-devtools,未命名組件將顯示成 <AnonymousComponent>
,這很沒有語義。經過提供 name 選項,能夠得到更有語義信息的組件樹。dom
hash 路由一個明顯的標誌是帶有#,咱們主要是經過監聽 url 中的 hash 變化來進行路由跳轉。(window.addEventListener('hashchange', this.refresh, false);
)函數
hash 的優點就是兼容性更好,在老版 IE 中都有運行,問題在於 url 中一直存在#不夠美觀,並且 hash 路由更像是 Hack 而非標準,相信隨着發展更加標準化的 History API 會逐步蠶食掉 hash 路由的市場。
history 路由
history 路由使用 History API 來實現,具體有:
window.history.back(); // 後退 window.history.forward(); // 前進 window.history.go(-3); // 後退三個頁面
history.pushState
用於在瀏覽歷史中添加歷史記錄, history.replaceState
方法的參數與pushState
方法如出一轍,區別是它修改瀏覽歷史中當前紀錄,而非添加記錄,一樣不觸發跳轉。
Object.defineProperty
有什麼缺陷?爲何在 Vue3.0 採用了 Proxy
,拋棄了 Object.defineProperty
?function type(obj) { var toString = Object.prototype.toString; var toType = {}; var typeArr = [ "Undefined", "Null", "Boolean", "Number", "String", "Object", "Array", "Function", "Date", "RegExp", "Error", "Arguments" ]; // 這裏利用了object 對象toString() 後 值爲 '[object Array]' 等狀況進行判斷 typeArr.map(function(item, index) { toType["[object " + item + "]"] = item.toLowerCase(); }); return typeof obj !== "object" ? typeof obj : toType[toString.call(obj)]; }
const equals = (a, b) => { if (a === b) return true; // 時間的判斷 if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime(); // 非 object 類型的判斷 if (!a || !b || (typeof a !== "object" && typeof b !== "object")) return a === b; if (a.prototype !== b.prototype) return false; if (Array.isArray(a) && Array.isArray(b)) a.sort(), b.sort(); let keys = Object.keys(a); if (keys.length !== Object.keys(b).length) return false; return keys.every(k => equals(a[k], b[k])); };
mouseover:當鼠標移入元素或其子元素都會觸發事件,因此有一個重複觸發,冒泡的過程。對應的移除事件是 mouseout
mouseenter:當鼠標移除元素自己(不包含元素的子元素)會觸發事件,也就是不會冒泡,對應的移除事件是 mouseleave
閉包就是可以讀取其餘函數內部變量的函數,或者子函數在外調用,子函數所在的父函數的做用域不會被釋放。
一個閉包小栗子:
function f1(){ n = 999; function f2(){ console.log(n); } return f2; } var result = f1(); //返回的是f2函數 result(); //999,讀取內部變量
new 操做符新建了一個空對象,這個對象原型指向構造函數的 prototype,執行構造函數後返回這個對象。
//所謂深度克隆,就是當對象的某個屬性值爲object或array的時候,要得到一份copy,而不是直接拿到引用值 function deepClone1(origin, target) { //origin是被克隆對象,target是咱們得到copy var target = target || {}; //定義target for (var key in origin) { //遍歷原對象 if (origin.hasOwnProperty(key)) { if (Array.isArray(origin[key])) { //若是是數組 target[key] = []; deepClone1(origin[key], target[key]); //遞歸 } else if (typeof origin[key] === "object" && origin[key] !== null) { target[key] = {}; deepClone1(origin[key], target[key]); //遞歸 } else { target[key] = origin[key]; } } } return target; } // 第二個function function deepClone2(data) { if (!data || !(data instanceof Object) || typeof data === "function") { return data; } var constructor = data.constructor; var result = new constructor(); for (var key in data) { if (data.hasOwnProperty(key)) { result[key] = deepClone2(data[key]); } } return result; } // 第三個fuction function deepClone3(origin, target) { var target = target || {}, toStr = Object.prototype.toString; for (var prop in origin) { if (origin.hasOwnProperty(prop)) { //不能把原型鏈上的一塊兒拷貝了 //判斷是元素類型仍是引用類型 if (typeof origin[prop] == "object" && typeof origin[prop] !== "null") { target[prop] = toStr.call(prop) == "[object Array]" ? [] : {}; arguments.callee(origin[prop], target[prop]); //遞歸調用 } else { target[prop] = origin[prop]; //原始類型直接複製 } } } return target; } // 第四個function function deepClone4(obj) { //判斷是不是簡單數據類型, if (typeof obj == "object") { //複雜數據類型 var result = obj.constructor == Array ? [] : {}; for (let i in obj) { result[i] = typeof obj[i] == "object" && obj[i] !== null ? deepClone4(obj[i]) : obj[i]; } } else { //簡單數據類型 直接 == 賦值 var result = obj; } return result; }
推薦使用 deepClone2()
防抖
所謂防抖,就是指觸發事件後在 n 秒內函數只能執行一次,若是在 n 秒內又觸發了事件,則會從新計算函數執行時間。(防誤觸)
// 延緩執行 function debounce(func, wait) { var timeout; return function() { var context = this; var args = arguments; console.log(args); console.log(func); if (timeout) clearTimeout(timeout); timeout = setTimeout(function() { func.apply(context, args); }, wait); }; } // 當即執行 function debounce(func, wait) { var timeout; return function() { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); var callNow = !timeout; timeout = setTimeout(function() { timeout = null; }, wait); if (callNow) func.apply(context, args); }; }
節流
所謂節流,就是指連續觸發事件可是在 n 秒中只執行一次函數。(限制流量)
// 時間戳 function throttle(func, wait) { var previous = 0; return function() { var now = Date.now(); var context = this; var args = arguments; if (now - previous > wait) { func.apply(context, args); previous = now; } }; } // 定時器 function throttle(func, wait) { var timeout; return function() { var context = this; var args = arguments; if (!timeout) { timeout = setTimeout(function() { timeout = null; func.apply(context, args); }, wait); } }; }