【直接收藏】前端JavaScript面試100問(wèn)(終)
87、事件代理
事件代理 也就是 事件委托
不是直接給標(biāo)簽添加事件 是給標(biāo)簽的父級(jí)添加事件 通過(guò) 事件對(duì)象 判斷觸發(fā)事件的標(biāo)簽對(duì)象是誰(shuí) 執(zhí)行不同的函數(shù)程序的語(yǔ)法形式
委托的優(yōu)點(diǎn)
減少內(nèi)存消耗
試想一下,若果我們有一個(gè)列表,列表之中有大量的列表項(xiàng),我們需要在點(diǎn)擊列表項(xiàng)的時(shí)候響應(yīng)一個(gè)事件
如果給每個(gè)列表項(xiàng)一一都綁定一個(gè)函數(shù),那對(duì)于內(nèi)存消耗是非常大的,效率上需要消耗很多性能;
因此,比較好的方法就是把這個(gè)點(diǎn)擊事件綁定到他的父層,也就是 ul 上,然后在執(zhí)行事件的時(shí)候再去匹配判斷目標(biāo)元素;
所以事件委托可以減少大量的內(nèi)存消耗,節(jié)約效率。
動(dòng)態(tài)綁定事件
比如上述的例子中列表項(xiàng)就幾個(gè),我們給每個(gè)列表項(xiàng)都綁定了事件;
在很多時(shí)候,我們需要通過(guò) AJAX 或者用戶操作動(dòng)態(tài)的增加或者去除列表項(xiàng)元素,那么在每一次改變的時(shí)候都需要重新給新增的元素綁定事件,給即將刪去的元素解綁事件;
如果用了事件委托就沒(méi)有這種麻煩了,因?yàn)槭录墙壎ㄔ诟笇拥?,和目?biāo)元素的增減是沒(méi)有關(guān)系的,執(zhí)行到目標(biāo)元素是在真正響應(yīng)執(zhí)行事件函數(shù)的過(guò)程中去匹配的;
所以使用事件在動(dòng)態(tài)綁定事件的情況下是可以減少很多重復(fù)工作的。
88、不卡頓
如何在不卡住頁(yè)面的情況下渲染數(shù)據(jù),也就是說(shuō)不能一次性將幾萬(wàn)條 都渲染出來(lái),而應(yīng)該一次渲染部分 DOM,那么就可以通過(guò) requestAnimationFrame 來(lái) 每 16 ms 刷新一次。
89、JavaScript中的instanceof
90、forEach中的await
不知道你是否寫(xiě)過(guò)類似的代碼:
我當(dāng)時(shí)期望的打印順序是
其實(shí)原因很簡(jiǎn)單,那就是 forEach 只支持同步代碼。
我們可以參考下 Polyfill 版本的 forEach,簡(jiǎn)化以后類似就是這樣的偽代碼
從上述代碼中我們可以發(fā)現(xiàn),forEach 只是簡(jiǎn)單的執(zhí)行了下回調(diào)函數(shù)而已,并不會(huì)去處理異步的情況。并且你在 callback 中即使使用 break 也并不能結(jié)束遍歷。
怎么解決?
一般來(lái)說(shuō)解決的辦法有2種,for...of和for循環(huán)。
使用 Promise.all 的方式行不行,答案是:不行
可以看到并沒(méi)有按照我們期望的輸出。
這樣可以生效的原因是 async 函數(shù)肯定會(huì)返回一個(gè) Promise 對(duì)象,調(diào)用 map 以后返回值就是一個(gè)存放了 Promise 的數(shù)組了,這樣我們把數(shù)組傳入 Promise.all 中就可以解決問(wèn)題了。但是這種方式其實(shí)并不能達(dá)成我們要的效果,如果你希望內(nèi)部的 fetch 是順序完成的,可以選擇第二種方式。
第1種方法是使用 for...of
這種方式相比 Promise.all 要簡(jiǎn)潔的多,并且也可以實(shí)現(xiàn)開(kāi)頭我想要的輸出順序。
但是這時(shí)候你是否又多了一個(gè)疑問(wèn)?為啥 for...of 內(nèi)部就能讓 await 生效呢。
因?yàn)?for...of 內(nèi)部處理的機(jī)制和 forEach 不同,forEach 是直接調(diào)用回調(diào)函數(shù),for...of 是通過(guò)迭代器的方式去遍歷。
第2種方法是使用 for循環(huán)
第3種方法是使用 while循環(huán)
要想在循環(huán)中使用async await,請(qǐng)使用for...of 或者 for 循環(huán), while循環(huán)
forEach支持async awaitforEach 在正常情況像下面這么寫(xiě)肯定是做不到同步的,程序不會(huì)等一個(gè)循環(huán)中的異步完成再進(jìn)行下一個(gè)循環(huán)。原因很明顯,在上面的模擬中,while 循環(huán)只是簡(jiǎn)單執(zhí)行了 callback,所以盡管 callback 內(nèi)使用了 await ,也只是影響到 callback 內(nèi)部。
要支持上面這種寫(xiě)法,只要稍微改一下就好
91、src和href
src和href都是用在外部資源的引入上,比如圖像,CSS文件,HTML文件,以及其他的web頁(yè)面等等,那么src和href的區(qū)別都有哪些呢?
1、請(qǐng)求資源類型不同
(1) href是Hypertext Reference的縮寫(xiě),表示超文本引用。用來(lái)建立當(dāng)前元素和文檔之間的鏈接。常用的有:link、a。
(2)在請(qǐng)求 src 資源時(shí)會(huì)將其指向的資源下載并應(yīng)用到文檔中,常用的有script,img 、iframe;
2、作用結(jié)果不同
(1)href 用于在當(dāng)前文檔和引用資源之間確立聯(lián)系;
(2)src 用于替換當(dāng)前內(nèi)容;
3、 瀏覽器解析方式不同
(1)若在文檔中添加href ,瀏覽器會(huì)識(shí)別該文檔為 CSS 文件,就會(huì)并行下載資源并且不會(huì)停止對(duì)當(dāng)前文檔的處理。這也是為什么建議使用 link 方式加載 CSS,而不是使用 @import 方式。
(2)當(dāng)瀏覽器解析到src ,會(huì)暫停其他資源的下載和處理,直到將該資源加載、編譯、執(zhí)行完畢,圖片和框架等也如此,類似于將所指向資源應(yīng)用到當(dāng)前內(nèi)容。這也是為什么建議把 js 腳本放在底部而不是頭部的原因。
92、JavaScript中事件綁定的方法
在JavaScript的學(xué)習(xí)中,我們經(jīng)常會(huì)遇到JavaScript的事件機(jī)制,例如,事件綁定、事件監(jiān)聽(tīng)、事件委托(事件代理)等。這些名詞是什么意思呢,有什么作用呢?
一、事件綁定要想讓 JavaScript 對(duì)用戶的操作作出響應(yīng),首先要對(duì) DOM 元素綁定事件處理函數(shù)。所謂事件處理函數(shù),就是處理用戶操作的函數(shù),不同的操作對(duì)應(yīng)不同的名稱。
在JavaScript中,有三種常用的綁定事件的方法:
在DOM元素中直接綁定;在JavaScript代碼中綁定;綁定事件監(jiān)聽(tīng)函數(shù)。
1、在DOM中直接綁定事件
2、在JavaScript代碼中綁定事件
3、使用事件監(jiān)聽(tīng)綁定事件
綁定事件的另一種方法是用 addEventListener() 或 attachEvent() 來(lái)綁定事件監(jiān)聽(tīng)函數(shù)。下面詳細(xì)介紹,事件監(jiān)聽(tīng)。
1)事件監(jiān)聽(tīng)
2)事件監(jiān)聽(tīng)的優(yōu)點(diǎn)
1、可以綁定多個(gè)事件;常規(guī)的事件綁定只執(zhí)行最后綁定的事件。
2、可以解除相應(yīng)的綁定
3)封裝事件監(jiān)聽(tīng)
93、git常見(jiàn)分支
git 分支命名規(guī)范
為規(guī)范開(kāi)發(fā),保持代碼提交記錄以及 git 分支結(jié)構(gòu)清晰,方便后續(xù)維護(hù),現(xiàn)規(guī)范 git 的相關(guān)操作。
主要規(guī)范兩點(diǎn):
git 分支命名規(guī)范
git 提交記錄規(guī)范
1. git 分支命名規(guī)范
? git 分支分為集成分支、功能分支和修復(fù)分支,分別命名為 develop、feature 和 hotfix,均為單數(shù)。不可使用 features、future、hotfixes、hotfixs 等錯(cuò)誤名稱。
master(主分支,永遠(yuǎn)是可用的穩(wěn)定版本,不能直接在該分支上開(kāi)發(fā))
develop(開(kāi)發(fā)主分支,所有新功能以這個(gè)分支來(lái)創(chuàng)建自己的開(kāi)發(fā)分支,該分支只做只合并操作,不能直接在該分支上開(kāi)發(fā))
feature-xxx(功能開(kāi)發(fā)分支,在develop上創(chuàng)建分支,以自己開(kāi)發(fā)功能模塊命名,功能測(cè)試正常后合并到develop分支)
feature-xxx-fix(功能bug修復(fù)分支,feature分支合并之后發(fā)現(xiàn)bug,在develop上創(chuàng)建分支修復(fù),之后合并回develop分支。PS:feature分支在申請(qǐng)合并之后,未合并之前還是可以提交代碼的,所以feature在合并之前還可以在原分支上繼續(xù)修復(fù)bug)
hotfix-xxx(緊急bug修改分支,在master分支上創(chuàng)建,修復(fù)完成后合并到 master)
注意事項(xiàng):
一個(gè)分支盡量開(kāi)發(fā)一個(gè)功能模塊,不要多個(gè)功能模塊在一個(gè)分支上開(kāi)發(fā)。
feature 分支在申請(qǐng)合并之前,最好是先 pull 一下 develop 主分支下來(lái),看一下有沒(méi)有沖突,如果有就先解決沖突后再申請(qǐng)合并。
94、前端引擎模板
JavaScript隨著各種神奇的實(shí)用功能庫(kù)日漸豐富,而越來(lái)越受到Web開(kāi)發(fā)者與設(shè)計(jì)師的追捧,例如jQuery,MooTools,Prototype等。
1) Jade
Jade是一個(gè)有著完善API和驚艷特性的JavaScript模板引擎。使用空白與縮進(jìn)敏感的代碼格式編寫(xiě)HTML頁(yè)面?;贜ode.js,運(yùn)行在服務(wù)器端。
2) Mustache
Mustache是一個(gè)logic-less(無(wú)邏輯或輕邏輯)語(yǔ)法模板。可以用于組織HTML、配置文件、源代碼在內(nèi)的任何東西。Mustache使用JavaScript對(duì)象的值,用來(lái)擴(kuò)展模板代碼中的大括號(hào)標(biāo)簽。
3) Transparency
Transparency是一個(gè)強(qiáng)大的客戶端模板引擎,用來(lái)將數(shù)據(jù)綁定到Web頁(yè)面的BOM結(jié)構(gòu)中。其模板無(wú)需特殊格式,直接完全符合HTML。直接使用JavaScript邏輯,無(wú)需新學(xué)特殊的“模板語(yǔ)言”。兼容IE9+、Chrome、Fx、iOS、安卓等瀏覽器。
4) Underscore.js
Underscore.js是一個(gè)JavaScript庫(kù),提供一系列實(shí)用的工具函數(shù)(helper)。Underscore.js僅作為額外的工具函數(shù)獨(dú)立工作,不擴(kuò)充(污染)任何JavaScript內(nèi)建對(duì)象的本身。
5) Embeddedjs
EJS以類似PHP的JS/HTML通過(guò)標(biāo)簽混排的形式,幫助開(kāi)發(fā)者將JavaScript和HTML部分有效分離。
6) DoTjs
最快和簡(jiǎn)潔的JavaScript模板引擎,同時(shí)用于Node.js和瀏覽器。
7) Handlebarsjs
一套語(yǔ)義化模板引擎。兼容Mustache。
8) T.js
一個(gè)用簡(jiǎn)單的JavaScript數(shù)據(jù)結(jié)構(gòu)去渲染表現(xiàn)html/xml內(nèi)容的模板引擎。
9) Dustjs
一套同時(shí)可用于瀏覽器或Node.js的異步模板引擎。
10) Nunjucks
Nunjucks是一套富功能的模板引擎。模板語(yǔ)言功能強(qiáng)大,支持塊繼承、自動(dòng)轉(zhuǎn)義、宏、異步控制等功能。
怎么樣的模板引擎是適合前端的
前端模板引擎需要有開(kāi)發(fā)時(shí)的透明性我認(rèn)為前端任何框架和工具都要有對(duì)開(kāi)發(fā)的透明性,模板引擎也不例外。所謂透明性即指我在搭建好開(kāi)發(fā)環(huán)境后,隨手寫(xiě)代碼隨手刷新瀏覽器就能看到最新的效果,而不需要額外地執(zhí)行任何命令或有任何的等待過(guò)程所以一切依賴編譯過(guò)程的模板引擎并不適合前端使用,編譯只能是模板引擎的一個(gè)特性,而不能是使用的前提更嚴(yán)格地說(shuō),使用FileWatch等手段進(jìn)行文件變更檢測(cè)并自動(dòng)編譯也不在我的考慮范圍之內(nèi),因?yàn)檫@會(huì)造成額外的等待,像我這種手速極快的人可能編譯速度跟不上由此可以推出,前端的模板引擎應(yīng)該是具備可在純前端環(huán)境中解析使用的能力的
前端模板引擎要有良好的運(yùn)行時(shí)調(diào)試能力
前端并不像后端,任何錯(cuò)誤都可以有嚴(yán)格的日志記錄和調(diào)用堆棧以供分析。由于用戶行為的不確定性、執(zhí)行環(huán)境的不確定性、各種第三方腳本的影響等,前端很難做到完全的錯(cuò)誤處理和跟蹤,這也導(dǎo)致前端必然存在需要直接在線上排查問(wèn)題的情況而當(dāng)問(wèn)題出現(xiàn)在模板引擎這一層時(shí),就需要模板引擎提供良好的調(diào)試能力一般來(lái)說(shuō),編譯后生成的函數(shù)的調(diào)試能力是弱于原先手動(dòng)編寫(xiě)的模板片斷的,因?yàn)樽詣?dòng)生成的函數(shù)基本不具備可讀性和可斷點(diǎn)跟蹤性因此在這一點(diǎn)上,一個(gè)供前端使用的模板引擎應(yīng)該具備在特定情況下從“執(zhí)行編譯后函數(shù)獲取HTML”換回“解析原模板再執(zhí)行函數(shù)獲取HTML”的模式,即應(yīng)該支持在兩種模式間切換或者更好地,一個(gè)強(qiáng)大的前端模板引擎編譯生成的函數(shù),可以使用Source Map或其它自定義的手段直接映射回原模板片段,不過(guò)現(xiàn)在并沒(méi)有什么模板引擎實(shí)現(xiàn)了這一功能
前端模板引擎要對(duì)文件合并友好
在HTTP/2普及之前,文件合并依舊是前端性能優(yōu)化中的一個(gè)重要手段,模板作為文件的一部分,依舊是需要合并的
在提供編譯功能的模板引擎中,我們可以使用編譯的手段將模板變?yōu)镴avaScript源碼,再在JavaScript的基礎(chǔ)上做文件合并
但是如果我們出于上文所說(shuō)的調(diào)試能力等原因希望保留原模板片段,那就需要模板引擎本身支持模板片段合并為一個(gè)文件了
大部分僅支持將一段輸入的字符串作為模板解析的引擎并不具備這一能力,他們天生并不能將一整個(gè)字符串切分為多個(gè)模板片段,因而無(wú)法支持模板片段層面上的文件合并
需要實(shí)現(xiàn)對(duì)文件合并的支持,最好的辦法就是讓模板的語(yǔ)法是基于“片段”的
前端模板引擎要擔(dān)負(fù)XSS的防范
從安全性上來(lái)說(shuō),前端對(duì)XSS的控制是有嚴(yán)格要求的
我在 單頁(yè)面(SPA)開(kāi)發(fā)會(huì)不會(huì)比多頁(yè)面有更多的安全問(wèn)題?- 張立理的回答 中有提到過(guò),前端對(duì)XSS的防范比較合適的方法是使用“默認(rèn)轉(zhuǎn)義”的白名單式策略
基于此,一個(gè)合理的模板引擎是必須支持默認(rèn)轉(zhuǎn)義的,即所有數(shù)據(jù)的輸出都默認(rèn)經(jīng)過(guò)escape的邏輯處理,將關(guān)鍵符號(hào)轉(zhuǎn)為對(duì)應(yīng)的HTML實(shí)體符號(hào),以從根源上杜絕XSS的入侵路徑
當(dāng)然并不是所有的內(nèi)容都必須經(jīng)過(guò)轉(zhuǎn)義的,在系統(tǒng)中免不了有對(duì)用戶輸入富文本的需求,因此需要支持特定的語(yǔ)法來(lái)產(chǎn)生無(wú)轉(zhuǎn)義的輸出,但時(shí)刻注意無(wú)轉(zhuǎn)義輸出才是特例,默認(rèn)情況下必須是轉(zhuǎn)義輸出的
前端模板引擎要支持片段的復(fù)用
這并不是前端模板引擎的需求,事實(shí)上任何模板引擎都應(yīng)該支持片段的復(fù)用,后端如Velocity、Smarty等無(wú)不擁有此功能
所謂片段復(fù)用,應(yīng)該有以下幾個(gè)層次的應(yīng)用:
一個(gè)片段可以被引入到另一處,相當(dāng)于一個(gè)變量到處用的效果
一個(gè)片段被引入時(shí),可以向其傳遞不同的數(shù)據(jù),相當(dāng)于一個(gè)函數(shù)到處用的效果
一個(gè)片段可以被外部替換,但外部不提供此片段的話保持一個(gè)默認(rèn)的內(nèi)容,類似設(shè)計(jì)模式中的策略模式
滿足第1和第2點(diǎn)的模板引擎并不少,而滿足第3點(diǎn)的前端模板引擎卻不多見(jiàn),而后端的Razor、Smarty等都具備這一功能
話說(shuō)我當(dāng)時(shí)設(shè)計(jì)我們自己的模板引擎的第3個(gè)版本時(shí),就想出了block這一個(gè)概念來(lái)實(shí)現(xiàn)第3點(diǎn),在做完交付將近半年之后,有人告訴我說(shuō)Smarty上就有這概念,頓時(shí)有種不知應(yīng)該高興還是悲傷的不知所措感。還好他并沒(méi)有懷疑我直接抄了別人的功能,不然真是冤枉
前端模板引擎要支持?jǐn)?shù)據(jù)輸出時(shí)的處理
所謂數(shù)據(jù)輸出時(shí)處理,指一個(gè)數(shù)據(jù)要在輸出時(shí)做額外的轉(zhuǎn)換,最常見(jiàn)的如字符串的trim操作,比較技術(shù)性的如markdown的轉(zhuǎn)換等
誠(chéng)然數(shù)據(jù)的轉(zhuǎn)換完全可以在將數(shù)據(jù)交給模板引擎前就通過(guò)JavaScript的邏輯處理完,但這會(huì)導(dǎo)致不少有些丑陋又有些冗余的代碼,對(duì)邏輯本身的復(fù)用性也會(huì)造成負(fù)面的影響
通常模板引擎對(duì)數(shù)據(jù)做額外處理會(huì)使用filter的形式實(shí)現(xiàn),類似bash中的管道的邏輯。filter的實(shí)現(xiàn)和注冊(cè)也會(huì)有不同的設(shè)計(jì),如mustache其實(shí)注冊(cè)的是fitler工廠,而另一些模板引擎則會(huì)直接注冊(cè)filter本身,不同設(shè)計(jì)有不同的考量點(diǎn),我們很難說(shuō)誰(shuí)好誰(shuí)壞
但是,模板引擎支持?jǐn)?shù)據(jù)的輸出處理后,會(huì)另我們?cè)诰幋a過(guò)程中產(chǎn)生一個(gè)新的糾結(jié),即哪些數(shù)據(jù)處理應(yīng)該交由模板引擎的filter實(shí)現(xiàn),哪些應(yīng)該在交給模板引擎前由自己的邏輯邏輯實(shí)現(xiàn)。這個(gè)話題展開(kāi)來(lái)又是一篇長(zhǎng)長(zhǎng)的論述,于當(dāng)前的話題無(wú)關(guān)就略過(guò)吧
前端模板引擎要支持動(dòng)態(tài)數(shù)據(jù)
在開(kāi)發(fā)過(guò)程中,其實(shí)有不少數(shù)據(jù)并不是靜態(tài)的,如EmberJS就提供了Computed Property這樣的概念,Angular也有類似的東西,Backbone則可以通過(guò)重寫(xiě)Model的get方法來(lái)變相實(shí)現(xiàn)
雖然ES5在語(yǔ)言層面上直接提供了getter的支持,但我們?cè)谇岸碎_(kāi)發(fā)的大部分場(chǎng)景下依舊不會(huì)使用這一語(yǔ)言特性,而會(huì)選擇將動(dòng)態(tài)的數(shù)據(jù)封裝為某種對(duì)象的get等方法而模板引擎在將數(shù)據(jù)轉(zhuǎn)為HTML片段的過(guò)程中,同樣應(yīng)該關(guān)注這一點(diǎn),對(duì)這些動(dòng)態(tài)計(jì)算的數(shù)據(jù)有良好的支持
說(shuō)得更明白一些,模板引擎不應(yīng)該僅僅接受純對(duì)象(Plain Object)作為輸入,而應(yīng)該更開(kāi)放地接受類似帶有g(shù)et方法的動(dòng)態(tài)的數(shù)據(jù)
一個(gè)比較合理的邏輯是,如果一個(gè)對(duì)象有一個(gè)get方法(模板引擎決定這個(gè)接口),則數(shù)據(jù)通過(guò)該方法獲取,其它情況下視輸入的對(duì)象為純對(duì)象(Plain Object),使用標(biāo)準(zhǔn)的屬性獲取邏輯
前端模板引擎要與異步流程嚴(yán)密結(jié)合
前端有一個(gè)很大的特點(diǎn),就是到處充斥著異步的流程。由于JavaScript在瀏覽器提供的引擎中單線程執(zhí)行的特性、大部分與IO相關(guān)的API都暴露為異步的事實(shí),以及多數(shù)模塊定義規(guī)范中模板的動(dòng)態(tài)獲取是異步的這一現(xiàn)象,注定我們無(wú)法將這個(gè)世界當(dāng)作完全同步來(lái)看
一個(gè)很常見(jiàn)的例子是,我們有一個(gè)AMD模塊存放了全局使用的常量,模板引擎需要使用這些常量。當(dāng)然我們可以在使用模板引擎之前讓JavaScript去異步獲取這一模塊,隨后將常量作為數(shù)據(jù)傳遞給模板引擎,但這是一種業(yè)務(wù)與視圖相對(duì)耦合的玩法,出于強(qiáng)迫癥我并不覺(jué)得這是一個(gè)漂亮的設(shè)計(jì),所以我們希望直接在模板中這么寫(xiě):
這是我假想的一個(gè)語(yǔ)法,通過(guò)$globals可以使用AMD Loader獲取globals這一模塊,隨后獲取其中的ICP_SERIAL屬性輸出
模板引擎支持異步是一個(gè)比較具有挑戰(zhàn)性的話題,我的計(jì)劃是在我們自己的模板引擎的下一個(gè)版本中嘗試實(shí)現(xiàn)。這其中涉及很多的技術(shù)點(diǎn),比如:
模板的輸出本身成了異步的方法,而不再像現(xiàn)在一樣直接返回字符串
分析模板對(duì)異步操作的依賴,整個(gè)字符串的拼接邏輯被打斷成多個(gè)異步
異步是需要等待的,且等待是未知的,從性能上考慮,是否需要考慮Stream式的輸出,以便完成一段提供一段
是提供內(nèi)置的固定幾種異步邏輯,還是基于Promise支持任何自定義的異步邏輯,在復(fù)雜度和實(shí)用性上作出平衡
至今我還沒(méi)有完全明確模板與異步結(jié)合的方式和接口,這個(gè)話題也沒(méi)辦法繼續(xù)深入探討了
前端模板引擎要支持不同的開(kāi)發(fā)模式
前端發(fā)展至今,有很多不同的開(kāi)發(fā)模式,比如:
最普通的HTML頁(yè)面,使用DOMContentLoaded等事件添加邏輯,特定交互下局部刷新頁(yè)面
采用傳統(tǒng)的MVC模型進(jìn)行單頁(yè)式開(kāi)發(fā)
使用MVVM方式以數(shù)據(jù)為核心,數(shù)據(jù)與視圖方向綁定進(jìn)行開(kāi)發(fā)
基于Immutable Data進(jìn)行數(shù)據(jù)比對(duì)Diff轉(zhuǎn)DOM更新的開(kāi)發(fā)(其中可能有Virtual DOM的引入)
一個(gè)模板引擎要能支持這么多種不同的的模式是一個(gè)非常大的挑戰(zhàn),特別是對(duì)雙向綁定的支持尤為突出。至今為止幾乎所有的支持雙向綁定的開(kāi)發(fā)框架都自帶了專用的模板引擎,這是因?yàn)殡p向綁定對(duì)模板有兩大要求:
能夠從模板中提取“這一模板對(duì)哪些數(shù)據(jù)有依賴”的元信息
能夠知道一個(gè)數(shù)據(jù)變化引擎的是模板的哪一塊,而不至于整個(gè)刷新
而通用模板引擎很少提供這兩個(gè)特性,所以沒(méi)辦法對(duì)不同的前端開(kāi)發(fā)模式進(jìn)行全面到位的支持
從模板引擎本身的實(shí)現(xiàn)上來(lái)說(shuō),一種方法是直接將模板解析后的類似AST的結(jié)構(gòu)暴露出去,供其他框架合理地處理,同時(shí)提供對(duì)模板局部的刷新功能(也可與前面所說(shuō)的模板片段一起考慮),但是大部分模板引擎為了性能等考慮,是不會(huì)解析出類似AST的語(yǔ)法結(jié)構(gòu)來(lái)的
前端模板引擎要有實(shí)例間的隔離
在大型的前端項(xiàng)目,特別是單頁(yè)式的項(xiàng)目中,會(huì)有完全未知個(gè)數(shù)的模板片段同時(shí)存在,如果這些片段是帶有名稱(出于復(fù)用的考慮)的,就很容易造成名稱上的沖突對(duì)于同一層級(jí)的邏輯(如大家都是業(yè)務(wù)層代碼,或者大家都是控件層代碼),名稱沖突是可以通過(guò)一些開(kāi)發(fā)時(shí)的約定來(lái)解決的。但不同層之間,由于封裝性的要求,外部不應(yīng)該知道一些僅內(nèi)部使用的片段的名稱,此時(shí)如果不幸有名稱與其它層有沖突,會(huì)讓情況變得比較麻煩,這類問(wèn)題甚至都不容易跟蹤,往往會(huì)導(dǎo)致大量的精力和時(shí)間的浪費(fèi)
因此,一個(gè)好的模板引擎應(yīng)該是多實(shí)例的,且不同實(shí)例間應(yīng)該相互具備隔離性,不會(huì)出現(xiàn)這種不可預(yù)期的沖突
將這個(gè)話題再往深地研究,就會(huì)發(fā)現(xiàn)單純的隔離是不夠的,不同層間除了不沖突的需求,同樣還有片段復(fù)用的需求,我們還會(huì)需要不同模板實(shí)例間可以開(kāi)放一些固定的片段共享,因此模板引擎各個(gè)實(shí)例的關(guān)系是一種組合依賴但又具備基本的封裝和隔離的狀態(tài)
95、datalist 用法
這里注意綁定datalist的id給input的list屬性,這樣在input輸入框下面就會(huì)出現(xiàn)列表
96、ajax同步和異步的區(qū)別
在使用ajax請(qǐng)求數(shù)據(jù)的時(shí)候,通常情況下我們都是把a(bǔ)sync:true當(dāng)做默認(rèn)來(lái)處理,讓我們的請(qǐng)求成為一個(gè)異步的請(qǐng)求。但是在某種情況下我們是需要吧async:false設(shè)置為false的,方便我們進(jìn)行觀察數(shù)據(jù)的走向、去處。那同步和異步有什么區(qū)別呢?
同步請(qǐng)求 async:false
分析這個(gè)時(shí)候ajax塊發(fā)出請(qǐng)求后,他會(huì)等待在function1()這個(gè)地方,不會(huì)去執(zhí)行function2(),直到function1()部分執(zhí)行完畢。異步請(qǐng)求 async:true
分析當(dāng)ajax塊發(fā)出請(qǐng)求后,他將停留function1(),等待返回結(jié)果,但同時(shí)(在這個(gè)等待過(guò)程中),function2()就可以跑起來(lái)??偨Y(jié)(兩者的區(qū)別)同步的請(qǐng)求的時(shí)候,代碼好比在排隊(duì),必須是一個(gè)挨著一個(gè)的去執(zhí)行,前面的沒(méi)有結(jié)束,后面的代碼就處于一個(gè)阻塞的狀態(tài)。異步執(zhí)行的時(shí)候,數(shù)據(jù)請(qǐng)求的同時(shí),其他代碼語(yǔ)句也可以同步執(zhí)行,比如,在數(shù)據(jù)請(qǐng)求的時(shí)候,由于某些愿意,需要慢慢的返回請(qǐng)求結(jié)果,在這個(gè)時(shí)候帶寬是很空閑的,那么,代碼不會(huì)等到前面的數(shù)據(jù)完全請(qǐng)求返回就可以開(kāi)始后面的代碼運(yùn)行。
97、JavaScript偽數(shù)組
數(shù)組
定義: 數(shù)組是一種類列表對(duì)象,它的原型中提供了遍歷和修改元素的相關(guān)操作。JavaScript 數(shù)組的長(zhǎng)度和元素類型都是非固定的。只能用整數(shù)作為數(shù)組元素的索引,而不能用字符串。對(duì)象是沒(méi)有索引的,是數(shù)組的基本特征。
obj[2]輸出’a’,是因?yàn)閷?duì)象就是普通的鍵值對(duì)存取數(shù)據(jù)而arr[2]輸出’a’ 則不同,數(shù)組是通過(guò)索引來(lái)存取數(shù)據(jù),arr[2]之所以輸出’a’,是因?yàn)閿?shù)組arr索引2的位置已經(jīng)存儲(chǔ)了數(shù)據(jù)obj.length并不具有數(shù)組的特性,并且obj沒(méi)有保存屬性length,那么自然就會(huì)輸出undefined而對(duì)于數(shù)組來(lái)說(shuō),length是數(shù)組的一個(gè)內(nèi)置屬性,數(shù)組會(huì)根據(jù)索引長(zhǎng)度來(lái)更改length的值為什么arr.length輸出3,而不是1在給數(shù)組添加元素時(shí),并沒(méi)有按照連續(xù)的索引添加,所以導(dǎo)致數(shù)組的索引不連續(xù),那么就導(dǎo)致索引長(zhǎng)度大于元素個(gè)數(shù)
偽數(shù)組
定義:
偽數(shù)組是一個(gè)對(duì)象(Object),而真實(shí)的數(shù)組是一個(gè)數(shù)組(Array)擁有l(wèi)ength屬性,且必須是number類型,其它屬性(索引)為字符串不具有數(shù)組所具有的方法,forEach()等,不過(guò)有Object的方法偽數(shù)組長(zhǎng)度不可變,真數(shù)組長(zhǎng)度可以變可以通過(guò)for in遍歷
常見(jiàn)的偽數(shù)組有:
函數(shù)內(nèi)部的 argumentsDOM 對(duì)象列表(比如通過(guò) document.getElementsByTags 得到的列表)jQuery 對(duì)象(比如 $(“div”) )偽數(shù)組是一個(gè) Object,而真實(shí)的數(shù)組是一個(gè) Array。偽數(shù)組存在的意義,是可以讓普通的對(duì)象也能正常使用數(shù)組的很多方法,比如:
結(jié)論對(duì)象沒(méi)有數(shù)組 Array.prototype 的屬性值,類型是 Object ,而數(shù)組類型是 Array數(shù)組是基于索引的實(shí)現(xiàn), length 會(huì)自動(dòng)更新,而對(duì)象是鍵值對(duì)使用對(duì)象可以創(chuàng)建偽數(shù)組,偽數(shù)組可以正常使用數(shù)組的大部分方法
98、同源策略
99、獲取第二大的數(shù)字
方法一將數(shù)組從大到小排序然后找第二個(gè)當(dāng)然在JS中有sort()方法可以進(jìn)行數(shù)組排序
方法二
定義兩個(gè)變量max min循環(huán)遍歷分別存儲(chǔ)當(dāng)前最大和第二大的數(shù)然后輸出第二大的數(shù)min;
100、forin和Object.keys的區(qū)別
使用for in 去遍歷 對(duì)象會(huì)將prototype上面擴(kuò)展的方法或者屬性也打印出來(lái)
解決方法可以為每一次的遍歷加上hasOwnPropertyhasOwnProperty具體的作用就是判斷該屬性是否屬于對(duì)象自身的屬性
// 遞歸寫(xiě)法