前段時間,在做阿里前端筆試題的時候,遇到了這樣一道編程題,題目內(nèi)容如下:
JSON.stringify 的功能是,將一個 JavaScript 字面量對象轉(zhuǎn)化為一個 JSON 格式的字符串。例如
const obj = {a:1, b:2} JSON.stringify(obj) // => '{"a":1,"b":2}'當要轉(zhuǎn)化的對象有“環(huán)”存在時(子節(jié)點屬性賦值了父節(jié)點的引用),為了避免死循環(huán),JSON.stringify 會拋出異常,例如:
const obj = { foo: { name: 'foo', bar: { name: 'bar' baz: { name: 'baz', aChild: null //待會讓它指向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請完善以下“環(huán)”檢查器函數(shù) cycleDetector,當入?yún)ο笾杏协h(huán)時返回 true,否則返回 false。
function cycleDetector(obj) { // 請?zhí)砑哟a }
“環(huán)”的形成是因為對象的子節(jié)點屬性賦值了父節(jié)點的引用,所以我們需要記錄下父節(jié)點的地址,然后再拿其子節(jié)點的屬性與之前記錄下的父節(jié)點地址做一個比較,當結(jié)果一致時,就形成了“環(huán)”。
那么,在“環(huán)”檢測器函數(shù)中,我們首先需要遍歷這個對象的屬性,前面提到了引用,那么我們只需對Object類型的屬性進行處理即可(簡單數(shù)據(jù)類型不存在引用關(guān)系)。對于Objcet類型的屬性,我們先與記錄下來的父節(jié)點地址做一個對比,如無匹配項,將當前屬性地址記錄下來,然后遍歷其子節(jié)點屬性,一層一層找下去。下面開始上代碼:
function cycleDetector(obj) {
var hasCircle = false, // 定義一個變量,標志是否有環(huán)
cache = []; // 定義一個數(shù)組,來保存對象類型的屬性值
(function(obj) {
var keys = Object.keys(obj); //獲取當前對象的屬性數(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ù),因為遞歸返回,后面遍歷的屬性不是這個數(shù)據(jù)的子屬性
}
}
}
})(obj)
return hasCircle
}
測試用例
/* 測試一 */
const obj = {
foo: {
name: 'foo',
bar: {
name: 'bar'
baz: {
name: 'baz',
aChild: null //待會讓它指向obj.foo
}
}
}
}
obj.foo.bar.baz.aChild = obj.foo // foo->bar->baz->aChild->foo 形成環(huán)
/* 測試二 */
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)