JS怎樣判斷一個(gè)對(duì)象是否存在"環(huán)"?

前段時(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 一、let 和 constlet:變量聲明, const:只讀常量聲明(聲明的時(shí)候賦值)。 let 與 var 的...
    dadage456閱讀 824評(píng)論 0 0
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 6,689評(píng)論 3 22
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 3,720評(píng)論 2 27
  • 基本用法 ES6 允許按照一定模式,從數(shù)組和對(duì)象中提取值,對(duì)變量進(jìn)行賦值,這被稱(chēng)為解構(gòu)(Destructuring...
    嘉奇呦_nice閱讀 823評(píng)論 0 2

友情鏈接更多精彩內(nèi)容