在開(kāi)發(fā)過(guò)程中踩了一個(gè)坑,覺(jué)得挺有意思,就順手編成了一道題。
// Cache是一個(gè)緩存類(lèi),使用時(shí)先在new方法中注冊(cè)獲取數(shù)據(jù)的方法,然后可通過(guò)get方法獲取數(shù)據(jù),
// 并且只有第一次調(diào)用get會(huì)真正調(diào)用new中注冊(cè)的方法獲取數(shù)據(jù),以后都直接從緩存中返回。
function Cache () {
this.store = {}
}
Cache.prototype.add = 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.下面的代碼說(shuō)明Cache的實(shí)現(xiàn)存在一個(gè)bug,嘗試修復(fù)它
const c = new Cache()
c.add('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.對(duì)以上代碼提出一些改進(jìn)意見(jiàn)
// 3.當(dāng)同時(shí)對(duì)同一個(gè)name,同一個(gè)key發(fā)起多個(gè)get,且緩存不存在時(shí),會(huì)導(dǎo)致fn多次執(zhí)行,優(yōu)化這個(gè)問(wèn)題
以下是解答:
作為一個(gè)緩存類(lèi),每次讀取到的數(shù)據(jù)應(yīng)該是相同的,顯然這里并不是。那么是哪里出了問(wèn)題?在js中如果返回值不是基礎(chǔ)類(lèi)型(如Number,String)返回的會(huì)是一個(gè)對(duì)象引用,而這里正是返回了緩存對(duì)象的引用,才導(dǎo)致調(diào)用者可以隨意更改緩存的內(nèi)容。怎么解決呢?拷貝后再返回,切斷緩存與返回值間的關(guān)聯(lián),而且必須是深拷貝才能徹底切斷。考慮使用場(chǎng)景是獲取后端數(shù)據(jù),這個(gè)地方的用json序列化實(shí)現(xiàn)。
get方法的參數(shù)key有默認(rèn)值1,這個(gè)邏輯并不正確。如果key為空會(huì)導(dǎo)致調(diào)用new中注冊(cè)的數(shù)據(jù)獲取方法時(shí)錯(cuò)誤地傳入key的默認(rèn)值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
})
}
我在剛編出這道的時(shí)候覺(jué)得不過(guò)就是一個(gè)考察return返回引用的題目,并不多難,但是在問(wèn)過(guò)幾個(gè)人以后才發(fā)現(xiàn)沒(méi)那么簡(jiǎn)單。首先需要對(duì)使用原型鏈構(gòu)造類(lèi)有基本的了解,對(duì)于那些如果是僅僅只是實(shí)現(xiàn)一下業(yè)務(wù)邏輯,不做任何抽象和封裝的ctrl+v工程師而言恐怕確實(shí)不需要懂。其次是Promise和ES6語(yǔ)法,相對(duì)來(lái)說(shuō)這些也是比較新的東西。
為什么人與人之間差別就那么大呢?同一個(gè)技術(shù)點(diǎn)對(duì)有的人來(lái)說(shuō)是不必多說(shuō)的基礎(chǔ),對(duì)于另一些人來(lái)說(shuō)卻是天方夜譚…