自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

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

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

[[355335]]

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

如何手動(dòng)實(shí)現(xiàn)Array.prototype.map方法?

熟練使用數(shù)組的內(nèi)置方法并不難。但是,如果您只是熟悉語法而又不了解原理,那么很難真正理解JavaScript。

對(duì)于Array.prototype.map,它將創(chuàng)建一個(gè)新數(shù)組,其中將填充在調(diào)用數(shù)組中每個(gè)元素上調(diào)用提供的函數(shù)的結(jié)果。

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

如果引用lodash,我們可以編寫一個(gè)map函數(shù),如下所示:

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

 

  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 

 

使用示例:

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

2. Object.defineProperty和代理

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

 

 

我們可以看到,當(dāng)我們嘗試連續(xù)打印obj.a三次時(shí),會(huì)得到三種不同的結(jié)果??雌饋矶嗝床豢伤甲h!

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

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

  • 訪問者屬性
  • Object.defineProperty
  • 代理

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

 

 

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

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

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

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

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

 

 

 

  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.   } 

 

 

 

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

 

  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,該方法的工作方式與我們用來訪問屬性的方法相同,除了不是直接聲明訪問屬性,而是通過Object.defineProperty配置訪問屬性。

這使用起來更加靈活,因此我們可以這樣編寫:

 

 

 

  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ì)象屬性的訪問。 只要我們使用代理來攔截對(duì)obj.a的訪問,然后依次返回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.  
  12. console.log(obj.a, obj.a, obj.a) 

 

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

為什么理解這個(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é)果。

3.范圍和閉包

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

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

 

  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ù),乍一看看起來非常繁瑣。那么,我們?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è)簡單的示例:

 

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

 

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

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

這是因?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,以使我們更容易理解代碼。

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

 

  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ì)使我們更容易理解代碼。如果將來遇到類似的問題,請(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),作用域如下所示:

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

在新作用域中,a的值為1,b的值為0,因此控制臺(tái)將打印出0。

再次執(zhí)行res.bar(2)。注意,res.bar(2)和res.bar(1)是并行關(guān)系,因此我們應(yīng)該像這樣繪制范圍圖:

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

因此,在此代碼中,控制臺(tái)也會(huì)打印出值0。

執(zhí)行res.bar(3)的過程也是如此,控制臺(tái)仍顯示0。

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

 

 

實(shí)際上,上述問題可以用其他方式改變。例如,可以將其更改為以下內(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è)問題之前,我們要做的第一件事是區(qū)分兩個(gè)不同的foo函數(shù),因此可以將上面的代碼更改為如下所示:

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

 

  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"。

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

然后執(zhí)行.bar(1)創(chuàng)建一個(gè)新的作用域。此參數(shù)1實(shí)際上是c的值。

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

然后.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é)果是:

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

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

4.撰寫 Compose

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

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

 

  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.  
  8. middleware.push((next) => {  
  9.   console.log(2)  
  10.   next()  
  11.   console.log(2.1) 
  12. }) 
  13.  
  14. middleware.push(() => {  
  15.   console.log(3) 
  16. }) 
  17.  
  18. let fn = compose(middleware) 
  19.  
  20. 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ù),因此我們可以編寫如下代碼:

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

 

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

 

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

 

 

function compose (middleware) {

 

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

 

下一個(gè)功能充當(dāng)繼續(xù)在中間件中運(yùn)行的開關(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.   } 

 

然后繼續(xù)在下一個(gè)函數(shù)中調(diào)用第三個(gè)函數(shù)…等待,這看起來像遞歸! 因此,我們可以編寫一個(gè)遞歸函數(shù)來完成此嵌套調(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.   } 

 

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

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

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

 

這4個(gè)問題可以檢測(cè)出你JavaScript水平的高低

 

 

  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.   } 

 

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

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

結(jié)論

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

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

責(zé)任編輯:華軒 來源: 今日頭條
相關(guān)推薦

2025-02-25 11:12:53

2024-07-22 00:00:00

2024-12-09 08:49:01

2017-05-18 09:16:54

前端CSS技巧

2019-06-14 10:56:43

JavaMaven編程語言

2020-08-06 08:27:21

JavaScript概念語言

2021-01-10 23:36:52

SQL數(shù)據(jù)庫技術(shù)

2019-10-18 15:16:10

Redis數(shù)據(jù)庫并發(fā)

2011-11-04 12:00:38

2020-07-30 08:27:33

Javascript閉包變量

2017-05-17 17:23:00

2018-09-03 14:49:27

Python實(shí)戰(zhàn)項(xiàng)目

2023-09-01 07:25:39

領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD

2020-11-11 07:52:04

CSS功能瀏覽器

2022-03-07 10:22:07

DevOps開發(fā)工具

2020-08-04 06:32:21

JavaScript代碼開發(fā)

2022-03-17 13:44:30

Git命令管理

2023-06-01 16:41:39

NumPyPython

2023-07-18 07:51:56

JavaScriptAPI

2014-11-07 10:26:05

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)