前段時(shí)間,在做阿里前端筆試題的時(shí)候,遇到了這樣一道編程題,題目?jī)?nèi)容如下:
JSON.stringify 的功能是,將一個(gè) JavaScript 字面量對(duì)象轉(zhuǎn)化為一個(gè) JSON 格式的字符串。例如
const obj = {a:1, b:2} JSON.stringify(obj) // => '{"a":1,"b":2}'當(dāng)要轉(zhuǎn)化的對(duì)象有“環(huán)”存在時(shí)(子節(jié)點(diǎn)屬性賦值了父節(jié)點(diǎn)的引用),為了避免死循環(huán),JSON.stringify 會(huì)拋出異常,例如:
const obj = { foo: { name: 'foo', bar: { name: 'bar' baz: { name: 'baz', aChild: null //待會(huì)讓它指向obj.foo } } } } obj.foo.bar.baz.aChild = obj.foo // foo->bar->baz->aChild->foo 形成環(huán) JSON.stringify(obj) // => TypeError: Converting circular structure to JSON請(qǐng)完善以下“環(huán)”檢查器函數(shù) cycleDetector,當(dāng)入?yún)?duì)象中有環(huán)時(shí)返回 true,否則返回 false。
function cycleDetector(obj) { // 請(qǐng)?zhí)砑哟a }
“環(huán)”的形成是因?yàn)閷?duì)象的子節(jié)點(diǎn)屬性賦值了父節(jié)點(diǎn)的引用,所以我們需要記錄下父節(jié)點(diǎn)的地址,然后再拿其子節(jié)點(diǎn)的屬性與之前記錄下的父節(jié)點(diǎn)地址做一個(gè)比較,當(dāng)結(jié)果一致時(shí),就形成了“環(huán)”。
那么,在“環(huán)”檢測(cè)器函數(shù)中,我們首先需要遍歷這個(gè)對(duì)象的屬性,前面提到了引用,那么我們只需對(duì)Object類(lèi)型的屬性進(jìn)行處理即可(簡(jiǎn)單數(shù)據(jù)類(lèi)型不存在引用關(guān)系)。對(duì)于Objcet類(lèi)型的屬性,我們先與記錄下來(lái)的父節(jié)點(diǎn)地址做一個(gè)對(duì)比,如無(wú)匹配項(xiàng),將當(dāng)前屬性地址記錄下來(lái),然后遍歷其子節(jié)點(diǎn)屬性,一層一層找下去。下面開(kāi)始上代碼:
function cycleDetector(obj) {
var hasCircle = false, // 定義一個(gè)變量,標(biāo)志是否有環(huán)
cache = []; // 定義一個(gè)數(shù)組,來(lái)保存對(duì)象類(lèi)型的屬性值
(function(obj) {
var keys = Object.keys(obj); //獲取當(dāng)前對(duì)象的屬性數(shù)組
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = obj[key];
if (typeof value == 'object' && value !== null) {
var index = cache.indexOf(value)
if (index !== -1) {
hasCircle = true
break
} else {
cache.push(value)
arguments.callee(value)
cache.pop() // 注意:這里要推出數(shù)據(jù),因?yàn)檫f歸返回,后面遍歷的屬性不是這個(gè)數(shù)據(jù)的子屬性
}
}
}
})(obj)
return hasCircle
}
測(cè)試用例
/* 測(cè)試一 */
const obj = {
foo: {
name: 'foo',
bar: {
name: 'bar'
baz: {
name: 'baz',
aChild: null //待會(huì)讓它指向obj.foo
}
}
}
}
obj.foo.bar.baz.aChild = obj.foo // foo->bar->baz->aChild->foo 形成環(huán)
/* 測(cè)試二 */
var obj = {
foo: {
name: 'foo',
bar: {
name: 'bar',
baz: {
name: 'baz',
aChild: null
}
}
},
aaa: {
name: "test",
bbb: null
}
}
obj.aaa.bbb = obj.foo; // aaa->bbb->bar->baz->aChild->null 不形成環(huán)