一道面試題引起的思考

来源:https://www.cnblogs.com/chenjg/archive/2018/11/23/10006502.html
-Advertisement-
Play Games

今天在認真乾(劃)活(水)的時候,看到群里有人發了一道頭條的面試題,就順便看了一下,發現挺有意思的,就決定分享給大家,並且給出我的解決方案和思考過程。 題目如下: 實現一個get函數,使得下麵的調用可以輸出正確的結果 乍眼一看,這不就是實現一個lodash.get方法嗎?看上去好像很簡單。所以我就開 ...


今天在認真乾(劃)活(水)的時候,看到群里有人發了一道頭條的面試題,就順便看了一下,發現挺有意思的,就決定分享給大家,並且給出我的解決方案和思考過程。

題目如下:

實現一個get函數,使得下麵的調用可以輸出正確的結果

const obj = { selector: { to: { toutiao: "FE Coder"} }, target: [1, 2, { name: 'byted'}]};

get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name');
// [ 'FE Coder', 1, 'byted']

乍眼一看,這不就是實現一個lodash.get方法嗎?看上去好像很簡單。所以我就開始寫了第一個版本。思想其實很簡單,遍歷傳進來的參數,使用split將每一個參數分隔開,然後遍歷取值,最終返回結果。

function get(data, ...args) {
    return args.map((item) => {
        const paths = item.split('.');
        let res = data;
        paths.map(path => res = res[path]);
        return res;
    })
}

一運行,果不其然,報錯了。

後來仔細看了一下提供的測試代碼,發現居然有target[0]這種東西。。居然還帶了個數組索引。

冷靜分析一下,對於後面帶了個索引的類型,比如'target[0]',我們肯定是要特殊對待的。所以,我們首先得先識別到這種特殊的類型,然後再對它進行額外處理。

這個時候,很快的就可以想到使用正則表達式來做這個事情。為什麼呢?因為像這種帶有索引的類型,他們都有一個特色,就是有固定的格式:[num],那麼我們只需要能構造出可以匹配這種固定格式的正則,就可以解決這個問題。

對於這種格式,不難想到可以用這個正則表達式來做判斷:/\[[0-9]+\]/gi,可是我們還需要將匹配值取出來。這個時候查了下正則表達式的文檔(文檔點擊這裡),發現有一個match方法,可以返回匹配成功的結果。那麼就讓我們來做個測試:

const reg = /\[[0-9]+\]/gi;
const str = "target[123123]";
const str1 = "target[]"

if (reg.test(str)) {
    console.log('test success');
}

if (!reg.test(str1)) {
    console.log('test fail');
}

const matchResult = str.match(reg);
console.log(matchResult); // ["[123123]"]

誒,我們現在已經找到瞭解決這種問題的方法,那讓我們趕緊來繼續改進下代碼。

function get(data, ...args) {
    const reg = /\[[0-9]+\]/gi;
    return args.map((item) => {
        const paths = item.split('.');
        let res = data;
        paths.map((path) => {
                  if (reg.test(path)) {
                    const match = path.match(reg)[0];
                    // 將target[0]里的target儲存到cmd里
                    const cmd = path.replace(match, '');
                    // 獲取數組索引
                    const arrIndex = match.replace(/[\[\]]/gi, '');
                    res = res[cmd][arrIndex];
                  } else {
                    res = res[path];
                  }
        });
        return res;
    });
}


const obj = { selector: { to: { toutiao: "FE Coder"} }, target: [1, 2, { name: 'byted'}]};

