日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
這4個(gè)問(wèn)題可以檢測(cè)出你JavaScript水平的高低

 JavaScript現(xiàn)在是一種非常流行的編程語(yǔ)言,基于該語(yǔ)言,派生了大量庫(kù)和框架。 但是,無(wú)論高層生態(tài)系統(tǒng)如何發(fā)展,離不開(kāi)原始的JavaScript。 在這里,我選擇了4個(gè)JavaScript面試問(wèn)題來(lái)測(cè)試程序員使用普通JavaScript的技能。

創(chuàng)新互聯(lián)專(zhuān)注于企業(yè)網(wǎng)絡(luò)營(yíng)銷(xiāo)推廣、網(wǎng)站重做改版、豐都網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、成都h5網(wǎng)站建設(shè)、商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性?xún)r(jià)比高,為豐都等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。

1.實(shí)現(xiàn)Array.prototype.map

如何手動(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,我們可以編寫(xiě)一個(gè)map函數(shù),如下所示:

 
 
 
 
  1. function map(array, iteratee) { 
  2.   let index = -1 
  3.   const length = array == null ? 0 : array.length 
  4.   const result = new Array(length) 
  5.   while (++index < length) { 
  6.     result[index] = iteratee(array[index], index, array) 
  7.   } 
  8.   return result
  9. }

使用示例:

2. Object.defineProperty和代理

如何實(shí)現(xiàn)這種編碼效果?

我們可以看到,當(dāng)我們嘗試連續(xù)打印obj.a三次時(shí),會(huì)得到三種不同的結(jié)果。看起來(lái)多么不可思議!

您可以創(chuàng)建一個(gè)神秘的對(duì)象obj來(lái)實(shí)現(xiàn)此效果嗎?

實(shí)際上,此問(wèn)題有三種解決方案:

  • 訪問(wèn)者屬性
  • Object.defineProperty
  • 代理

根據(jù)ECMAScript,對(duì)象的屬性可以采用兩種形式:

從邏輯上講,對(duì)象是屬性的集合。每個(gè)屬性都是數(shù)據(jù)屬性或訪問(wèn)器屬性:

  • 數(shù)據(jù)屬性將鍵值與ECMAScript語(yǔ)言值和一組布爾屬性相關(guān)聯(lián)。
  • 訪問(wèn)器屬性將鍵值與一個(gè)或兩個(gè)訪問(wèn)器函數(shù)以及一組布爾屬性相關(guān)聯(lián)。訪問(wèn)器函數(shù)用于存儲(chǔ)或檢索與屬性關(guān)聯(lián)的ECMAScript語(yǔ)言值。

所謂的數(shù)據(jù)屬性通常是我們寫(xiě)的:

let obj = { a: 1, b: 2}

我們對(duì)一個(gè)對(duì)象的屬性只有兩個(gè)操作:讀取屬性和設(shè)置屬性。對(duì)于訪問(wèn)器屬性,我們使用get和set方法定義屬性,其編寫(xiě)方式如下:

 
 
 
 
  1. let obj = { 
  2.   get a(){ 
  3.     console.log('triggle get a() method') 
  4.     console.log('you can do anything as you want') 
  5.     return 1 
  6.   }, 
  7.   set a(value){ 
  8.     console.log('triggle set a() method') 
  9.     console.log('you can do anything as you want') 
  10.     console.log(`you are trying to assign ${value} to obj.a`) 
  11.   }
  12. }

訪問(wèn)屬性為我們提供了強(qiáng)大的元編程能力,因此我們可以通過(guò)以下方式滿足我們的要求:

 
 
 
 
  1. let obj = { 
  2.   _initValue: 0, 
  3.   get a() { 
  4.     this._initValue++; 
  5.     return this._initValue 
  6.   }
  7. }
  8. console.log(obj.a, obj.a, obj.a)

第二種方法是使用Object.defineProperty,該方法的工作方式與我們用來(lái)訪問(wèn)屬性的方法相同,除了不是直接聲明訪問(wèn)屬性,而是通過(guò)Object.defineProperty配置訪問(wèn)屬性。

這使用起來(lái)更加靈活,因此我們可以這樣編寫(xiě):

 
 
 
 
  1. 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ì)象屬性的訪問(wèn)。 只要我們使用代理來(lái)攔截對(duì)obj.a的訪問(wèn),然后依次返回1、2和3,我們就可以在以下條件之前完成要求:

 
 
 
 
  1. let initValue = 0;
  2. let obj = new Proxy({}, { 
  3.   get: function(item, property, itemProxy){ 
  4.     if(property === 'a'){ 
  5.       initValue++; 
  6.       return initValue 
  7.     } 
  8.     return item[property] 
  9.   }
  10. })
  11. console.log(obj.a, obj.a, obj.a)

