Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
源:魚頭的Web海洋
之前刷某乎的時(shí)候,看到這么一個(gè)問題:“如何衡量一個(gè)人的 JavaScript 水平?[2]”然后自己也不要臉地回答了一下這個(gè)問題。以下是我的答案:
原文如下:
A:看一個(gè)人寫代碼是否有規(guī)范,代碼是否壯健,是否可拓展,可讀性高不高,API設(shè)計(jì)是否合理。
這些都是長(zhǎng)年累月積累下來的且獨(dú)立于編程語(yǔ)言以外的。
遠(yuǎn)比把什么手寫bind,原型鏈,閉包給背下來更有價(jià)值。
這才是證明你代碼水平的關(guān)鍵點(diǎn)。
Q:在面試的時(shí)候如何快速判斷出呢?
A:讓面試者設(shè)計(jì)個(gè)組件,不用寫,回答就行。從API設(shè)計(jì),文檔編寫,項(xiàng)目結(jié)構(gòu),單元測(cè)試,編寫模式,性能優(yōu)化等方面來回答。
有工作經(jīng)驗(yàn)的人,基本業(yè)務(wù)邏輯都能寫,但是寫的好不好,就是經(jīng)驗(yàn)跟能力以及學(xué)習(xí)力的體現(xiàn) 。
首先來個(gè)免責(zé)聲明,以上的回答都是個(gè)人的經(jīng)驗(yàn)與見解,答案肯定不唯一,甚至不一定全對(duì),所以求輕噴。
上面問如何在面試的時(shí)候快速判斷對(duì)方是否是高級(jí)前端的時(shí)候,我為什么說是“設(shè)計(jì)組件”呢?
因?yàn)槲矣X得有一定實(shí)力的前端來說,“組件”這個(gè)概念是繞不過的,或者看過開源組件的源碼,或者自己寫過組件。
對(duì)于一般的業(yè)務(wù)問題,我相信作為一個(gè)從業(yè)了一定時(shí)間的開發(fā)者,無論水平如何,這都不是問題,但是如何區(qū)分這個(gè)開發(fā)者的水平,可以通過他寫的代碼來判斷,當(dāng)然也不完全是,畢竟在996或者趕進(jìn)度的時(shí)候,很容易就會(huì)為了完成快速出產(chǎn)品而寫,這種情況下代碼質(zhì)量跟個(gè)人水平不一定能體現(xiàn)。
下面,我們以設(shè)計(jì)一個(gè)“按鈕(<Button>)組件”為例,來探索這個(gè)問題。
首先“按鈕(<Button>)”的作用這個(gè)我們是否明確?它是裝飾性的組件還是功能性的組件?
這個(gè)問題很簡(jiǎn)單,“按鈕(<Button>)”是一個(gè)功能性的組件,是讓用戶通過點(diǎn)擊或觸碰來采取行動(dòng)并做出選擇的一個(gè)組件。
那么“按鈕(<Button>)”通常放在什么地方?有經(jīng)驗(yàn)的開發(fā)可能會(huì)想到以下場(chǎng)景:
?對(duì)話框?模態(tài)窗口?表單?卡片?工具欄
代表狀態(tài)可能會(huì)有以下幾種:
?默認(rèn)狀態(tài)?初始狀態(tài)?信息狀態(tài)?警告狀態(tài)?危險(xiǎn)狀態(tài)
形態(tài)可能會(huì)有以下幾種:
?實(shí)心按鈕?文本按鈕?描邊按鈕?圖標(biāo)按鈕?圓角按鈕?直角按鈕
尺寸可能會(huì)有以下幾種:
?small?medium?large
操作性可能會(huì)有以下幾種:
?回車鍵點(diǎn)擊?鼠標(biāo)點(diǎn)擊?觸摸點(diǎn)擊?禁止點(diǎn)擊
攜帶的事件可能有以下兩種:
?click事件?回車鍵keydown事件?tap事件
以上雖然是偏樣式,但是作為一個(gè)組件開發(fā)者,這都是我們?nèi)粘K枰紤]的。
在API的設(shè)計(jì)環(huán)節(jié),我們通過上述的場(chǎng)景,我們可能會(huì)暴露出以下的API
?type:按鈕狀態(tài)?size:按鈕尺寸?color:按鈕顏色?text:按鈕內(nèi)的文本?icon:按鈕內(nèi)的圖標(biāo)?htmlType:原生按鈕支持的type屬性?attrs:其他的原生屬性?variant:按鈕形態(tài)?click:鼠標(biāo)點(diǎn)擊事件?tap:觸摸屏點(diǎn)擊事件?keydown:回車鍵按下事件
在我們API設(shè)計(jì)好之后,我們就可以開始開發(fā)了,這時(shí)候根據(jù)我們項(xiàng)目的類型,選擇的開發(fā)工具以及模式,可能會(huì)有所不同。
我們是獨(dú)立編寫還是直接在項(xiàng)目里面去編寫,如果是獨(dú)立編寫,選擇哪個(gè)打包工具,是gulp還是webpack還是其它,為什么這么選?
例如如果我們是用TS寫,我們可能需要編寫B(tài)utton.d.ts,如果是vue的組件,我們還得考慮Vue.use注入到Vue中,也就是Button.install(vue),如果是react,我們還得考慮是否使用React.forwardRef來進(jìn)行ref轉(zhuǎn)發(fā)。
然后就是我們的代碼規(guī)范,是用Function還是Class,共同的代碼塊如何抽象,如何,還有命名規(guī)范是什么,哪些屬性必選,哪些屬性可選,默認(rèn)值是什么?我們是怎么考慮的?
所以最終的組件使用可能會(huì)是這種形式:
import Button from './componenet/Button'<Button htmlType="submit" aria-label="add" variant="contained" color="rgba(17, 82, 147, 1)" click="clickHandler" />添加</Button>
在我們開發(fā)的過程中,有一道麻煩但又必不可少的工序就是單元測(cè)試,這時(shí)候單元測(cè)試的庫(kù)我們是怎么選?用Jest還是Mocha?測(cè)試用例怎么寫?如何模擬點(diǎn)擊或者異步響應(yīng)?是否需要快照(snapshots)?這也是在我們的考慮范圍內(nèi)。
所以我們的測(cè)試腳本可能長(zhǎng)這樣:
import Button from './componenet/Button'import { shallow } from 'enzyme'describe('<Button />', ()=> { it('render success', ()=> { const wrapper=shallow(( <Button htmlType="submit" aria-label="add" variant="contained" color="rgba(17, 82, 147, 1)" click="clickHandler" />添加</Button> )) expect(wrapper.text('添加')).to.equal(true) })})
其它的諸如開發(fā)文檔,使用文檔,版本迭代,項(xiàng)目配置,打包開發(fā)優(yōu)化,以及其他自動(dòng)化的功能,也是我們所需要考慮。
以上便是我們?cè)陂_發(fā)一個(gè)“按鈕(<Button>)組件”時(shí)可能會(huì)考慮到的點(diǎn),可能有不夠完善的地方,但是我想說的意思是,這其實(shí)可以很好的衡量一個(gè)人的JavaScript水平。比如你再會(huì)手寫原型鏈關(guān)系圖,閉包,防抖,節(jié)流等基礎(chǔ)概念,但是如果不在項(xiàng)目中運(yùn)用起來,終究是紙上談兵,對(duì)技術(shù)水平?jīng)]有太多實(shí)質(zhì)的幫助,當(dāng)然不是說精通這些內(nèi)容不好,但是比起實(shí)戰(zhàn),還是差強(qiáng)人意。
能手寫代碼的不一定是高級(jí),但是如果能寫好一個(gè)組件,水平再差也不會(huì)差到哪里去。
本文似乎有點(diǎn)文不對(duì)題了,本來談的是“如何衡量一個(gè)人的JavaScript水平”,結(jié)果卻超綱了許多。但是通過這種方式,確實(shí)能夠判斷出一個(gè)人代碼水平,當(dāng)然也并不只是JS,換成安卓,IOS也同樣適用。
不知道你是通過什么方式來衡量一個(gè)的JavaScript水平的呢?歡迎留言區(qū)域回復(fù)互動(dòng)。
者|顏海鏡
編輯|覃云
出處丨前端之巔
本文已獲作者授權(quán),轉(zhuǎn)載來源:
https://segmentfault.com/a/1190000016389031
劃重點(diǎn),這是一道面試必考題,很多面試官都喜歡問這個(gè)問題,我就被問過好幾次了。
要實(shí)現(xiàn)上圖的效果看似很簡(jiǎn)單,實(shí)則暗藏玄機(jī),本文總結(jié)了一下 CSS 實(shí)現(xiàn)水平垂直居中的方式大概有下面這些,本文將逐一介紹一下,我將本文整理成了一個(gè) github 倉(cāng)庫(kù)在:https://github.com/yanhaijing/vertical-center
歡迎大家 star。
僅居中元素定寬高適用:
居中元素不定寬高:
為了實(shí)現(xiàn)上面的效果先來做些準(zhǔn)備工作,假設(shè) HTML 代碼如下,總共兩個(gè)元素,父元素和子元素:
<div class="wp"> <div class="box size">123123</div> </div>
wp 是父元素的類名,box 是子元素的類名,因?yàn)橛卸▽捄筒欢▽挼膮^(qū)別,size 用來表示指定寬度,下面是所有效果都要用到的公共代碼,主要是設(shè)置顏色和寬高。
注意:后面不在重復(fù)這段公共代碼,只會(huì)給出相應(yīng)提示。
/* 公共代碼 */ .wp { border: 1px solid red; width: 300px; height: 300px; } .box { background: green; } .box.size{ width: 100px; height: 100px; } /* 公共代碼 */
絕對(duì)定位的百分比是相對(duì)于父元素的寬高,通過這個(gè)特性可以讓子元素的居中顯示,但絕對(duì)定位是基于子元素的左上角,期望的效果是子元素的中心居中顯示。
為了修正這個(gè)問題,可以借助外邊距的負(fù)值,負(fù)的外邊距可以讓元素向相反方向定位,通過指定子元素的外邊距為子元素寬度一半的負(fù)值,就可以讓子元素居中了,css 代碼如下。
/* 此處引用上面的公共代碼 */ /* 此處引用上面的公共代碼 */ /* 定位代碼 */ .wp { position: relative; } .box { position: absolute;; top: 50%; left: 50%; margin-left: -50px; margin-top: -50px; }
這是我比較常用的方式,這種方式比較好理解,兼容性也很好,缺點(diǎn)是需要知道子元素的寬高。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute1.html
這種方式也要求居中元素的寬高必須固定,HTML 代碼如下:
<div class="wp"> <div class="box size">123123</div> </div>
這種方式通過設(shè)置各個(gè)方向的距離都是 0,此時(shí)再講 margin 設(shè)為 auto,就可以在各個(gè)方向上居中了。
/* 此處引用上面的公共代碼 */ /* 此處引用上面的公共代碼 */ /* 定位代碼 */ .wp { position: relative; } .box { position: absolute;; top: 0; left: 0; right: 0; bottom: 0; margin: auto; }
這種方法兼容性也很好,缺點(diǎn)是需要知道子元素的寬高。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute2.html
這種方式也要求居中元素的寬高必須固定,所以我們?yōu)?box 增加 size 類,HTML 代碼如下:
<div class="wp"> <div class="box size">123123</div> </div>
感謝 css3 帶來了計(jì)算屬性,既然 top 的百分比是基于元素的左上角,那么在減去寬度的一半就好了,代碼如下
/* 此處引用上面的公共代碼 */ /* 此處引用上面的公共代碼 */ /* 定位代碼 */ .wp { position: relative; } .box { position: absolute;; top: calc(50% - 50px); left: calc(50% - 50px); }
這種方法兼容性依賴 calc 的兼容性,缺點(diǎn)是需要知道子元素的寬高。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute3.html
還是絕對(duì)定位,但這個(gè)方法不需要子元素固定寬高,所以不再需要 size 類了,HTML 代碼如下:
<div class="wp"> <div class="box">123123</div> </div>
修復(fù)絕對(duì)定位的問題,還可以使用 css3 新增的 transform,transform 的 translate 屬性也可以設(shè)置百分比,其是相對(duì)于自身的寬和高,所以可以講 translate 設(shè)置為 -50%,就可以做到居中了,代碼如下:
/* 此處引用上面的公共代碼 */ /* 此處引用上面的公共代碼 */ /* 定位代碼 */ .wp { position: relative; } .box { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
這種方法兼容性依賴 translate2d 的兼容性。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute4.html
利用行內(nèi)元素居中屬性也可以做到水平垂直居中,HTML 代碼如下:
<div class="wp"> <div class="box">123123</div> </div>
把 box 設(shè)置為行內(nèi)元素,通過 text-align 就可以做到水平居中,但很多同學(xué)可能不知道通過通過 vertical-align 也可以在垂直方向做到居中,代碼如下:
/* 此處引用上面的公共代碼 */ /* 此處引用上面的公共代碼 */ /* 定位代碼 */ .wp { line-height: 300px; text-align: center; font-size: 0px; } .box { font-size: 16px; display: inline-block; vertical-align: middle; line-height: initial; text-align: left; /* 修正文字 */ }
這種方法需要在子元素中將文字顯示重置為想要的效果。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/lineheight.html
很多同學(xué)一定和我一樣不知道 writing-mode 屬性,感謝 @張?chǎng)涡窭蠋煹姆答仯?jiǎn)單來說 writing-mode 可以改變文字的顯示方向,比如可以通過 writing-mode 讓文字的顯示變?yōu)榇怪狈较颉?/p>
<div class="div1">水平方向</div> <div class="div2">垂直方向</div> .div2 { writing-mode: vertical-lr; }
顯示效果如下:
水平方向 垂 直 方 向
更神奇的是所有水平方向上的 css 屬性,都會(huì)變?yōu)榇怪狈较蛏系膶傩裕热?text-align,通過 writing-mode 和 text-align 就可以做到水平和垂直方向的居中了,只不過要稍微麻煩一點(diǎn):
<div class="wp"> <div class="wp-inner"> <div class="box">123123</div> </div> </div> /* 此處引用上面的公共代碼 */ /* 此處引用上面的公共代碼 */ /* 定位代碼 */ .wp { writing-mode: vertical-lr; text-align: center; } .wp-inner { writing-mode: horizontal-tb; display: inline-block; text-align: center; width: 100%; } .box { display: inline-block; margin: auto; text-align: left; }
這種方法實(shí)現(xiàn)起來和理解起來都稍微有些復(fù)雜。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/writing-mode.html
曾經(jīng) table 被用來做頁(yè)面布局,現(xiàn)在沒人這么做了,但 table 也能夠?qū)崿F(xiàn)水平垂直居中,但是會(huì)增加很多冗余代碼:
<table> <tbody> <tr> <td class="wp"> <div class="box">123123</div> </td> </tr> </tbody> </table>
tabel 單元格中的內(nèi)容天然就是垂直居中的,只要添加一個(gè)水平居中屬性就好了。
.wp { text-align: center; } .box { display: inline-block; }
這種方法就是代碼太冗余,而且也不是 table 的正確用法。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/table.html
css 新增的 table 屬性,可以讓我們把普通元素,變?yōu)?table 元素的現(xiàn)實(shí)效果,通過這個(gè)特性也可以實(shí)現(xiàn)水平垂直居中。
<div class="wp"> <div class="box">123123</div> </div>
下面通過 css 屬性,可以讓 div 顯示的和 table 一樣:
.wp { display: table-cell; text-align: center; vertical-align: middle; } .box { display: inline-block; }
這種方法和 table 一樣的原理,但卻沒有那么多冗余代碼,兼容性也還不錯(cuò)。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/css-table.html
flex 作為現(xiàn)代的布局方案,顛覆了過去的經(jīng)驗(yàn),只需幾行代碼就可以優(yōu)雅的做到水平垂直居中。
<div class="wp"> <div class="box">123123</div> </div> .wp { display: flex; justify-content: center; align-items: center; }
目前在移動(dòng)端已經(jīng)完全可以使用 flex 了,PC 端需要看自己業(yè)務(wù)的兼容性情況。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/flex.html
感謝 @一絲姐 反饋的這個(gè)方案,css 新出的網(wǎng)格布局,由于兼容性不太好,一直沒太關(guān)注,通過 grid 也可以實(shí)現(xiàn)水平垂直居中。
<div class="wp"> <div class="box">123123</div> </div> .wp { display: grid; } .box { align-self: center; justify-self: center; }
代碼量也很少,但兼容性不如 flex,不推薦使用。
點(diǎn)擊查看完整 DEMO:
http://yanhaijing.com/vertical-center/grid.html
下面對(duì)比下各個(gè)方式的優(yōu)缺點(diǎn),肯定又雙叒叕該有同學(xué)說回字的寫法了,簡(jiǎn)單總結(jié)下:
小貼士:關(guān)于 flex 的兼容性決方案,請(qǐng)看這里:
https://yanhaijing.com/css/2016/08/21/flex-practice-on-mobile/
最近發(fā)現(xiàn)很多同學(xué)都對(duì) css 不夠重視,這其實(shí)是不正確的,比如下面的這么簡(jiǎn)單的問題都有那么多同學(xué)不會(huì),我也是很無語(yǔ):
<div class="red blue">123</div> <div class="blue red">123</div> .red { color: red } .blue { color: blue }
問兩個(gè) div 的顏色分別是什么,竟然只有 40% 的同學(xué)能夠答對(duì),這 40% 中還有很多同學(xué)不知道為什么,希望這些同學(xué)好好補(bǔ)習(xí)下 CSS 基礎(chǔ)。
備JavaScript面試時(shí)應(yīng)了解的事項(xiàng)。
JavaScript現(xiàn)在是一種非常流行的編程語(yǔ)言,基于該語(yǔ)言,派生了大量庫(kù)和框架。 但是,無論高層生態(tài)系統(tǒng)如何發(fā)展,離不開原始的JavaScript。 在這里,我選擇了4個(gè)JavaScript面試問題來測(cè)試程序員使用普通JavaScript的技能。
如何手動(dòng)實(shí)現(xiàn)Array.prototype.map方法?
熟練使用數(shù)組的內(nèi)置方法并不難。但是,如果您只是熟悉語(yǔ)法而又不了解原理,那么很難真正理解JavaScript。
對(duì)于Array.prototype.map,它將創(chuàng)建一個(gè)新數(shù)組,其中將填充在調(diào)用數(shù)組中每個(gè)元素上調(diào)用提供的函數(shù)的結(jié)果。
如果引用lodash,我們可以編寫一個(gè)map函數(shù),如下所示:
function map(array, iteratee) {
let index=-1
const length=array==null ? 0 : array.length
const result=new Array(length)
while (++index < length) {
result[index]=iteratee(array[index], index, array)
}
return result
}
使用示例:
如何實(shí)現(xiàn)這種編碼效果?
我們可以看到,當(dāng)我們嘗試連續(xù)打印obj.a三次時(shí),會(huì)得到三種不同的結(jié)果。看起來多么不可思議!
您可以創(chuàng)建一個(gè)神秘的對(duì)象obj來實(shí)現(xiàn)此效果嗎?
實(shí)際上,此問題有三種解決方案:
· 訪問者屬性
· Object.defineProperty
· 代理
根據(jù)ECMAScript,對(duì)象的屬性可以采用兩種形式:
從邏輯上講,對(duì)象是屬性的集合。每個(gè)屬性都是數(shù)據(jù)屬性或訪問器屬性:
· 數(shù)據(jù)屬性將鍵值與ECMAScript語(yǔ)言值和一組布爾屬性相關(guān)聯(lián)。
· 訪問器屬性將鍵值與一個(gè)或兩個(gè)訪問器函數(shù)以及一組布爾屬性相關(guān)聯(lián)。訪問器函數(shù)用于存儲(chǔ)或檢索與屬性關(guān)聯(lián)的ECMAScript語(yǔ)言值。
所謂的數(shù)據(jù)屬性通常是我們寫的:
let obj={ a: 1, b: 2}
我們對(duì)一個(gè)對(duì)象的屬性只有兩個(gè)操作:讀取屬性和設(shè)置屬性。對(duì)于訪問器屬性,我們使用get和set方法定義屬性,其編寫方式如下:
let obj={
get a(){
console.log('triggle get a() method')
console.log('you can do anything as you want')
return 1
},
set a(value){
console.log('triggle set a() method')
console.log('you can do anything as you want')
console.log(`you are trying to assign ${value} to obj.a`)
}
}
訪問屬性為我們提供了強(qiáng)大的元編程能力,因此我們可以通過以下方式滿足我們的要求:
let obj={
_initValue: 0,
get a() {
this._initValue++;
return this._initValue
}
}
console.log(obj.a, obj.a, obj.a)
第二種方法是使用Object.defineProperty,該方法的工作方式與我們用來訪問屬性的方法相同,除了不是直接聲明訪問屬性,而是通過Object.defineProperty配置訪問屬性。
這使用起來更加靈活,因此我們可以這樣編寫:
let obj={}Object.defineProperty(obj, 'a', { get: (function(){ let initValue=0; return function(){ initValue++; return initValue } })()})console.log(obj.a, obj.a, obj.a)
在這里的get方法中,我們使用了一個(gè)閉包,以便我們需要使用的變量initValue隱藏在閉包中,并且不會(huì)污染其他范圍。
第三種方法是使用代理。
使用代理,我們可以攔截對(duì)對(duì)象屬性的訪問。 只要我們使用代理來攔截對(duì)obj.a的訪問,然后依次返回1、2和3,我們就可以在以下條件之前完成要求:
let initValue=0;
let obj=new Proxy({}, {
get: function(item, property, itemProxy){
if(property==='a'){
initValue++;
return initValue
}
return item[property]
}
})
console.log(obj.a, obj.a, obj.a)
為什么理解這個(gè)問題很重要?因?yàn)镺bject.defineProperty和Proxy給了我們強(qiáng)大的元編程能力,所以我們可以適當(dāng)?shù)匦薷膶?duì)象以做一些特殊的事情。
在著名的前端框架Vue中,其核心機(jī)制之一是數(shù)據(jù)的雙向綁定。在Vue2.0中,Vue通過使用Object.defineProperty實(shí)現(xiàn)了該機(jī)制。在Vue3.0中,使用Proxy完成此機(jī)制。
如果不掌握Vue之類的框架,您將無法真正理解。如果您掌握了這些原則,則只需學(xué)習(xí)Vue的一半,就可以獲得兩倍的結(jié)果。
運(yùn)行此代碼的結(jié)果是什么?
function foo(a,b) {
console.log(b)
return {
foo:function(c){
return foo(c,a);
}
};
}
let res=foo(0);
res.foo(1);
res.foo(2);
res.foo(3);
上面的代碼同時(shí)具有多個(gè)嵌套函數(shù)和三個(gè)foo嵌套函數(shù),乍一看看起來非常繁瑣。那么,我們?nèi)绾卫斫膺@一點(diǎn)呢?
首先,請(qǐng)確保上面的代碼中有多少個(gè)功能?我們可以看到在上面的代碼中的兩個(gè)地方都使用了關(guān)鍵字函數(shù),因此上面的代碼中有兩個(gè)函數(shù),即第一行函數(shù)foo(a,b) 和第四行 foo:function(c)。并且這兩個(gè)函數(shù)具有相同的名稱。
第二個(gè)問題:第5行的foo(c,a)調(diào)用哪個(gè)函數(shù)?如果不確定,讓我們來看一個(gè)簡(jiǎn)單的示例:
var obj={
fn:function (){
console.log(fn);
}
};
obj.fn()
如果我們運(yùn)行該代碼,是否會(huì)引發(fā)異常? 答案是肯定的。
這是因?yàn)閛bj.fn()方法的上限是全局的,并且無法訪問obj內(nèi)部的fn方法。
回到前面的示例,以同樣的邏輯,當(dāng)我們調(diào)用foo(c,a)時(shí),實(shí)際上是在第一行上調(diào)用foo函數(shù)。
當(dāng)我們調(diào)用res.foo(1)時(shí),將調(diào)用哪個(gè)foo? 顯然,第4行的foo函數(shù)被調(diào)用。
因?yàn)檫@兩個(gè)foo函數(shù)的工作方式不同,所以我們可以將其中一個(gè)的名稱更改為bar,以使我們更容易理解代碼。
function foo(a,b) {
console.log(b)
return {
bar:function(c){
return foo(c,a);
}
};
}
let res=foo(0);
res.bar(1);
res.bar(2);
res.bar(3);
此更改不會(huì)影響最終結(jié)果,但會(huì)使我們更容易理解代碼。如果將來遇到類似的問題,請(qǐng)嘗試此技巧。
每次調(diào)用一個(gè)函數(shù)時(shí),都會(huì)創(chuàng)建一個(gè)新的作用域,因此我們可以繪制圖表以幫助我們理解代碼工作原理的邏輯。
當(dāng)我們執(zhí)行l(wèi)et res=foo(0);時(shí),實(shí)際上是在執(zhí)行foo(0,undefiend)。此時(shí),將在程序中創(chuàng)建一個(gè)新的作用域,在當(dāng)前作用域中a=0,b=undefined。因此,我繪制的圖看起來像這樣。
然后將執(zhí)行console.log(b),因此它第一次在控制臺(tái)中打印出" undefined"。
然后執(zhí)行res.bar(1),創(chuàng)建一個(gè)新范圍,其中c=1:
然后從上面的函數(shù)中再次調(diào)用foo(c,a),它實(shí)際上是foo(1,0),作用域如下所示:
在新作用域中,a的值為1,b的值為0,因此控制臺(tái)將打印出0。
再次執(zhí)行res.bar(2)。注意,res.bar(2)和res.bar(1)是并行關(guān)系,因此我們應(yīng)該像這樣繪制范圍圖:
因此,在此代碼中,控制臺(tái)也會(huì)打印出值0。
執(zhí)行res.bar(3)的過程也是如此,控制臺(tái)仍顯示0。
因此,以上代碼的最終結(jié)果是:
實(shí)際上,上述問題可以用其他方式改變。例如,可以將其更改為以下內(nèi)容:
function foo(a,b) {
console.log(b)
return {
foo:function(c){
return foo(c,a);
}
};
}
foo(0).foo(1).foo(2).foo(3);
在解決這個(gè)問題之前,我們要做的第一件事是區(qū)分兩個(gè)不同的foo函數(shù),因此可以將上面的代碼更改為如下所示:
function foo(a,b) {
console.log(b)
return {
bar:function(c){
return foo(c,a);
}
};
}
foo(0).bar(1).bar(2).bar(3);
執(zhí)行foo(0)時(shí),作用域與以前相同,然后控制臺(tái)將打印出" undefined"。
然后執(zhí)行.bar(1)創(chuàng)建一個(gè)新的作用域。此參數(shù)1實(shí)際上是c的值。
然后.bar(1)方法再次調(diào)用foo(c,a),它實(shí)際上是foo(1,0)。這里的參數(shù)1實(shí)際上將是新作用域中a的值,而0將是新作用域中b的值。
因此,控制臺(tái)隨后輸出了b的值,即0。
再次調(diào)用.bar(2),在新作用域中c的值為2:
然后.bar(2)調(diào)用foo(c,a),它實(shí)際上是foo(2,1),其中2是新作用域中a的值,而1是新作用域中b的值。
因此,控制臺(tái)隨后輸出了b的值,即0。
然后它將執(zhí)行.bar(3),該過程與之前相同,因此我將不擴(kuò)展其描述,此步驟控制臺(tái)將打印出2。
如上所述,代碼運(yùn)行的最終結(jié)果是:
好了,經(jīng)過漫長(zhǎng)的旅程,我們終于得到了答案。 這個(gè)問題很好地檢驗(yàn)了受訪者對(duì)封閉和范圍的理解。
假設(shè)我們有一個(gè)看起來像這樣的函數(shù):
function compose (middleware) { // some code}
compose函數(shù)接受函數(shù)數(shù)組中間件:
let middleware=[]
middleware.push((next)=> {
console.log(1)
next()
console.log(1.1)
})
middleware.push((next)=> {
console.log(2)
next()
console.log(2.1)
})
middleware.push(()=> {
console.log(3)
})
let fn=compose(middleware)
fn()
當(dāng)我們嘗試執(zhí)行fn時(shí),它將調(diào)用中間件中的函數(shù),并將下一個(gè)函數(shù)作為參數(shù)傳遞給每個(gè)小函數(shù)。
如果我們?cè)谝粋€(gè)小函數(shù)中執(zhí)行next,則將調(diào)用中間件中該函數(shù)的next函數(shù)。而且,如果您接下來不執(zhí)行,程序也不會(huì)崩潰。
執(zhí)行完上面的代碼后,我們得到以下結(jié)果:
1232.11.1
那么,我們?nèi)绾尉帉懸粋€(gè)compose函數(shù)來做到這一點(diǎn)呢?
首先,compose函數(shù)必須返回一個(gè)composed函數(shù),因此我們可以編寫如下代碼:
function compose (middleware) {
return function () { }
}
然后,在返回的函數(shù)中,中間件的第一個(gè)函數(shù)開始執(zhí)行。我們還將傳遞下一個(gè)函數(shù)作為其參數(shù)。所以讓我們這樣寫:
function compose (middleware) {
return function () {
let f1=middleware[0]
f1(function next(){ })
}
}
下一個(gè)功能充當(dāng)繼續(xù)在中間件中運(yùn)行的開關(guān),如下所示:
function compose (middleware) {
return function () {
let f1=middleware[0]
f1(function next(){
let f2=middleware[1]
f2(function next(){ ... })
})
}
}
然后繼續(xù)在下一個(gè)函數(shù)中調(diào)用第三個(gè)函數(shù)…等待,這看起來像遞歸! 因此,我們可以編寫一個(gè)遞歸函數(shù)來完成此嵌套調(diào)用:
function compose (middleware) {
return function () {
dispatch(0)
function dispatch (i) {
const fn=middleware[i]
if (!fn) return null
fn(function next () {
dispatch(i + 1)
})
}
}
}
好的,這就是我們的撰寫功能,所以讓我們對(duì)其進(jìn)行測(cè)試:
好吧,此功能完全可以完成其所需的工作。 但是我們也可以優(yōu)化我們的compose函數(shù)可以支持異步函數(shù)。 我們可以改進(jìn)以下代碼:
function compose (middleware) {
return async function () {
await dispatch(0)
function async dispatch (i) {
const fn=middleware[i]
if (!fn)
return null
await fn(function next () {
dispatch(i + 1)
})
}
}
}
實(shí)際上,以上的撰寫功能是眾所周知的節(jié)點(diǎn)框架koa的核心機(jī)制。
當(dāng)我選擇候選人時(shí),我接受他/她對(duì)某些框架不熟悉。畢竟,JavaScript生態(tài)系統(tǒng)中有太多的庫(kù)和框架,沒有人能完全掌握它們。但是我確實(shí)希望候選人知道這些重要的原始JavaScript技巧,因?yàn)樗鼈兪撬袔?kù)和框架的基礎(chǔ)。
實(shí)際上,我的草稿中還有其他一些面試問題,但由于本文篇幅有限,因此在此不再繼續(xù)解釋。稍后再與您分享。
本文主要涉及普通JavaScript,而不涉及瀏覽器,節(jié)點(diǎn),框架,算法,設(shè)計(jì)模式等。如果您對(duì)這些主題也感興趣,請(qǐng)隨時(shí)發(fā)表評(píng)論。
感謝您的閱讀!
(本文由聞數(shù)起舞翻譯自bitfish的文章《Improve Your JavaScript Level With These 4 Questions》,轉(zhuǎn)載請(qǐng)注明出處,原文鏈接:https://medium.com/javascript-in-plain-english/i-use-these-4-questions-to-find-outstanding-javascript-developers-4a468ea17155)
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。