今晚我加的一個前端群里有人問了這樣一個問題,下面這段代碼在Chrome中運行:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script>
var a = {name: '1'};
console.log(a);
a.name = '2';
console.log(a);
</script>
</body>
</html>
打開控制台后,卻發現運行結果是這樣的:
Object
name: "2"
__proto__: Object
Object
name: "2"
__proto__: Object
這個結果不符合我們所期望的第一個輸出是name: "1"。
難道console.log是異步的?
可是當我將那段JavaScript代碼粘貼到控制台直接運行,或是打開控制台后刷新一遍網頁,運行結果就會變成:
Object {name: "1"}
Object {name: "2"}
這正是我們所期望的結果。
為什么會這樣?
因為代碼在運行的時候控制台沒有打開。
首先明確一點,a所儲存的是一個引用類型值的地址,所有對a的操作都會具體到這個地址所對應的那個對象上。其次,console並不是JavaScript提供的對象,而是瀏覽器的控制台提供的。這具體到不同的瀏覽器,比如Chrome中是由Devtool的控制台提供,Firefox中是由Firebug的控制台提供。
在Chrome中,console.log在控制台打開后才起作用,也就是說,當你打開控制台時,console.log才會將之前被傳進去的參打印出來。
那么問題來了,在上述代碼中,傳進console.log中的參是一個地址,當代碼執行完畢后,打開控制台,console.log開始起作用,那么它打印出的實際上是已經做完全部處理后的對象。這就相當於這樣的執行順序:
var a = {name: '1'};
a.name = '2';
console.log(a);
console.log(a);
如果是打開控制台再運行代碼,那么console.log是直接起作用的,代碼會順序執行,所以所看到的結果是符合期望的。
那么該如何解決這個問題?只能在執行的過程中創建新對象來曲線救國了:
var a = {name: '1'};
console.log(JSON.parse(JSON.stringify(a)));
a.name = '2';
console.log(JSON.parse(JSON.stringify(a)));
這相當於幫瀏覽器對要被改變的對象存了個快照。
當然,只要你開着控制台來執行代碼,那么是不會出現這樣的問題的。
最后我認為這不是個BUG,相反這是個節省運算資源的行為。無論控制台是否打開console.log都起作用是十分不妥的。
開着控制台還會出現不符合期望的結果才是BUG,然而它早就被修復了。