為什么理解這個(gè)問(wèn)題很重要?因?yàn)镺bject.defineProperty和Proxy給了我們強(qiáng)大的元編程能力,所以我們可以適當(dāng)?shù)匦薷膶?duì)象以做一些特殊的事情。

在著名的前端框架Vue中,其核心機(jī)制之一是數(shù)據(jù)的雙向綁定。在Vue2.0中,Vue通過(guò)使用Object.defineProperty實(shí)現(xiàn)了該機(jī)制。在Vue3.0中,使用Proxy完成此機(jī)制。

如果不掌握Vue之類(lèi)的框架,您將無(wú)法真正理解。如果您掌握了這些原則,則只需學(xué)習(xí)Vue的一半,就可以獲得兩倍的結(jié)果。

3.范圍和閉包

運(yùn)行此代碼的結(jié)果是什么?

 
 
 
 
  1. function foo(a,b) { 
  2.   console.log(b) 
  3.   return { 
  4.     foo:function(c){ 
  5.       return foo(c,a); 
  6.     } 
  7.   };
  8. }
  9. let res = foo(0); 
  10. res.foo(1); 
  11. res.foo(2); 
  12. res.foo(3);

上面的代碼同時(shí)具有多個(gè)嵌套函數(shù)和三個(gè)foo嵌套函數(shù),乍一看看起來(lái)非常繁瑣。那么,我們?nèi)绾卫斫膺@一點(diǎn)呢?

首先,請(qǐng)確保上面的代碼中有多少個(gè)功能?我們可以看到在上面的代碼中的兩個(gè)地方都使用了關(guān)鍵字函數(shù),因此上面的代碼中有兩個(gè)函數(shù),即第一行函數(shù)foo(a,b) 和第四行 foo:function(c)。并且這兩個(gè)函數(shù)具有相同的名稱(chēng)。

第二個(gè)問(wèn)題:第5行的foo(c,a)調(diào)用哪個(gè)函數(shù)?如果不確定,讓我們來(lái)看一個(gè)簡(jiǎn)單的示例:

 
 
 
 
  1. var obj={ 
  2.   fn:function (){ 
  3.     console.log(fn); 
  4.   }
  5. };
  6. obj.fn()

如果我們運(yùn)行該代碼,是否會(huì)引發(fā)異常? 答案是肯定的。

這是因?yàn)閛bj.fn()方法的上限是全局的,并且無(wú)法訪問(wèn)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è)的名稱(chēng)更改為bar,以使我們更容易理解代碼。

 
 
 
 
  1. function foo(a,b) { 
  2.   console.log(b) 
  3.   return { 
  4.     bar:function(c){ 
  5.       return foo(c,a); 
  6.     } 
  7.   };
  8. }
  9. let res = foo(0); 
  10. res.bar(1); 
  11. res.bar(2); 
  12. res.bar(3);

此更改不會(huì)影響最終結(jié)果,但會(huì)使我們更容易理解代碼。如果將來(lái)遇到類(lèi)似的問(wèn)題,請(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。因此,我繪制的圖看起來(lái)像這樣。

然后將執(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)的過(guò)程也是如此,控制臺(tái)仍顯示0。

因此,以上代碼的最終結(jié)果是:

實(shí)際上,上述問(wèn)題可以用其他方式改變。例如,可以將其更改為以下內(nèi)容:

 
 
 
 
  1. function foo(a,b) { 
  2.   console.log(b) 
  3.   return { 
  4.     foo:function(c){ 
  5.       return foo(c,a); 
  6.     } 
  7.   };
  8. }
  9. foo(0).foo(1).foo(2).foo(3);

在解決這個(gè)問(wèn)題之前,我們要做的第一件事是區(qū)分兩個(gè)不同的foo函數(shù),因此可以將上面的代碼更改為如下所示:

 
 
 
 
  1. function foo(a,b) { 
  2.   console.log(b) 
  3.   return { 
  4.     bar:function(c){ 
  5.       return foo(c,a); 
  6.     } 
  7.   };
  8. }
  9.  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),該過(guò)程與之前相同,因此我將不擴(kuò)展其描述,此步驟控制臺(tái)將打印出2。

如上所述,代碼運(yùn)行的最終結(jié)果是:

好了,經(jīng)過(guò)漫長(zhǎng)的旅程,我們終于得到了答案。 這個(gè)問(wèn)題很好地檢驗(yàn)了受訪者對(duì)封閉和范圍的理解。

4.撰寫(xiě) Compose

