84669 人學習
152542 人學習
20005 人學習
5487 人學習
7821 人學習
359900 人學習
3350 人學習
180660 人學習
48569 人學習
18603 人學習
40936 人學習
1549 人學習
1183 人學習
32909 人學習
#這是抽出來的兩行程式碼 var name = Symbol('test') 一直提示 無法轉換,是關鍵字保留?還是其他原因? 為什麼換一個var name1 = Symbol('test')卻可以透過編譯? 其他普通的var s1 s2也可透過編譯。
var name = Symbol('test')
var name1 = Symbol('test')
var s1 s2
這問題挺有意思的,我也從來沒有註意到,翻了翻資料,發現原來是很多事情巧合的湊合到一塊兒,然後出現了這個問題。精確的來說,是瀏覽器的預設行為和JavaScript的隱式類型變換搗的鬼。
瀏覽器的預設行為
隱式類型變換
一點一點來,首先,var和let的差別在哪裡?
var
let
var宣告的變數會被提升到目前函數作用域頂端,如果是在全域那麼這個變數將會成為window的一個屬性。 而對於let聲明的變量,它會將變量提升至當前塊級作用域,並且如果是在全局,當前變量也不會成為window的屬性。
window
所以,在全局會出現這樣的事情:
var test1 = 'test1'; let test2 = 'test2'; console.log(window.test1); // test1 console.log(window.test2); // undefined
然後,name為名的變數和別的變數有什麼差別? 上面我們知道了,var name = 'test1';其實可以等同於window.name = 'test1',很容易就能想到,name是不是固定的保留字?
name
var name = 'test1';
window.name = 'test1'
翻翻規範,還真是的。 window.name屬性表示的是目前視窗上下文的名稱。下面是window的部分介面:
window.name
[ReplaceableNamedProperties] interface Window { // the current browsing context readonly attribute WindowProxy window; readonly attribute WindowProxy self; readonly attribute Document document; attribute DOMString name; //.. }
name屬性在這裡的最後一行,沒有readonly的前綴,說明它是可讀可寫的,它的資料型別則是DOMString。 DOMString是指UTF-16的字串,在JavaScript中它會直接對應到String。
readonly
DOMString
String
所以當我們給window.name賦值的時候,這個值會被強制轉換為String。
我們可以試試看:
var name = { a:1, b:2 }; console.log(window.name); // [object Object] var name = [0, 1, 2, 3]; console.log(window.name); // 0,1,2,3
到了這裡大概就能猜到,var name = Symbol('test');的錯誤,應該是Symbol變數在做型別轉換的時候出了問題。而實際報的錯誤也證實了我們的猜測:TypeError: Cannot convert a Symbol value to a string。
var name = Symbol('test');
Symbol
TypeError: Cannot convert a Symbol value to a string
但是,似乎不太對,Symbol變數是可以轉換成字串的啊,例如:
let test = Symbol('test'); console.log(test.toString()); // Symbol(test) console.log(String(test)); // Symbol(test)
嘛,這就是比較老生常談的東西了,JavaScript的隱式型別變換和顯式的強制轉換對於部分變數是不同的。很不幸,在這裡Symbol就是這麼一類。
Symbol被隱式的轉換時,它會先呼叫其內部的ToPrimitive接口,拿到其原始值,然後在其中再調用ToString函數轉換為字串。注意,這裡的這個ToString函數是其內部的抽象方法,和暴露在外的Symbol.prototype.toString()不是一個東西。
ToPrimitive
ToString
Symbol.prototype.toString()
對於Symbol變數而言,當其調用ToString的時候就會報錯,更詳細的我就不展開了,有興趣的可以自己看看規範:ToString ( argument )。
我剛剛也在控制台試了一下,確實是個很神奇的BUG,不過你將
var name = Symbol("test"); //改成 let name = Symbol("test"); //试试。。
然後驚訝的發現BUG又沒了。 。我猜跟瀏覽器是怎麼解析文法有關,但這些東西我也不懂啊。
name是window的特有屬性,如果你換個變數試試看就不會報錯了。 。 。
name 是 window 的特有屬性,在全域環境下定義的name變量,賦任何值都會自動轉換成字串,而Symbol類型不能直接轉換為字串,所以報錯了。
你可以
var name = 1; console.log(name);
就知道了。
這問題挺有意思的,我也從來沒有註意到,翻了翻資料,發現原來是很多事情巧合的湊合到一塊兒,然後出現了這個問題。精確的來說,是
瀏覽器的預設行為
和JavaScript的隱式類型變換
搗的鬼。一點一點來,首先,
var
和let
的差別在哪裡?var
宣告的變數會被提升到目前函數作用域頂端,如果是在全域那麼這個變數將會成為window
的一個屬性。而對於
let
聲明的變量,它會將變量提升至當前塊級作用域,並且如果是在全局,當前變量也不會成為window
的屬性。所以,在全局會出現這樣的事情:
然後,
name
為名的變數和別的變數有什麼差別?上面我們知道了,
var name = 'test1';
其實可以等同於window.name = 'test1'
,很容易就能想到,name
是不是固定的保留字?翻翻規範,還真是的。
window.name
屬性表示的是目前視窗上下文的名稱。下面是window
的部分介面:name
屬性在這裡的最後一行,沒有readonly
的前綴,說明它是可讀可寫的,它的資料型別則是DOMString
。DOMString
是指UTF-16的字串,在JavaScript中它會直接對應到String
。所以當我們給
window.name
賦值的時候,這個值會被強制轉換為String
。我們可以試試看:
到了這裡大概就能猜到,
var name = Symbol('test');
的錯誤,應該是Symbol
變數在做型別轉換的時候出了問題。而實際報的錯誤也證實了我們的猜測:TypeError: Cannot convert a Symbol value to a string
。但是,似乎不太對,
Symbol
變數是可以轉換成字串的啊,例如:嘛,這就是比較老生常談的東西了,JavaScript的隱式型別變換和顯式的強制轉換對於部分變數是不同的。很不幸,在這裡
Symbol
就是這麼一類。Symbol
被隱式的轉換時,它會先呼叫其內部的ToPrimitive
接口,拿到其原始值,然後在其中再調用ToString
函數轉換為字串。注意,這裡的這個ToString
函數是其內部的抽象方法,和暴露在外的Symbol.prototype.toString()
不是一個東西。對於
Symbol
變數而言,當其調用ToString
的時候就會報錯,更詳細的我就不展開了,有興趣的可以自己看看規範:ToString ( argument )。我剛剛也在控制台試了一下,確實是個很神奇的BUG,不過你將
然後驚訝的發現BUG又沒了。 。我猜跟瀏覽器是怎麼解析文法有關,但這些東西我也不懂啊。
name是window的特有屬性,如果你換個變數試試看就不會報錯了。 。 。
name 是 window 的特有屬性,在全域環境下定義的name變量,賦任何值都會自動轉換成字串,而Symbol類型不能直接轉換為字串,所以報錯了。
你可以
就知道了。