溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

發(fā)布時(shí)間:2021-09-30 11:41:56 來(lái)源:億速云 閱讀:110 作者:柒染 欄目:web開(kāi)發(fā)

今天就跟大家聊聊有關(guān)可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

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

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é)果。

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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 }

使用示例:

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

2. Object.defineProperty和代理

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wè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ì)象的屬性可以采用兩種形式:

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

從邏輯上講,對(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ù)屬性通常是我們寫的:

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

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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`)    } }
可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

let obj = {    _initValue: 0,    get a() {      this._initValue++;      return this._initValue    } }  console.log(obj.a, obj.a, obj.a)
可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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,我們就可以在以下條件之前完成要求:

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)
可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

為什么理解這個(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之類的框架,您將無(wú)法真正理解。如果您掌握了這些原則,則只需學(xué)習(xí)Vue的一半,就可以獲得兩倍的結(jié)果。

3.范圍和閉包

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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ù),乍一看看起來(lái)非常繁瑣。那么,我們?nèi)绾卫斫膺@一點(diǎn)呢?

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

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

var obj={    fn:function (){      console.log(fn);    } };  obj.fn()

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

這是因?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è)的名稱更改為bar,以使我們更容易理解代碼。

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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ì)使我們更容易理解代碼。如果將來(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)像這樣。

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

然后將執(zhí)行console.log(b),因此它第一次在控制臺(tái)中打印出" undefined"。

然后執(zhí)行res.bar(1),創(chuàng)建一個(gè)新范圍,其中c = 1:

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

然后從上面的函數(shù)中再次調(diào)用foo(c,a),它實(shí)際上是foo(1,0),作用域如下所示:

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

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

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
function foo(a,b) {    console.log(b)    return {      foo:function(c){        return foo(c,a);      }    }; }  foo(0).foo(1).foo(2).foo(3);

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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"。

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

然后.bar(1)方法再次調(diào)用foo(c,a),它實(shí)際上是foo(1,0)。這里的參數(shù)1實(shí)際上將是新作用域中a的值,而0將是新作用域中b的值。

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

因此,控制臺(tái)隨后輸出了b的值,即0。

再次調(diào)用.bar(2),在新作用域中c的值為2:

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

然后.bar(2)調(diào)用foo(c,a),它實(shí)際上是foo(2,1),其中2是新作用域中a的值,而1是新作用域中b的值。

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

因此,控制臺(tái)隨后輸出了b的值,即0。

然后它將執(zhí)行.bar(3),該過(guò)程與之前相同,因此我將不擴(kuò)展其描述,此步驟控制臺(tái)將打印出2。

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

4.撰寫 Compose

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
function compose (middleware) { // some code}

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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ù)。而且,如果您接下來(lái)不執(zhí)行,程序也不會(huì)崩潰。

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

1232.11.1

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

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
function compose (middleware) {    return function () { } }

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

function compose (middleware) {

function compose (middleware) {    return function () {      let f1 = middleware[0]      f1(function next(){ })    } }

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wè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ù)&hellip;等待,這看起來(lái)像遞歸! 因此,我們可以編寫一個(gè)遞歸函數(shù)來(lái)完成此嵌套調(diào)用:

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么
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è)試:

可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么

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

可以檢測(cè)出你JavaScript水平的4個(gè)問(wè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ù)和框架,沒(méi)有人能完全掌握它們。但是我確實(shí)希望候選人知道這些重要的原始JavaScript技巧,因?yàn)樗鼈兪撬袔?kù)和框架的基礎(chǔ)。

看完上述內(nèi)容,你們對(duì)可以檢測(cè)出你JavaScript水平的4個(gè)問(wèn)題分別是什么有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI