您好,登錄后才能下訂單哦!
本篇內容介紹了“從前端性能優化引申出來的經典面試題有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
渲染優化
渲染優化是前端優化中一個很重要的部分,一個好的首屏時間能給用戶帶來很好的體驗,這里要說的一點是關于首屏時間的定義,不同的團隊對首屏時間定義不一樣,有的團隊認為首屏時間就是白屏時間,是從頁面加載到第一個畫面出現的時間。但是當我們說到用戶體驗的時候,僅僅是這樣還達不到效果,所以有的前端團隊認為,首屏時間應該是從頁面加載到用戶可以進行正常的頁面操作時間,那么我們就依照后者來進行說明
js css 加載順序
說渲染優化之前,我們還需要說一個小插曲,就是比較經典的一道問題"瀏覽器地址欄輸入url發生了什么",理解了這個我們才可以更清楚js,css加載順序對渲染的影響
問題 1:地址欄輸入url 發生了什么
這個問題經常被人提起,有人回答比較簡潔點,有人可能回答的比較詳細,下面就說一下主要流程
首先會進行 url 解析,根據 dns 系統進行 ip 查找
根據 ip 就可以找到服務器,然后瀏覽器和服務器會進行 TCP 三次握手建立連接,如果此時是 https 的話,還會建立 TLS 連接以及協商加密算法,這里就會出現另一個需要注意的問題"https 和 http 的區別"(下文會講到)
連接建立之后瀏覽器開始發送請求獲取文件,此時這里還會出現一種情況就是緩存,建立連接后是走緩存還是直接重新獲取,需要看后臺設置,所以這里會有一個關注的問題"瀏覽器緩存機制",緩存我們等會在講,現在我們就當沒有緩存,直接去獲取文件
首先獲取 html 文件,構建 DOM 樹,這個過程是邊下載邊解析,并不是等 html 文件全部下載完了,再去解析 html,這樣比較浪費時間,而是下載一點解析一點
好了解析到 html 頭部時候,又會出現一種問題,css,js 放到哪里了?不同的位置會造成渲染的不同,此時就會出現另一個需要關注的問題"css,js 位置應該放哪里?為什么",我們先按照正確的位置來說明(css 放頭部,js 放尾部)
解析到了 html 頭部發現有 css 文件,此時下載 css 文件,css 文件也是一邊下載一邊解析的,構建的是 CSSOM 樹,當 DOM 樹和 CSSOM 樹全部構建完之后,瀏覽器會把 DOM 樹和 CSSOM 樹構建成渲染樹。
樣式計算, 上面最后一句"DOM 樹和 CSSOM 樹會一起構建成渲染樹"說的有點籠統,其實還有更細一點的操作,但是一般回答到上面應該就可以了,我們現在接上面說一下構造渲染樹的時候還做了哪些事情。第一個就是樣式計算,DOM樹 和 CSSOM樹有了之后,瀏覽器開始樣式計算,主要是為 DOM 樹上的節點找到對應的樣式
構建布局樹,樣式計算完之后就開始構建布局樹。主要是為 DOM 樹上的節點找到頁面上對應位置以及一些"display:none"元素的隱藏。
構建分層樹,布局樹完成后瀏覽器還需要建立分層樹,主要是為了滿足滾動條,z-index,position 這些復雜的分層操作
將分層樹圖塊化,利用光柵找到視圖窗口下的對應的位圖。主要是因為一個頁面可能有幾屏那么長,一下渲染出來比較浪費,所以瀏覽器會找到視圖窗口對應的圖塊,將這部分的圖塊進行渲染
最終渲染進程將整個頁面渲染出來,在渲染的過程中會還出現重排和重繪,這也是比較愛問的問題"重排重繪為什么會影響渲染,如何避免?"
以上過程大概講解了一下從 url 到頁面渲染的整個過程,其實涉及到了幾個需要關注的問題,下面來具體講講
問題 2:js css 順序對前端優化影響
上面我們說到了整個渲染流程,但是沒有說到 css 和 js 對渲染的影響。渲染樹的構成必須要 DOM 樹和 CSSOM 樹的,所以盡快的構建 CSSOM 樹是一個重要的優化手段,如果 css 文件放在尾部,那么整個過程就是一個串行的過程先解析了 dom,再去解析 css。所以 css 我們一般都是放在頭部,這樣 DOM 樹和 CSSOM 樹的構建是同步進行的。
再來看 js,因為 js 的運行會阻止 DOM 樹的渲染的,所以一旦我們的 js 放在了頭部,而且也沒有異步加載這些操作的話,js 一旦一直在運行,DOM 樹就一直構建不出來,那么頁面就會一直出現白屏界面,所以一般我們會把 js 文件放在尾部。當然放到尾部也不是就沒有問題了,只是問題相對較小,放到尾部的 js 文件如果過大,運行時間長,代碼加載時,就會有大量耗時的操作造成頁面不可點擊,這就是另一個問題,但這肯定比白屏要好,白屏是什么頁面都沒有,這種是頁面有了只是操作不流暢。
js 腳本放在尾部還有一個原因,有時候 js 代碼會有操作 dom 節點的情況,如果放在頭部執行,DOM樹還沒有構建,拿不到 DOM 節點但是你又去使用就會出現報錯情況,錯誤沒處理好的話頁面會直接崩掉
問題 3:重排重繪為什么會影響渲染,如何避免?
重排和重繪為什么會影響渲染,哪個影響更大,如何避免是經常被問到的一道題目,我們先來說一下重繪
重繪
重繪指的是不影響界面布局的操作,比如更改顏色,那么根據上面的渲染講解我們知道,重繪之后我們只需要在重復進行一下樣式計算,就可以直接渲染了,對瀏覽器渲染的影響相對較小
重排
重排指的是影響界面布局的操作,比如改變寬高,隱藏節點等。對于重排就不是一個重新計算樣式那么簡單了,因為改變了布局,根據上面的渲染流程來看涉及到的階段有樣式計算,布局樹 重新生成,分層樹重新生成,所以重排對瀏覽器的渲染影響是比較高的
避免方法
js 盡量減少對樣式的操作,能用 css 完成的就用 css
果必須要用 js 操作樣式,能合并盡量合并不要分多次操作
resize 事件 最好加上防抖,能盡量少觸發就少觸發
加載對 dom 操作盡量少,能用 createDocumentFragment 的地方盡量用
加圖片的時候,提前寫好寬高
問題 4:瀏覽器緩存機制
瀏覽器緩存是比較常見的問題,我會從瀏覽器緩存的方式,緩存的實現, 緩存在哪里這幾個點來說明
緩存方式
我們經常說的瀏覽器緩存有兩種,一種是強制緩存,一種是協商緩存,因為下面有具體實現講解,所以這里就說一下概念
協商緩存
協商緩存意思是文件已經被緩存了,但是否從緩存中讀取是需要和服務器進行協商,具體如何協商要看請求頭/響應頭的字段設置,下面會說到。需要注意的是協商緩存還是發了請求的
強制緩存
強制緩存就是文件直接從緩存中獲取,不需要發送請求
緩存實現
強制緩存
強制緩存在 http1.0 的時候用的是 Expires,是響應頭里面的一個字段表示的是文件過期時間。是一個絕對時間,正因為是絕對時間所以在某些情況下,服務器的時區和瀏覽器時區不一致的時候就會導致緩存失效。為了解決這個問題,HTPP1.1 引入了一個新的響應頭 cache-control 它的可選值如下
cache-control
max-age: 緩存過期時間,是一個相對時間
public: 表示客戶端和代理服務器都會緩存
private: 表示只在客戶端緩存
no-cache: 協商緩存標識符,表示文件會被緩存但是需要和服務器協商
no-store: 表示文件不會被緩存
HTTP1.1 利用的就是 max-age:600 來強制緩存,因為是相對時間,所以不會出現 Expires 問題
協商緩存
協商緩存是利用 Last-Modified/if-Modified-Since,Etag/if-None-Match 這兩對請求、響應頭。
Last-Modified/if-Modified-Since
Etag/If-None-Match
由于 Last-Modified 的時間粒度是秒,有的文件在 1s 內可能被改動多次。這種方式在這種特殊情況下還是會失效,所以HTTP1.1又引入了 Etag 字段。這個字段是根據文件內容生成一個標記符比如"W/"5f9583bd-10a8"",然后再和 If-None-Match 進行對比就能更準確的知道文件有沒有被改動過
瀏覽器第一次發送請求獲取文件緩存下來,服務器響應頭返回一個 if-Modified-Since,記錄被改動的時間
瀏覽器第二次發送請求的時候會帶上一個 Last-Modified 請求頭,時間就是 if-Modified-Since 返回的值。然后服務器拿到這個字段和自己內部設置的時間進行對比,時間相同表示沒有修改,就直接返回 304 從緩存里面獲取文件
緩存在哪里
知道了緩存方式和實現,再來說一下緩存存在哪個地方,我們打開掘金可以看到如下的信息 。緩存的來源有兩個地方 from dist cache,from memeory cache
form memory cache
這個是緩存在內存里面,優點是快速,但是具有時效性,當關閉 tab 時候緩存就會失效。
from dist cache
這個是緩存在磁盤里面,雖然慢但是還是比請求快,優點是緩存可以一直被保留,即使關閉 tab 頁,也會一直存在
何時緩存在memory,合適緩存在dist?
這個問題網上很少找的到標準答案,大家一致的說法是js,圖片文件瀏覽器會自動保存在memory中,css文件因為不常修改保存在dist里面,我們可以打開掘金網站,很大一部分文件都是按照這個規則來的,但是也有少數js文件也是緩存在dist里面。所以他的存放機制到底是什么樣了?我帶著這個疑問查了好多文章,雖然最后沒有確切找到答案,但是一個知乎的回答可以給我們提供思路,下面引用一個知乎回答者一段話
第一個現象(以圖片為例):訪問-> 200 -> 退出瀏覽器再進來-> 200(from disk cache) -> 刷新 -> 200(from memory cache)。總結: 會不會是chrome很聰明的判斷既然已經從disk拿來了, 第二次就內存拿吧 快。(笑哭)
第二個現象(以圖片為例):只要圖片是base64 我看都是from memroy cache。總結: 解析渲染圖片這么費勁的事情,還是做一次然后放到內存吧。用的時候直接拿
第三個現象(以js css為例):個人在做靜態測試的發現,大型的js css文件都是直接disk cache。結: chrome會不會說 我去 你這么大太占地方了。你就去硬盤里呆著吧。慢就慢點吧。
第四個現象:隱私模式下,幾乎都是 from memroy cache。總結: 隱私模式 是吧。我不能暴露你東西,還是放到內存吧。你關,我死。
上面幾點是雖然很幽默,但是卻可以從中找到一部分答案,但是我覺得另一個知乎回答我更贊同
瀏覽器運行的時候也是由幾個進程協作的,所以操作系統為了節省內存,會把一部分內存里的資源交換回磁盤的交換區,當然交換是有策略的,比如最常用的就是LRU。
什么時候存dist,什么時候存memoey都是在瀏覽器控制下的,memory不夠了可能就會考慮去存dist了,所以經過上面所說我自己總結結果如下
大一點的文件會緩存在dist里面,因為內存也是有限的,磁盤的空間更大
小一點文件js,圖片存的是memory
css文件一般存在dist
特殊情況memory大小是有限制的,瀏覽器也會根據自己的內置算法,把一部分js文件存到dist里面
問題 5:https 和 http 的區別
說到https和http的區別,可以說一下https服務器和客戶端連接的差異,以及https特定的加密算法協商,甚至可能還要說到對稱加密,非對稱加密和證書等,篇幅很長,請看我之前單獨寫的一篇https詳解,里面講的非常詳細。
請求優化
講請求優化的之前先來總結下上面說到的js, css文件順序優化,為了讓渲染更快,我們需要把js放到尾部,css放到頭部,然后還要注意在書寫js的時候盡量減少重排,重繪。書寫html,css的時候盡量簡潔,不要冗余,目的是為了更快的構建DOM樹和CSSOM樹。好了下面我們在來說說請求優化,請求優化可以從請求數量和請求時間兩方面入手
減少請求數量
將小圖片打包成base64
利用雪碧圖融合多個小圖片
利用緩存上面已經說到過
減少請求時間
將js,css,html等文件能壓縮的盡量壓縮,減少文件大小,加快下載速度
利用webpack打包根據路由進行懶加載,不要初始就加載全部,那樣文件會很大
能升級到高版本的http就升級到高版本(這個回答是套話),為什么高版本能提高速度具體看上面我說的那篇https文章
建立內部CDN能更快速的獲取文件
webpack優化
介紹了渲染優化,現在來看看webpack優化,自己平常寫demo給團隊做培訓的時候都是自己手寫webpack配置,雖然也就幾十行,但每次都能讓我鞏固webpack的基本配置,下面直接說一下webpack優化手段有哪些
基礎配置優化
extensions 這個配置是屬于resolve里面的,經常用來對文件后綴進行擴展,寫法如下
resolve: { extensions: ['.ts', '.tsx', '.js'] }
這個配置表示webpack會根據extensions去尋找文件后綴名,所以如果我們的項目主要用ts寫的話,那我們就可以.tsx和.ts寫前面,目的是為了讓webpack能夠快速解析
alias 這個配置也是屬于resolve里面的,是用來映射路勁,能減少打包時間的主要原因是能夠讓webpack快速的解析文件路徑,找到對應的文件,配置如下
resolve: { alias: { Components: path.resolve(__dirname, './src/components') } }
noParse
noParse表示不需要解析的文件,有的文件可能是來自第三方的文件,被 providePlugin引入作為windows上的變量來使用,這樣的文件相對比較大,并且已經是被打包過的,所以把這種文件排除在外是很有必要的,配置如下
module: { noParse: [/proj4\.js/] }
exclude
某些loader會有這樣一個屬性,目的是指定loader作用的范圍,exclude表示排除某些文件不需要babel-loader處理,loader的作用范圍小了,打包速度自然就快了,用babel-loader舉一個簡單例子
{ test: /\.js$/, loader: "babel-loader", exclude: path.resolve(__dirname, 'node_modules') }
devtool
這個配置是一個調試項,不同的配置展示效果不一樣,打包大小和打包速度也不一樣,比如開發環境下cheap-source-map肯定比source-map快,至于為什么,強烈推薦自己之前寫的這一篇講解devtool的文章:webpack devtools篇講的非常詳細。
{ devtool: 'cheap-source-map' }
.eslintignore
這個雖不是webpack配置但是對打包速度優化還是很有用的,在我的實踐中eslint檢查對打包的速度影響很大,但是很多情況我們不能沒有這個eslint檢查,eslint檢查如果僅僅在vs里面開啟的話,可能不怎么保險。
因為有可能你vs中的eslint插件突然關閉了或者某些原因vs不能檢查了,只能靠webpack構建去幫你攔住錯誤代碼的提交,即使這樣還不能確保萬無一失,因為你可能某一次提交代碼很急沒有啟動服務,直接盲改提交上去了。這個時候只能通過最后一道屏障給你保護,就是在CI的時候。比如我們也會是在jenkins構建的時候幫你進行eslint檢查,三道屏障確保了我們最終出的鏡像是不會有問題的。
所以eslint是很重要的,不能刪掉,在不能刪掉的情況下怎么讓檢查的時間更少了,我們就可以通過忽略文件,讓不必要的文件禁止eslint,只對需要的文件eslint可以很大程度提高打包速度
loader,plugins優化
上述說了幾個基礎配置優化,應該還有其他的基礎配置,今后遇到了再繼續添加,現在在來講講利用某些loader,plugins來提高打包速度的例子
cache-loader
這個loader就是在第一次打包的時候會緩存打包的結果,在第二次打包的時候就會直接讀取緩存的內容,從而提高打包效率。但是也需要合理利用,我們要記住一點你加的每一個loader,plugins都會帶來額外的打包時間。這個額外時間比他帶來的減少時間多,那么一味的增加這個loader就沒意義,所以cache-loader最好用在耗時比較大的loader上,配置如下
{ rules: [ { test: /\.vue$/, use: [ 'cache-loader', 'vue-loader' ], include: path.resolve(__dirname, './src') } ] }
webpack-parallel-uglify-plugin, uglifyjs-webpack-plugin, terser-webpack-plugin
在上面的渲染優化中我們已經知道,文件越小渲染的速度是越快的。所以我們在配置webpack時候經常會用到壓縮,但是壓縮也是需要消耗時間的,所以我們我們經常會用到上面三個插件之一來開啟并行壓縮,減少壓縮時間,我們用webpack4推薦使用的terse-webpack-plugin做例子來說明
optimization: { minimizer: [ new TerserPlugin({ parallel: true, cache: true }) ], }
happypack, parallel-webpack, thread-loader
這幾個loader/plugin和上面一樣也是開啟并行的,只不過是開啟并行構建。由于happypack的作者說自己的興趣已經不再js上了,所以已經沒有維護了,并推薦如果使用的是webpack4的話,就去使用thread-loader。基本配置如下
{ test: /\.js$/, use: [ { loader: "thread-loader", options: threadLoaderOptions }, "babel-loader", ], exclude: /node_modules/, }
DllPlugin,webpack.DllReferencePlugin
上面說的幾個并行插件理論上是可以增加構建速度,網上很多文章都是這么說的,但是我在實際的過程中使用,發現有時候不僅沒提升反而還降低了打包速度,網速查閱給的理由是可能你的電腦核數本來就低,或者當時你CPU運行已經很高了,再去開啟多進程導致構建速度降低。
上面說的幾個并行插件可能在某些情況下達不到你想要的效果,然而在我們團隊優化webpack性能經驗來看,這次所說的兩個插件是很明顯并且每次都能提高打包速度的。原理就是先把第三方依賴先打包一次生成一個js文件,然后真正打包項目代碼時候,會根據映射文件直接從打包出來的js文件獲取所需要的對象,而不用再去打包第三方文件。只不過這種情況打包配置稍微麻煩點,需要寫一個webpack.dll.js。大致如下
webpack.dll.js
const path = require('path'); const webpack = require('webpack'); module.exports = { entry: { library: ["vue", "moment"] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, 'json-dll'), library: '[name]' }, plugins: [ new webpack.DllPlugin({ path: './json-dll/library.json', name: '[name].json' }) ] }
webpack.dev.js
new AddAssetHtmlWebpack({ filepath: path.resolve(__dirname, './json-dll/library.dll.js') }), ew webpack.DllReferencePlugin({ manifest: require("./json-dll/library.json") })
其他優化配置
這些插件就簡單的介紹下,在我的個人項目中已經使用過,自我感覺還可以,具體使用可以查閱npm或者github
webpack-bundle-analyzer
這個插件可以用可視化幫我們分析打包體積,從而來采用合適的優化方式去改進我們的webpack配置
speed-measure-webpack-plugin
這個插件可以告訴我們打包時候每一個loader或者plugin花費了多少時間,從而對耗時比較長的plugin和loader做優化
friendly-errors-webpack-plugin
這個插件可以幫我們優化打包日志,我們打包時候經常看到很長一個日志信息,有的時候是不需要的,也不會去看所以可以用這個插件來簡化
代碼優化
這是最后一部分代碼優化了,這里的代碼性能優化我只說我在工作中感受到的,至于其他的比較小的優化點比如createDocumentFragment使用可以查查其他文章
能不操作dom不要操作dom,哪怕有時候需要改設計
很多情況下我們都能用css還原設計稿,但是有些時候單單從css沒法還原,尤其組件還不是你寫的時候,比如我們團隊用的就是antd,有時候的產品設計單從css上沒法實現,只能動用js,刪除,增加節點在配合樣式才能完成。
由于我們又是一個做大數據的公司,這個時候就會出現性能問題,最開始寫代碼時候,產品說什么就是什么,說什么我都會想辦法搞出來,不管用什么方法。后來到客戶現場大數據請況下,性能缺點立馬暴露的出來。
所以代碼優化的原則之一我認為是能不寫的代碼就不寫,當然這是要從性能角度出發,通過性能分析給產品說出理由,并且最好還能提供更好的解決方案,這個才是我們需要考慮的。
如果用的是react 一定用寫shouldComponentUpdate這個生命周期函數,不然打印的時候你會發現,你自己都迷糊為什么執行了這么多遍
將復雜的比對,變成簡單比對
這句話是什么意思了?我們就拿shouldComponentUpdate舉例子,用這個函數沒問題,但是可以做的更好,我們在工作中經常這么寫
shouldComponentUpdate(nextPrpops) { return JSON.stringify(nextPrpops.data) !== JSON.stringify(this.props.data) }
如果這是一個分頁表格,data是每一頁數據,數據改變了重新渲染,在小數據場景下這本身是沒有問題。但是如果在大數據的場景下可能會有問題,可能有人有疑問,既然做了分頁怎么還會有大數據了,因為我們的產品是做大數據分析日志的,一頁十條日志,有的日志可能非常的長,也就是說就算是10條數據比對起來也是很耗時,所以當時想法能不能找到其他的替代變量來表示數據變了?比如下面這樣
shouldComponentUpdate(nextPrpops) { return nextPrpops.data[0].id !== this.props.data[0].id }
第一條的id不一樣就表示數據變化了行不行,顯然在某種情況下是存在的,也有人會說可能會出現id一樣,那如果換成下面這種了?
shouldComponentUpdate(nextPrpops) { return nextPrpops.current !== this.props.current }
將data的比對轉換成了current的比對,因為頁數變了,數據基本都是變了,對于我們自己日志的展示來說基本不存在兩頁數據是一模一樣的,如果有那可能是后臺問題。然后在好好思考這個問題,即使存在了兩頁數據一摸一樣,頂多就是這個表格不重新渲染,但是兩頁數據一摸一樣不重新渲染是不是也沒有問題,因為數據是一樣的。或者如果你還是不放心,那下面這種會不會好點
this.setState({ data, requestId: guid() }) shouldComponentUpdate(nextPrpops) { return nextPrpops.requestId !== this.props.requestId }
給一個requestId跟宗data,后面就只比對requestId。上面的寫法可能都有問題,但是主要是想說的是我們在寫代碼時候可以想想是不是可以"將復雜的比對,變成簡單比對"
學習數據結構和算法,一定會在你的工作中派上用場
我們經常會聽到學習數據結構和算法沒有什么大的用處,因為工作基本用不上。這句話我之前覺得沒錯,現在看來錯的很嚴重。我們所學的每一樣技能,都會在將來的人生中派上用場。之前寫完代碼就丟了不去優化所以我覺得算法沒意義,又難又容易忘記。但現在要求自己做完需求,開啟mock,打開perfermance進行大數據量的測試,看著那些標紅的火焰圖和肉眼可見的卡頓,就明白了算法和數據結構的重要性,因為此時你只能從它身上獲取優化,平時你很排斥它,到優化的時候你是那么想擁有它。我拿自己之前寫的代碼舉例,由于公司代碼是保密的我就把變量換一下,偽代碼如下
data.filter(({id}) => { return selectedIds.includes(id); })
就是這樣幾行代碼,邏輯就是篩選出data里面已經被勾選的數據。基本上很多人都可能這么寫,因為我看我們團隊里面都是這么寫的。產品當時已經限制data最多200數據,所以寫完完全沒壓力,性能沒影響。但是秉著對性能優化的原則(主要是被現場環境搞怕了~~~),我開啟了mock服務,將數據調到了2萬條再去測試,代碼弊端就暴露出來了,界面進入卡頓,重新選擇的時候也會卡頓。然后就開始了優化,當時具體的思路如下
按照現在的代碼來看,這是一個兩層循環的暴力搜索時間復雜度為O(n^2)。所以想著能不能降一下復雜度至少是O(nlogn),看了一下代碼只能從selectedIds.includes(id)這句入手,于是想著可不可以用二分,但是立馬被否定因為二分是需要有序的,我這數組都是字符串怎么二分。
安靜了一下之后,回想起看過的算法課程和書籍以及做的算法題,改變暴力搜索的方法基本都是
1:上指針
2:數組升維
3:利用hash表
前兩者被我否定了因為我覺得還沒那么復雜,于是利用hash表思想解決這個問題,因為js里面有一個天然的hash表結構就是對象。我們知道hash表的查詢是O(1)的,所以我將代碼改寫如下
const ids = {}; selectedIds.forEach(id => ids[id] = 1); data.filter(({id}) => { return !!ids[id]; })
將從selectedIds查詢變成從ids查詢,這樣時間復雜度就從O(n^2)變成了O(n)了,這段代碼增加了
const ids = {}; selectedIds.forEach(id => ids[id] = 1);
其實增加了一個selectedIds遍歷也是一個O(n)的復雜度,總來說復雜度是O(2n),但是從時間復雜度長期期望來看還是一個O(n)的時間復雜度,只不過額外增加了一個對象,所以這也是一個典型的空間換時間的例子,但是也不要擔心,ids用完之后垃圾回收機制會把他回收的。
“從前端性能優化引申出來的經典面試題有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。