一道賦值面試題引發的思考

Jsonz發表於2018-07-31

部落格地址

github 歡迎start follow

本篇主要說一些基礎知識點,關於多項賦值順序,物件引用等,期間插入一點es6只是以及解決問題的思路。

開頭先來做一道面試題

part1

var a={n:1};
var b=a;
a.x=a={n:2};
console.log(a.x);
console.log(b.x);
複製程式碼

最後輸出的是什麼? 先不說答案,我們來分析一下

L2(第二行) 我們把a賦值給b, 由於a是物件型別,這就意味著b和a指向同一個記憶體地址

L3 a.x = a = { n: 2}

這裡我們有個疑惑,這句語句執行順序是 a = {n: 2} && a.x = {n:2} 還是 a.x = {n:2} && a= {n:2} 還是這種 a = {n: 2} && a.x = a

我們這裡可以藉助 Object.defineProperty 或 ES6的 Proxy來驗證多項賦值的順序是怎樣的

const obj = new Proxy({}, {
  set(target, key, value, r) {
    console.log(key, value)
    if (key === 'a') Reflect.set(target, key, 'isA', r);
    else Reflect.set(target, key, value, r);
  }
});

obj.b = obj.a= {n: 1};
// 輸出:
// "a" {n: 1}
// "b" {n: 1}

obj.a; // isA
obj.b; // {n: 1}
複製程式碼

所以我們可以得出 賦值的順序是從右邊開始到左邊的。而且是直接 a = {n: 1}, a.x = {n:1 },而不是 a.x = a 這樣去賦值

現在我們再借助 Proxy 來分析一開始part1這道題,用obj.a, obj.b 來代替原題目的 a和b。

var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}!`);
    return Reflect.set(target, key, value, receiver);
  }
});

obj.a = {n: 1 };// getting a;
obj.b = obj.a; // getting a; setting b;
obj.a.x = obj.a = {n:2 }; // getting a; setting a;
複製程式碼

可以看到 obj.a.x = obj.a = {n: 2}這段語句執行時,會先輸出一個 getting a 再輸出 setting a

這就意味著在對 obj.a.x 賦值時,程式是先獲取 obj.a指向的物件的記憶體地址,此時觸發了 getting a,然後再對右邊 obj.a 進行賦值,觸發了 setting a, 賦值完最後一步才是對 obj.a.x賦值 {n:2 }

重點: 在對obj.a.x賦值的時刻已經獲取了obj.a該物件指向的記憶體地址,所以後面a就算指向其他地址,也和這裡的obj.a.x無關。此時指向該地址的還有obj.b

我們再用三張圖來捋一捋整理的思路

執行 obj.a = {n: 1}; obj.b = obj.a後obj對應的引用是這樣的 一道賦值面試題引發的思考

執行 obj.a.x = xxx一道賦值面試題引發的思考

執行obj.a.x = obj.a = {n:2}一道賦值面試題引發的思考

至此,這道面試題相信大家都有答案了,可以自己去控制檯驗證一下。 假如這時候再執行 obj.a.n = 3, 列印obj.b會輸出什麼呢?

part2

接下來我們來看另一道題,關於物件迴圈引用的

var a = { n: 1}
a.b = a;
複製程式碼

這裡的a明顯是迴圈引用,那麼我們要怎樣才能判斷一個物件是否是迴圈引用呢?

其實這道題我一開始除了遞迴判斷外沒有很好的解決方案,後面是群裡一個叫話費的大佬說(這道題也是他出的)直接用 JSON.stringify,微信小遊戲的原始碼裡面就是這麼去判斷。

JSON.stringify 如果遇到引數裡有迴圈引用的,就會丟擲一個迴圈呼叫的錯誤 Uncaught TypeError: Converting circular structure to JSON

那如果不用JSON.stringify或者想要自己實現一個去檢測迴圈呼叫,該怎麼寫呢?(面試官和部門前端leader最喜歡這麼問)

一般遇到這種,最簡單的方法就是去找這個方法的 polyfill, json3。我找的是 json3的 polyfill 裡面大概是遍歷物件存到stack陣列,再在解析的時候去判斷是否有迴圈引用的情況。 json3.js#L482

照著他的思路大概寫了一個,其實就是前面說到的簡單遞迴判斷

var stack = [];
function fn(obj) {
    var len;
    for (len = stack.length; len--;) {
        if (stack[len] === obj) throw TypeError()
    }
    stack.push(obj);
    for (let k in obj) {
        const value = obj[k]
        if (typeof value === 'object') fn(value);
    }
}
複製程式碼

最後第一次講關於語言知識點的,如果發現有錯誤的地方 歡迎指出~

謝謝惠顧,請笑納

相關文章