天天看點

如果我去參加前端面試,我應該能做出大聖老師的這道題...

我是一名自學敲代碼的管理學研究所學生,喜歡 js/ts 但是菜得不行,平常挺關注國内的前端圈。

如果我去參加前端面試,我應該能做出大聖老師的這道題...

拍澄湖大閘蟹 Ingo Schulz/Offset by Shutterstock

除了讀雪碧大佬[1]等等大佬的知乎外(蒟蒻還看不太懂),平常也看看大聖老師[2]等等的B站。

有一次看大聖老師直播點評履歷,他提到:“如果我來面試你,我就把我面前的筆記本給你,随便給你打開個網頁比如淘寶,你給我用浏覽器現場統計一下各個标簽出現的次數。”

!這道題應該不難?我分析無非就是考察了三點:

  • 最最基礎的浏覽器調試能力
  • 算法能力
  • 基礎的 JavaScript API 應用

剛和爸媽打完球回來,那我就做做這道題。

首先咱捋一下思路:

  • 其實早在聽到這個題目時,我腦子中就蹦出兩個字:『遞歸』!
  • 畢竟,我們的網頁就是一棵 DOM 樹,從根部有子節點,子節點還有子節點,對于每個節點,我們能夠

    知道這個節點是什麼标簽

    并且

    對其子節點做同樣的事

    就可以了

然後我們捋一下需要哪些技術細節:

  • 首先我們應該擷取根節點,這個好說,我們在浏覽器的控制台裡試一試就知道:

    document.children[0]

  • 然後我們應該能夠擷取每個标簽對象的

    字元串名字

    子節點清單

    ,分别是

    tagName

    children

  • 至于如何實作「遞歸」呢?這裡未必要用到遞歸,我用的是寬度優先搜尋 BFS ,簡單一個隊列就能實作

值得一提的是,我近一個月裡寫了基于

C++

Python

JavaScript/TypeScript

Scala/Java

的不同項目/小腳本(工作要求...),是以我也記不住 JavaScript 的 API ,我都是在浏覽器控制台裡試出來的,比如

擷取标簽的名字是 tagName

擷取子節點 Array 是 children

。如下圖,我試關鍵詞試出來的,要不然誰記得住啊。

如果我去參加前端面試,我應該能做出大聖老師的這道題...

輸入 tag 會不會得到我想要的 API 呢?果然!

下面動手來做吧

第零步,打開浏覽器的

Sources

,建立一個 Snippet 。

如果我去參加前端面試,我應該能做出大聖老師的這道題...

Sources

首先我不知道 JavaScript 裡有沒有現成的隊列資料結構,應該是沒有,那我就自己實作一個吧。

class Queue {
    #array = []
    constructor () {
        this.#array = []
    }

    top () {
        return this.#array[0]
    }

    size () {
        return this.#array.length
    }

    pop () {
        this.#array.shift()
    }

    push (ele) {
        this.#array.push(ele)
    }
}
           

複制

很簡單的封裝!我平時做算法題都是用 C++ ,是以這裡方法的名稱就都盡量接近 C++ 的

std::queue<T>

接下來咱們寫 BFS 就行了!

我看現在大佬們都把每個邏輯封裝在函數裡,是以咱也把腳本運作邏輯

main()

裡,然後再在外面調用一下

main()

,看着整潔點。

const main = () => {

    const dict = {}
    const queue = new Queue()

    const htmlTag = document.children[0]
    dict[htmlTag.tagName] += 1  // !!!
    queue.push(htmlTag)

    while (queue.size() > 0) {
        const t = queue.top()
        queue.pop()

        for (let i = 0; i < t.children.length; i ++) {
            childTag = t.children[i]
            dict[htmlTag.tagName] += 1  // !!!
            queue.push(childTag)
        }
    }

    for (let item in dict) {
        console.log(item, ': ', dict[item])
    }
}

main()
           

複制

上面是最最簡單的 BFS 實作了,可見這道題着實不是用算法難為我們,很實在的一道題。

注意我标注的

!!!

兩行,這裡有一個問題:

  • dict = {}

    中,對于未聲明過的鍵值,如果直接調用運算,會報錯

    dict[未聲明的鍵值] +=1 // 報錯!

  • 而 js 又不是 Python ,沒有

    setdefault

    給我們用比如

    dict.setdefault(鍵值, 0); dict[鍵值] += 1

  • js 也不是 C++ ,直接預設未出現過的鍵值的值為 0
  • 是以我們需要再寫一個

    dict[未聲明的鍵值] +=1

    功能

咱們把這個邏輯寫成一個

Effect

,傳回一個函數,以顯示咱很注重邏輯複用性(劃去)。

const addDictEffect =  (dict) => {
    return (name) => {
        if (dict[name]) {
            dict[name] += 1
        } else {
            dict[name] = 1
        }
    }
}
           

複制

OK 那下面在修改一下

main

,一共有三處!

const main = () => {

    const dict = {}
    const addDict = addDictEffect(dict)  // 第一處!
    const queue = new Queue()

    const htmlTag = document.children[0]
    addDict(htmlTag.tagName)  // 第二處!
    queue.push(htmlTag)

    while (queue.size() > 0) {
        const t = queue.top()
        queue.pop()

        for (let i = 0; i < t.children.length; i ++) {
            childTag = t.children[i]
            addDict(childTag.tagName)  // 第三處!
            queue.push(childTag)
        }
    }

    for (let item in dict) {
        console.log(item, ': ', dict[item])
    }
}

main()
           

複制

啪!很快啊,本題目解決。

www.taobao.com

結果如下。

如果我去參加前端面試,我應該能做出大聖老師的這道題...

代碼

如果我去參加前端面試,我應該能做出大聖老師的這道題...

結果

其他網頁均可測試。

完整代碼

class Queue {
    #array = []
    constructor () {
        this.#array = []
    }

    top () {
        return this.#array[0]
    }

    size () {
        return this.#array.length
    }

    pop () {
        this.#array.shift()
    }

    push (ele) {
        this.#array.push(ele)
    }
}

const addDictEffect =  (dict) => {
    return (name) => {
        if (dict[name]) {
            dict[name] += 1
        } else {
            dict[name] = 1
        }
    }
}

const main = () => {

    const dict = {}
    const addDict = addDictEffect(dict)
    const queue = new Queue()

    const htmlTag = document.children[0]
    addDict(htmlTag.tagName)
    queue.push(htmlTag)

    while (queue.size() > 0) {
        const t = queue.top()
        queue.pop()

        for (let i = 0; i < t.children.length; i ++) {
            childTag = t.children[i]
            addDict(childTag.tagName)
            queue.push(childTag)
        }
    }

    for (let item in dict) {
        console.log(item, ': ', dict[item])
    }
}

main()
           

複制

目前不會js/ts+沒做過項目,菜到都沒法給大聖老師發履歷讓他點評。期待早日能到發履歷的地步。

參考資料

[1]

雪碧大佬: https://www.zhihu.com/people/doodlewind

[2]

大聖老師: https://space.bilibili.com/26995758