假設(shè)我們有一個(gè)看起來(lái)像這樣的函數(shù):

 
 
 
 
  1. function compose (middleware) { // some code}

compose函數(shù)接受函數(shù)數(shù)組中間件:

 
 
 
 
  1. let middleware = []
  2. middleware.push((next) => { 
  3.   console.log(1) 
  4.   next() 
  5.   console.log(1.1)
  6. })
  7. middleware.push((next) => { 
  8.   console.log(2) 
  9.   next() 
  10.   console.log(2.1)
  11. })
  12. middleware.push(() => { 
  13.   console.log(3)
  14. })
  15. let fn = compose(middleware)
  16. fn()

當(dāng)我們嘗試執(zhí)行fn時(shí),它將調(diào)用中間件中的函數(shù),并將下一個(gè)函數(shù)作為參數(shù)傳遞給每個(gè)小函數(shù)。

如果我們?cè)谝粋€(gè)小函數(shù)中執(zhí)行next,則將調(diào)用中間件中該函數(shù)的next函數(shù)。而且,如果您接下來(lái)不執(zhí)行,程序也不會(huì)崩潰。

執(zhí)行完上面的代碼后,我們得到以下結(jié)果:

1232.11.1

那么,我們?nèi)绾尉帉?xiě)一個(gè)compose函數(shù)來(lái)做到這一點(diǎn)呢?

首先,compose函數(shù)必須返回一個(gè)composed函數(shù),因此我們可以編寫(xiě)如下代碼:

 
 
 
 
  1. function compose (middleware) { 
  2.   return function () { }
  3. }

然后,在返回的函數(shù)中,中間件的第一個(gè)函數(shù)開(kāi)始執(zhí)行。我們還將傳遞下一個(gè)函數(shù)作為其參數(shù)。所以讓我們這樣寫(xiě):

function compose (middleware) {

 
 
 
 
  1. function compose (middleware) { 
  2.   return function () { 
  3.     let f1 = middleware[0] 
  4.     f1(function next(){ }) 
  5.   }
  6. }

下一個(gè)功能充當(dāng)繼續(xù)在中間件中運(yùn)行的開(kāi)關(guān),如下所示:

 
 
 
 
  1. function compose (middleware) { 
  2.   return function () { 
  3.     let f1 = middleware[0] 
  4.     f1(function next(){ 
  5.       let f2 = middleware[1] 
  6.       f2(function next(){ ... }) 
  7.     }) 
  8.   }
  9. }

然后繼續(xù)在下一個(gè)函數(shù)中調(diào)用第三個(gè)函數(shù)…等待,這看起來(lái)像遞歸! 因此,我們可以編寫(xiě)一個(gè)遞歸函數(shù)來(lái)完成此嵌套調(diào)用:

 
 
 
 
  1. function compose (middleware) { 
  2.   return function () { 
  3.     dispatch(0) 
  4.     function dispatch (i) { 
  5.       const fn = middleware[i] 
  6.       if (!fn) return null 
  7.       fn(function next () { 
  8.         dispatch(i + 1) 
  9.       }) 
  10.     } 
  11.   }
  12. }

好的,這就是我們的撰寫(xiě)功能,所以讓我們對(duì)其進(jìn)行測(cè)試:

好吧,此功能完全可以完成其所需的工作。 但是我們也可以?xún)?yōu)化我們的compose函數(shù)可以支持異步函數(shù)。 我們可以改進(jìn)以下代碼:

 
 
 
 
  1. function compose (middleware) { 
  2.   return async function () { 
  3.     await dispatch(0) 
  4.     function async dispatch (i) { 
  5.       const fn = middleware[i] 
  6.       if (!fn) 
  7.         return null 
  8.       await fn(function next () { 
  9.         dispatch(i + 1) 
  10.       }) 
  11.     } 
  12.   }
  13. }

實(shí)際上,以上的撰寫(xiě)功能是眾所周知的節(jié)點(diǎn)框架koa的核心機(jī)制。

當(dāng)我選擇候選人時(shí),我接受他/她對(duì)某些框架不熟悉。畢竟,JavaScript生態(tài)系統(tǒng)中有太多的庫(kù)和框架,沒(méi)有人能完全掌握它們。但是我確實(shí)希望候選人知道這些重要的原始JavaScript技巧,因?yàn)樗鼈兪撬袔?kù)和框架的基礎(chǔ)。

結(jié)論

實(shí)際上,我的草稿中還有其他一些面試問(wèn)題,但由于本文篇幅有限,因此在此不再繼續(xù)解釋。稍后再與您分享。

本文主要涉及普通JavaScript,而不涉及瀏覽器,節(jié)點(diǎn),框架,算法,設(shè)計(jì)模式等。如果您對(duì)這些主題也感興趣,請(qǐng)隨時(shí)發(fā)表評(píng)論。


文章題目:這4個(gè)問(wèn)題可以檢測(cè)出你JavaScript水平的高低
標(biāo)題來(lái)源:http://www.5511xx.com/article/dhpdgoj.html