在開發過程中踩了一個坑,覺得挺有意思,就順手編成了一道題。
// 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語法,相對來說這些也是比較新的東西。
為什麼人與人之間差別就那麼大呢?同一個技術點對有的人來說是不必多說的基礎,對於另一些人來說卻是天方夜譚…