糾結的連等賦值


  偶的看到一段有意思的代碼:

var a = {n: 1};
a.x = a = {n: 2};
console.log(a.x);

  作為一個熱衷於“鑽牛角尖”的人,樓主對這樣的代碼很感興趣,也不禁陷入了思考。so也不要說寫這樣的代碼難維護啥的,純粹為了思考邏輯。

  首先,這是一個連等賦值,而且賦值的是個對象。如果是基本的JavaScript類型,不存在引用,也就不會有類似的問題,但是邏輯是一樣的。

  

  賦值過程是怎么樣的呢?以上的翻譯是,b先賦值5,然后(b=5)會返回所賦的值(也就是5),然后再賦值給a,也就是:

a = (b = 5);

  所以最初的代碼,可以修改成:

var a = {n: 1};
a.x = (a = {n: 2});    // modified here
console.log(a.x);

  如果是這樣,那么以下代碼有什么區別:

var a = {n: 1};
// a.x = (a = {n: 2});    // modified here
a = {n: 2};
a.x = a;
console.log(a.x);

  很明顯,結果不一樣,繼續了解下JavaScript的運算順序。

var a = 10;        
function fn() {
    a = 20;
    return 20;
}

var b = a + fn();
console.log(b);

  輸出30,我們猜測JavaScript是從左到右運算的(事實也確實如此)。先取出a在內存中的值(10),然后執行fn函數,返回20(同時改變了a的值),10+20=30。

  如果運算過程中有括號啥的,是不是會“智能”地改變運算順序呢?

var a = 10;        
function fn() {
    a = 20;
    return 20;
}

var b = a + (fn() * 2);
console.log(b);

  輸出50,還是從左到右,沒有智能地先運算括號里的內容,然后改變a的值,再和a相加。這在JavaScript內核中是怎么實現的?這個我也沒有研究過,但是我知道C++等語言是沒有這么“智能”的,可以通過堆棧來模擬,比如簡單計算器

  再回頭看這道題,聲明一個變量a,指向一個對象{n: 1},然后執行連等。可以肯定的是,a.x和a指向的是同一塊內存,我們從左到右運算,a.x實際操作的是a指向的對象,發現沒有x這個key,那么就會自動添加,它的值等待右邊的運算結果。然后a重新引用了一個對象,並且之前的{n:1}這個對象新的key(x)的value也引用到同一個對象上。大概過程如下圖,過程2我把它理解為等待,等待過程3中a的運算結果。

  如果要說的稍微“專業”一點,連等是先確定所有變量的指針,再讓指針指向那個賦值

  事實上,解析器在接受到 a.x = a = {n:2} 這樣的語句后,會這樣做:

  1. 找到 a 和 a.x 的指針。如果已有指針,那么不改變它。如果沒有指針,即那個變量還沒被申明,那么就創建它,指向 null。
    a 是有指針的,指向 {n:1};a.x 是沒有指針的,所以創建它,指向 null。
  2. 然后把上面找到的指針,都指向最右側賦的那個值,即 {n:2}

  如果理解了,試試這道題吧:

var a, b, c, d;
a = b = c = d = {a: 1};
a.x = a = b.y = b = c.z = c = {}
console.log(a, b, c, d);

 

參考:

  1. Operator_Precedence
  2. javascript 連等賦值問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM