本文收錄於 GitHub 日問: DailyQuestion,內含大廠內推機會、面經大全及若干面試題,天天學習五分鐘,一年進入大廠中。javascript
在 Issue 中交流與討論: 01 什麼是防抖和節流,他們的應用場景有哪些
防抖,顧名思義,防止抖動,以避免把一次事件誤認爲屢次,敲鍵盤就是一個天天都會接觸到的防抖操做。css
想要了解一個概念,必先了解概念所應用的場景。在 JS 這個世界中,有哪些防抖的場景呢html
代碼以下,能夠看出來防抖重在清零 clearTimeout(timer)
前端
function debounce (f, wait) { let timer return (...args) => { clearTimeout(timer) timer = setTimeout(() => { f(...args) }, wait) } }
節流,顧名思義,控制水的流量。控制事件發生的頻率,如控制爲1s發生一次,甚至1分鐘發生一次。與服務端(server)及網關(gateway)控制的限流 (Rate Limit) 相似。java
scroll
事件,每隔一秒計算一次位置信息等代碼以下,能夠看出來節流重在加鎖 timer=timeout
node
function throttle (f, wait) { let timer return (...args) => { if (timer) { return } timer = setTimeout(() => { f(...args) timer = null }, wait) } }
clearTimeout
。防抖能夠比做等電梯,只要有一我的進來,就須要再等一下子。業務場景有避免登陸按鈕屢次點擊的重複提交。timer=timeout; timer=null
。節流能夠比做過紅綠燈,每等一個紅燈時間就能夠過一批。<blockquote> 更多描述: 如何獲取瀏覽器的惟一標識,原理是什麼 </blockquote>linux
在 Issue 中交流與討論: 02 在前端開發中,如何獲取瀏覽器的惟一標識
因爲不一樣的系統顯卡繪製 canvas
時渲染參數、抗鋸齒等算法不一樣,所以繪製成圖片數據的 CRC
校驗也不同。css3
function getCanvasFp () { const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') ctx.font = '14px Arial' ctx.fillStyle = '#ccc' ctx.fillText('hello, shanyue', 2, 2) return canvas.toDataURL('image/jpeg') }
所以根據 canvas
能夠獲取瀏覽器指紋信息。nginx
canvas
,獲取 base64
的 dataurlmd5
摘要計算,獲得指紋信息可是對於常見的需求就有成熟的解決方案,若在生產環境使用,可使用如下庫git
它依據如下信息,獲取到瀏覽器指紋信息,而這些信息,則成爲 component
canvas
webgl
UserAgent
AudioContext
requestIdleCallback(function () { Fingerprint2.get((components) => { const values = components.map((component) => component.value) const fp = Fingerprint2.x64hash128(values.join(''), 31) }) })
在 fingerprintjs2
中,對於 component
也有分類
component
同一設備跨瀏覽器也能夠獲得相同的值,有些獨立瀏覽器,獲得不一樣的值component
刷新後值就會發生變化,稱爲不穩定組件在實際業務中,可根據業務選擇合適的組件
const options = { excludes: {userAgent: true, language: true} }
根據 canvas
能夠獲取瀏覽器指紋信息
canvas
,獲取 base64
的 dataurlmd5
摘要計算,獲得指紋信息若在生產環境使用,可使用 fingerprintjs2,根據業務需求,如單設備是否可跨瀏覽器,以此選擇合適的 component
在 Issue 中交流與討論: 03 在服務端應用中如何得到客戶端 IP
若是有 x-forwarded-for
的請求頭,則取其中的第一個 IP,不然取創建鏈接 socket 的 remoteAddr。
而 x-forwarded-for
基本已成爲了基於 proxy 的標準HTTP頭,格式以下,可見第一個 IP 表明其真實的 IP,能夠參考 MDN X-Forwarded-For
X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178 X-Forwarded-For: <client>, <proxy1>, <proxy2>
如下是 koa
獲取 IP 的方法
get ips() { const proxy = this.app.proxy; const val = this.get(this.app.proxyIpHeader); let ips = proxy && val ? val.split(/\s*,\s*/) : []; if (this.app.maxIpsCount > 0) { ips = ips.slice(-this.app.maxIpsCount); } return ips; }, get ip() { if (!this[IP]) { this[IP] = this.ips[0] || this.socket.remoteAddress || ''; } return this[IP]; },
參見源碼: https://github.com/koajs/koa/...
<blockquote> 更多描述: 假設有一個字符串 hello. hello. hello.
須要替換爲 AAA
,即把 hello.
替換爲 A
</blockquote>
在 Issue 中交流與討論: 04 js 如何所有替代一個子串爲另外一個子串
若是須要全量替換字符串,可使用 String.prototype.replace(re, replacer)
,其中正則表達式須要開啓 global
flag
const s = 'foo foo foo' s.replce(/foo/g, 'bar')
那如題中,是否可使用正則表達式來替代子串
答:不能夠,由於使用子串構建正則時,有可能有特殊字符,就有可能出現問題,以下
// 期待結果: 'AhelloX hello3 ' > 'hello. helloX hello3 '.replace(new RegExp('hello. ', 'g'), 'A') < "AAA"
而在 javascript
中替換子串只能使用一種巧妙的辦法:str.split('foo').join('bar')
> 'hello. hello. hello. '.split('hello. ').join('A') < "AAA"
真是一個巧(笨)妙(拙)的辦法啊!!!!!大概 TC39 也意識到了一個問題,因而出了一個新的 API,在 ESNext
中
String.prototype.replaceAll() 'aabbcc'.replaceAll('b', '.'); // 'aa..cc'
詳細文檔在 String.prototype.replaceAll
兩種辦法
str.split('foo').join('bar')
str.replaceAll('foo', 'bar')
,在 ESNext
中,目前支持性很差<blockquote> 更多描述: 在編寫腳本時,有時會出現內存過大發生 OOM 的事情,那咱們如何得知某個進程的內存?另外又如何監控它 </blockquote>
在 Issue 中交流與討論: 05 如何獲取一個進程的內存並監控
經過 ps
能夠獲知一個進程所佔用的內存
$ ps -O rss -p 3506 PID RSS S TTY TIME COMMAND 3506 6984 S pts/1 00:00:00 vim
若是要監控內存,確定使用對進程萬能的命令 pidstat
(PS: 這名字一聽就知道是幹嗎的)
## -r 顯示內存信息 ## -p 指定 pid ## 1: 每一個一秒打印一次 $ pidstat -r -p 3506 1 Linux 3.10.0-957.21.3.el7.x86_64 (shanyue) 11/04/19 _x86_64_ (2 CPU) 20:47:35 UID PID minflt/s majflt/s VSZ RSS %MEM Command 20:47:36 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:37 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:38 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:39 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:40 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:41 0 3506 0.00 0.00 139940 6984 0.18 vim
pidstat
是屬於 sysstat
下的 linux 性能工具,但在 mac 中,如何定位內存的變化?此時可使用萬能的 top/htop
$ htop -p 31796
簡而言之,有如下三個命令
pidstat -r
htop/top -p
ps -O rss -p
關於更多指標的監控能夠參考個人文章: linux 各項監控指標小記
在 Issue 中交流與討論: 06 CORS 若是須要指定多個域名怎麼辦
CORS
經過控制 Access-Control-Allow-Origin
控制哪些域名能夠共享資源,取值以下
Access-Control-Allow-Origin: <origin> | *
其中 *
表明全部域名,origin
表明指定特定域名,那如何設置多個域名了?
此時須要經過代碼實現,根據請求頭中的 Origin
來設置響應頭 Access-Control-Allow-Origin
,那 Origin 又是什麼東西?
並非全部請求都會自動帶上 Origin
,在瀏覽器中帶 Origin
的邏輯以下
Origin
,值爲當前域名Origin
邏輯理清楚後,關於服務器中對於 Access-Control-Allow-Origin
設置多域名的邏輯也很清晰了
Origin
,證實未跨域,則不做任何處理Origin
,證實跨域,根據 Origin
設置相應的 Access-Control-Allow-Origin: <Origin>
使用僞代碼實現以下:
// 獲取 Origin 請求頭 const requestOrigin = ctx.get('Origin'); // 若是沒有,則跳過 if (!requestOrigin) { return await next(); } // 設置響應頭 ctx.set('Access-Control-Allow-Origin', requestOrigin)
此時能夠給多個域名控制 CORS,但此時假設有兩個域名訪問 static.shanyue.tech
的跨域資源
foo.shanyue.tech
,響應頭中返回 Access-Control-Allow-Origin: foo.shanyue.tech
bar.shanyue.tech
,響應頭中返回 Access-Control-Allow-Origin: bar.shanyue.tech
看起來一切正常,但若是中間有緩存怎麼辦?
foo.shanyue.tech
,響應頭中返回 Access-Control-Allow-Origin: foo.shanyue.tech
,被 CDN 緩存bar.shanyue.tech
,因由緩存,響應頭中返回 Access-Control-Allow-Origin: foo.shanyue.tech
,跨域出現問題此時,Vary: Origin
就上場了,表明爲不一樣的 Origin
緩存不一樣的資源
CORS 如何指定多個域名?
根據請求頭中的 Origin
來設置響應頭 Access-Control-Allow-Origin
,思路以下
Vary: Origin
,避免 CDN 緩存破壞 CORS 配置Origin
,證實未跨域,則不做任何處理Origin
,證實瀏覽器訪問跨域,根據 Origin
設置相應的 Access-Control-Allow-Origin: <Origin>
使用僞代碼實現以下
// 獲取 Origin 請求頭 const requestOrigin = ctx.get('Origin'); ctx.set('Vary', 'Origin') // 若是沒有,則跳過 if (!requestOrigin) { return await next(); } // 設置響應頭 ctx.set('Access-Control-Allow-Origin', requestOrigin)
相關問題: 如何避免 CDN 爲 PC 端緩存移動端頁面
在 Issue 中交流與討論: 07 既然 cors 配置能夠作跨域控制,那能夠防止 CSRF 攻擊嗎
對 CORS 一點用也沒有
form
提交不經過 CORS
檢測,你能夠在本地進行測試xhr
及 fetch
進行提交被 CORS 攔住,可是對於簡單請求而言,請求仍被髮送,已形成了攻擊在 Issue 中交流與討論: 08 如何避免 CDN 爲 PC 端緩存移動端頁面
若是 PC 端和移動端是一套代碼則不會出現這個問題。這個問題出如今 PC 端和移動端是兩套代碼,卻共用一個域名。
使用 nginx
配置以下,根據 UA 判斷是否移動端,而走不一樣的邏輯 (判斷UA是否移動端容易出問題)
location / { // 默認 PC 端 root /usr/local/website/web; # 判斷 UA,訪問移動端 if ( $http_user_agent ~* "(Android|webOS|iPhone|iPad|BlackBerry)" ){ root /usr/local/website/mobile; } index index.html index.htm; }
解決方案一般使用 Vary
響應頭,來控制 CDN 對不一樣請求頭的緩存。
此處可使用 Vary: User-Agent
,表明若是 User-Agent 不同,則從新發起請求,而非從緩存中讀取頁面
Vary: User-Agent
固然,User-Agent
實在過多,此時緩存失效就會過多。
使用 Vary: User-Agent
,根據 UA 進行緩存。
Vary: User-Agent
但最好不要出現這種狀況,PC 端和移動端若是是兩套代碼,建議用兩個域名,理由以下
nginx
判斷是否移動端容易出錯在 Issue 中交流與討論: 09 如何實現表格單雙行條紋樣式
經過 css3
中僞類 :nth-child
來實現。其中 :nth-child(an+b)
匹配下標 { an + b; n = 0, 1, 2, ...}
且結果爲整數的子元素
nth-child(2n)
/nth-child(even)
: 雙行樣式nth-child(2n+1)
/nth-child(odd)
: 單行樣式其中 tr
在表格中表明行,實現表格中單雙行樣式就很簡單了:
tr:nth-child(2n) { background-color: red; } tr:nth-child(2n+1) { background-color: blue; }
同理:
:nth-child(-n+3)
:nth-last-child(-n+3)
在 Issue 中交流與討論: 10 簡述下 css specificity
css specificity
即 css 中關於選擇器的權重,如下三種類型的選擇器依次降低
id
選擇器,如 #app
class
、attribute
與 pseudo-classes
選擇器,如 .header
、[type="radio"]
與 :hover
type
標籤選擇器和僞元素選擇器,如 h1
、p
和 ::before
其中通配符選擇器 *
,組合選擇器 + ~ >
,否認僞類選擇器 :not()
對優先級無影響
另有內聯樣式 <div class="foo" style="color: red;"></div>
及 !important
(最高) 具備更高的權重
:not
的優先級影響 - codepen 能夠看出:not
對選擇器的優先級無任何影響
在 Issue 中交流與討論: 11 node 中 module.exports 與 exports 有什麼區別
一句話:exports
是 module.exports
的引用,若是 exports
沒有重賦值,則兩者沒有任何區別
相似以下所示
const exports = module.exports
那以下結果會如何導出?
module.exports = 100 exports = 3
很顯然會導出 100,畢竟 exports
進行了重賦值。
那在 node 源碼中如何實現的呢? 從源碼裏能夠看出 exports 的實質
詳見源碼: https://github.com/nodejs/nod...,能夠看出符合猜測
衆所周知,node 中全部的模塊代碼都被包裹在這個函數中
(function(exports, require, module, __filename, __dirname) { exports.a = 3 });
而如下源碼指出,exports
是如何得來
const dirname = path.dirname(filename); const require = makeRequireFunction(this, redirects); let result; // 從這裏能夠看出來 exports 的實質 const exports = this.exports; const thisValue = exports; const module = this; if (requireDepth === 0) statCache = new Map(); if (inspectorWrapper) { result = inspectorWrapper(compiledWrapper, thisValue, exports, require, module, filename, dirname); } else { // 這裏是模塊包裝函數 result = compiledWrapper.call(thisValue, exports, require, module, filename, dirname); }
<blockquote> 更多描述: 一些 SaaS 系統基於 Pricing 的考慮,會限制團隊人數及同時在線數,如何實現 </blockquote>
在 Issue 中交流與討論: 12 如何獲取當前系統中的在線用戶數 (併發用戶數)一些 SaaS 系統基於訂價策略的考慮,會限制團隊人數及同時在線數,如何實現?
經過 redis
的 zset
可實現併發用戶數。
當一個用戶請求任何接口時,實現一個 middleware,處理如下邏輯
// 當一個用戶訪問任何接口時,對該用戶Id,寫入 zset await redis.zadd(`Organization:${organizationId}:concurrent`, Date.now(), `User:${userId}`) // 查詢當前機構的併發數 // 經過查詢一分鐘內的活躍用戶來確認併發數,若是超過則拋出特定異常 const activeUsers = await redis.zrangebyscore(`Organization:${organizationId}:concurrent`, Date.now() - 1000 * 60, Date.now()) // 查出併發數 const count = activeUsers.length // 刪掉過時的用戶 await redis.zrembyscore(`Organization:${organizationId}:concurrent`, Date.now() - 1000 * 60, Date.now())
在 Issue 中交流與討論: 13 如何把 json 數據轉化爲 demo.json 並下載文件
json 視爲字符串,能夠利用 DataURL
進行下載
Text -> DataURL
除了使用 DataURL,還能夠轉化爲 Object URL 進行下載
Text -> Blob -> Object URL
能夠把如下代碼直接粘貼到控制檯下載文件
function download (url, name) { const a = document.createElement('a') a.download = name a.rel = 'noopener' a.href = url // 觸發模擬點擊 a.dispatchEvent(new MouseEvent('click')) // 或者 a.click() } const json = { a: 3, b: 4, c: 5 } const str = JSON.stringify(json, null, 2) // 方案一:Text -> DataURL const dataUrl = `data:,${str}` download(dataUrl, 'demo.json') // 方案二:Text -> Blob -> ObjectURL const url = URL.createObjectURL(new Blob(str.split(''))) download(url, 'demo1.json')
<a href="url" download><a>
標籤並設置 url
及 download
屬性來下載json
轉化爲 dataurl
來構造 URLjson
轉換爲 Blob
再轉化爲 ObjectURL
來構造 URL在 Issue 中交流與討論: 14 在瀏覽器中如何監聽剪切板中內容
經過 Clipboard API
能夠獲取剪切板中內容,但須要獲取到 clipboard-read
的權限,如下是關於讀取剪貼板內容的代碼:
// 是否可以有讀取剪貼板的權限 // result.state == "granted" || result.state == "prompt" const result = await navigator.permissions.query({ name: "clipboard-read" }) // 獲取剪貼板內容 const text = await navigator.clipboard.readText()
注: 該方法在
devtools
中不生效
相關問題: 【Q019】如何實現選中複製的功能
我是山月,正致力於天天用五分鐘可以看完的簡短答案回答一個大廠高頻面試題。
歡迎關注公衆號【互聯網大廠招聘】,定時推送大廠內推信息及面試題簡答,天天學習五分鐘,半年進入大廠中