一次阿里面試後對函式本質的理解

言己發表於2019-02-16

一次阿里面試後對函式本質的理解

寫在前面

  • 環境:阿里的線上程式設計系統允許面試官線上考察面試者的程式設計能力.

  • 考點:程式設計和理論.

    • 程式設計:分為技術自驅力、非同步操作、程式設計風格(顆粒小)、變數作用域、DOM操作等.

    • 理論:效能優化,瀏覽器執行機制,協議/標準等

本文側重於程式設計,在程式設計中對函式的運用是寫好程式的基礎。(參考開發者技能修煉的五個等級中“第二階梯:Developer,開發工程師”“知道了變數、邏輯與函式的意義”)

丟擲一個問題

如何寫一個訊號燈?(參考一道關於Promise應用的面試題)

那麼如何寫好一個訊號燈?

我們首先發散一下思維:promise,yield,async/await(ES7)。好的,技術點有了,那接下來該選擇哪個方式呢?

不妨就選擇Promise,和其他方式做個對比。

對比技術點

唉,一上來就看到async/await是ES7的特性,新生代的佼佼者,Promise已無用武之地?(參考Async/Await替代Promise的6個理由)

那我們還是先來看看有沒有挽回餘地(對promise多少有些感情了)?

  • 2.錯誤處理

文中錯誤處理一點說到“Async/Await讓try/catch可以同時處理同步和非同步錯誤。在下面的promise示例中,try/catch不能處理JSON.parse的錯誤,因為它在Promise中。我們需要使用.catch,這樣錯誤處理程式碼非常冗餘。並且,在我們的實際生產程式碼會更加複雜。”

我們想想問題根源應該是每步都需要catch,那將then中的函式統一放入陣列,然後遞迴執行可以解決這個問題。(另外,參考Toxicity:這些關鍵字有毒裡面有說到eval with try/catch對效能有一定的影響)

  • 3.條件語句

文中說到“需要獲取資料,然後根據返回資料決定是直接返回,還是繼續獲取更多的資料。”

我們可以適當通過reject(“)解決的,記得最後一定要有一個catch((err) => console.log(“))。等等那是和上面衝突了(每步catch)。這樣使用裝飾者模式對函式再wrapper。

  • 4.中間值

文中提到“你很可能遇到過這樣的場景,呼叫promise1,使用promise1返回的結果去呼叫promise2,然後使用兩者的結果去呼叫promise3。”

我們使用觀察者模式解決。

  • 6.除錯

文中提到“最後一點,也是非常重要的一點在於,async/await能夠使得程式碼除錯更簡單。2個理由使得除錯Promise變得非常痛苦”

我們也通過遞迴和 將要執行的函式放入一個陣列解決。

對Promise幾個主要缺點找到了補償措施,就可以進行編碼實現了。(當然我們還是要擁抱新特性的)

變數命名

export function singalLamp(singalArr) {
}

大家都知道對比typeScript,JS是動態+弱型別(動弱無關)。那麼變數命名就需要在表達清邏輯的同時攜帶變數型別。好的程式碼是儘量通過命名讓使用者理解和使用。(畢竟同時維護大量文件和程式碼是個難事)

總結一句就是:駝峰+邏輯+型別。

引數使用

var doSomething = function(obj) {
    var _adapter = {
        name : `xioaming`,
        titile : `xiaoming`,
        age : 24,
        color : `pink`,
        size : 100
    }

    for (var i in _adapter) {
        _adapter[i] = obj[i] || _adapter[i];
    }

    //dosomething
}
export function signalLight(data) {
  const sign = data.slice();
}
  • 對傳入的引數應該儘量拆卸,以免使用者傳參屬性變更。(屬性較多時,考慮使用介面卡模式)

  • 變數的使用應盡力保證函式是純函式。對傳參deepClone/slice,不修改外部變數。

函式宣告

export function singalLamp(singalArr) {
    function tic(singal, time) {
        return () => new Promise((res) => setTimeout(() => {
            console.log(singal);
            res();
        }, time));
    }

    const rawArr = singalArr.slice();
}    

函式的宣告/定義有A:function test() {} ; B:const test = function() {}; C:const test = () => {},那麼他們有神麼區別?

A方式:函式會提升,提升意味著在該作用域(scope)任何位置都可以使用。知道了這些,我們可以得出一個結論:使用該方式函式必須是純函式。

B/C方式:函式不會提升,此種方式一般定義一個非純函式,非純函式(這裡指依賴於外部的變數)提升了也沒有意義。因為它要依賴於上下文,即呼叫變數的初始化。

C方式:C方式中是一個箭頭函式,不免讓我們思考為什麼箭頭函式沒有function test() {}這種會提升的定義方式呢? 答案是箭頭函式自身的特性(this指向依賴詞法/靜態作用域),這使得箭頭函式的提升沒有意義。

函式使用

export function singalLamp(singalArr) {
    function tic(singal, time) {
        return () => new Promise((res) => setTimeout(() => {
            console.log(singal);
            res();
        }, time));
    }

    const rawArr = singalArr.slice();
    const lampArr = rawArr.reduce(function(prev, item) {
        return prev.concat([tic(item, 1000)]);
    }, []);
    const step = function(iterator) {
        if (iterator === lampArr.length) {
            return step(0);
        } else {
            return () => lampArr[iterator]().then(step(++iterator));
        }
    }

    step(0)();
}

singalLamp([`red`, `green`, `yellow`]);

函式的使用主要有兩種:

  • 閉包

閉包的本質是(對共享變數的操作,典型運用是觀察者模式/備忘錄模式)

  • 普通

封裝,複用。(當我門對一段邏輯不需要複用時,我們仍將它寫成函式的動機是:細顆粒化邏輯)

效能優化

在Web開發過程中,可以進行效能優化的方面多如牛毛,筆者在這裡介紹幾處切合本文主題的優化方式。即js的高效能程式碼書寫(參考編寫高效能的JS程式碼),這裡簡單說幾個:

  • i++與++i

使用字首自增表示式,也能帶來小小的效能提升。(++i代替i++)

  • 閉包

雖然上文介紹了閉包的實用性,但是還是應該儘量避免使用閉包,它就和remove dom一樣讓人詬病。(垃圾回收問題,記憶體)

  • const與let

就 let 而言,他的使用場景應該是相對較少的,我們只會在 loop(for,while 迴圈)及少量必須重定義的變數上用到他。

猜想:就執行效率而言,const 由於不可以重新賦值的特性,所以可以做更多語法靜態分析方面的優化,從而有更高的執行效率。(參考阿里FED部落格ES6 你可能不知道的事 – 基礎篇)

總結

要寫好一個專案需要相容,效能,安全等。寫好一個功能需要設計模式和解耦需求。寫好一個函式需要考慮[`對比技術點`,`變數命名`,`引數使用`,`函式宣告`,`函式使用`].join(`+`);

其他

個人部落格歡迎交流共勉成長

相關文章