console.log(get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name'));

寫完趕緊運行一下,完美,輸出了正確的結果了。那麼到這裡就結束了?

改進

可是總感覺有點不妥,感覺事情沒有那麼簡單。一般來說,面試題除了考驗你解決問題的能力之外,可能還考驗著你思考問題的全面性、嚴謹性。像上面那種寫法,如果用戶傳入了一個不存在的path鏈或者一些其他特殊情況,就可能導致整個程式crash掉。想下lodash.get調用方式,即使你傳入了錯誤的path,他也可以幫你做處理,並且返回一個undefined。因此,我們還需要完善這個方法。

function get(data, ...args) {
    const reg = /\[[0-9]+\]/gi;
    return args.map((item) => {
        const paths = item.split('.');
        let res = data;
        paths.map(path => {
            try {
                if (reg.test(path)) {
                    const match = path.match(reg)[0];
                    const cmd = path.replace(match, '');
                    const arrIndex = match.replace(/[\[\]]/gi, '');
                    res = res[cmd][arrIndex];
                } else {
                    res = res[path];
                }
            } catch (err) {
                console.error(err);
                res = undefined;
            }
        });
        return res;
    });
}

在這裡,我們對每一個path的處理進行了try catch處理。若出錯了,則返回undefined。哇,這樣看起來就比較穩了。

那麼,有沒有別的解決方法呢?

群里有一個大佬提出了一種更簡單也很取巧的解決方案,就是通過構建一個Function解決這個問題(Function的詳細介紹點擊這裡)。由於代碼很簡單,我就直接貼出來了:

function get(data, ...args) {
    const res = JSON.stringify(data);
    return args.map((item) => (new Function(`try {return ${res}.${item} } catch(e) {}`))());
}

const obj = { selector: { to: { toutiao: "FE Coder"} }, target: [1, 2, { name: 'byted'}]};

console.log(get(obj, 'selector.to.toutiao', 'target[0]', 'target[2].name', 'asd'));

看完之後,就兩個字,牛逼。

這種方法我承認一開始我確實沒想到,確實是很奇技淫巧。不過仔細思考了下,其實很多框架都用到了這個奇技淫巧。比如說vue里,就使用new Function的方式來動態創建函數,解決執行動態生成的代碼的問題。

image-20181123104236389

image-20181123104312474

再比如說,Function.prototype.bind方法里(我寫了個類似的bind方法:倉庫),也使用了Function來解決一些問題(fn.length丟失問題)。說明這個東西還是挺有用的,得學習瞭解一波,說不定哪天就用到了。

更新

有人提到了那種Function的方式沒辦法處理以下的處理:

let obj = {time : new Date(), a : "this is a", b : 30};

因為JSON.stringfy後,Date、Function和RegExp類型的變數都會失效。對於這種情況,評論區有個大佬(馮恆智)也提到了一種很好的解決方案:

function get(data, ...args) {
    return args.map((item) => (new Function('data',`try {return data.${item} } catch(e) {}`))(data));
}

除此之外, 代碼宇宙提出了另一種解決方案,就是將"target[0]"分為兩個key,也很簡單粗暴,就是將在split之前,將字元串里的'['替換為'.',將']'直接去掉。這樣就可以將"target[0]"變為"target.0"。具體代碼如下:

function get(data, ...args) {
    return args.map((item) => {
                let res = data;
                item
                    .replace(/\[/g, ".")
                    .replace(/\]/g, "")
                    .split('.')
                    .map(path => res = res && res[path]);
        return res;
    })
}

而且這兩種方式的好處在於,它也可以處理多維數組的情況。

總結

學習完之後,最重要就是要總結,只有總結下來了,知識才是你自己的。那麼我來總結下文章想表達的內容:

  1. 對於具有固定格式的字元串,可以考慮使用正則表達式來識別和匹配。
  2. 實現一個功能的時候,不要只考慮正常情況,要多考慮一些非正常情況,比如輸入格式不對、用戶不按套路來或者因為一些奇奇怪怪的事情報錯。並且能對可預見的非正常情況做一個容錯處理。
  3. 有時候還是可以多學習瞭解一下一些黑科技(比如Function),說不定哪天就可以用它來解決問題。

本文地址在->本人博客地址, 歡迎給個 start 或 follow


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Less (Leaner Style Sheets 的縮寫) 是一門向後相容的 CSS 擴展語言。包含了 Less 語言以及利用 JavaScript 開發的用於將 Less 樣式轉換成 CSS 樣式的 Less.js 工具。 因為 Less 和 CSS 非常像,學習很容易。而且 Less 僅對 C... ...
  • 目標:建一個自動化工作流環境 自動編譯 自動合併 自動刷新 自動部署 工作流: 1 全局安裝webpack 執行命令: 其中webpack-cil 是命令介面工具 2 初始化當前項目:npm init 然後一路回車即可 3 為了避免衝突,我們再進行局部安裝:npm install webpack w ...
  • 獲取某個對象的原型Object.getPrototypeOf(obj) Object.getPrototypeOf({})==Object.prototypetrue var a = new Object();Object.getPrototypeOf(a)==Object.prototypetru ...
  • HTML<!-- 被註釋的內容 --> CSS/* 被註釋的內容 */ JavaScript單行註釋://被註釋的內容多行註釋:/*被註釋的內容*/ ...
  • 如今,各種互聯網的Web應用程式層出不窮,那麼如何快速入門,成長為一個優秀的Web開發工作者呢? 這個問題不容易回答,幾乎所有的培訓機構都不能清晰地解答。 所以對於Web開發剛剛入門的菜鳥們,我覺得只有通過去做,去實驗,學會Web開發,可能是學著學著,實驗著實驗著就會了。 沒有人告訴你如何去做。我學 ...
  • es6新特性, 提取數組或對象中的值,按照對應位置, 為變數賦值。 交換變數的值變得容易 詳情 點擊這裡 ...
  • git指令總結及常見問題積累與解決方案 git初始化一個項目並且長傳到伺服器後端步驟: 1、本地文件操作 通過:git init初始化化一個項目 會出現一個隱藏文件 ,可以文件夾屬性設置進行查看,此時文件的狀態有三個:原始文件狀態、納入緩存文件狀態、納入版本庫的文件狀態。 原始文件狀態:是文件最初始 ...
  • 在實際開發中,我們經常遇見邊框需要背景漸變的實現要求,那麼如何去實現呢,今天給大家分享依稀幾種情況 1.直角的背景漸變 註意問題:border-image的使用是不能實現圓角的效果,各位需要註意這個屬性 2.圓角的背景漸變 代碼如下:利用偽類元素去實現背景邊的漸變效果,同時我們還可以加上動畫效果,利 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...