[js]一道快取類面試題

一桶冷水發表於2018-03-07

在開發過程中踩了一個坑,覺得挺有意思,就順手編成了一道題。

// Cache是一個快取類,使用時先在new方法中註冊獲取資料的方法,然後可通過get方法獲取資料,
// 並且只有第一次呼叫get會真正呼叫new中註冊的方法獲取資料,以後都直接從快取中返回。

function Cache () {
    this.store = {}
}

Cache.prototype.new = function (name, fn) {
    if (!name || !fn || typeof fn !== 'function') {
        return
    }
    this.store[name] = {name, fn, data: {}}
}

Cache.prototype.get = function (name, key) {
    const self = this.store[name]
    key = key || 1
    if (self.data[key]) {
        return Promise.resolve(self.data[key])
    }
    return self.fn(key).then(data => {
        self.data[key] = data
        return data
    })
}

Cache.prototype.clear = function (name, key) {
    this.store[name].data[key] = null
}

Cache.prototype.clearAll = function (name) {
    this.store[name].data = {}
}

// 1.下面的程式碼說明Cache的實現存在一個bug,嘗試修復它

const c = new Cache()

c.new('foo', function (key) {
    return Promise.resolve([1])
})

c.get('foo').then(
    list0 => {
        console.log(list0)
        list0.push(2)
        return c.get('foo')
    }).then(
    list1 => {
        console.log(list1)
        list1.push(3)
        return c.get('foo')
    }).then(
    list2 => {
        console.log(list2)
    })

// 2.對以上程式碼提出一些改進意見
複製程式碼

以下是解答:

  • 作為一個快取類,每次讀取到的資料應該是相同的,顯然這裡並不是。那麼是哪裡出了問題?在js中如果返回值不是基礎型別(如Number,String)返回的會是一個物件引用,而這裡正是返回了快取物件的引用,才導致呼叫者可以隨意更改快取的內容。怎麼解決呢?拷貝後再返回,切斷快取與返回值間的關聯,而且必須是深拷貝才能徹底切斷。考慮使用場景是獲取後端資料,這個地方的用json序列化實現。

  • get方法的引數key有預設值1,這個邏輯並不正確。如果key為空會導致呼叫new中註冊的資料獲取方法時錯誤地傳入key的預設值1。

Cache.prototype.get = function (name, key) {
  const self = this.store[name]
  if (self.data[key]) {
    return Promise.resolve(JSON.parse(self.data[key]))
  }
  return self.fn(key).then(data => {
    self.data[key] = JSON.stringify(data)
    return data
  })
}
複製程式碼

我在剛編出這道的時候覺得不過就是一個考察return返回引用的題目,並不多難,但是在問過幾個人以後才發現沒那麼簡單。首先需要對使用原型鏈構造類有基本的瞭解,對於那些如果是僅僅只是實現一下業務邏輯,不做任何抽象和封裝的ctrl+v工程師而言恐怕確實不需要懂。其次是Promise和ES6語法,相對來說這些也是比較新的東西。

為什麼人與人之間差別就那麼大呢?同一個技術點對有的人來說是不必多說的基礎,對於另一些人來說卻是天方夜譚…

相關文章