亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

有關(guān)javascript強(qiáng)制轉(zhuǎn)換不得不說(shuō)的故事

xcold / 1824人閱讀

摘要:我們首先了解一下中有關(guān)類型轉(zhuǎn)換的知識(shí)。新增類型拋出異常從列表可以明顯看到少了一個(gè)類型轉(zhuǎn)換為的規(guī)則。這里要強(qiáng)調(diào)一點(diǎn)第二個(gè)表達(dá)式?jīng)]有涉及到強(qiáng)制類型轉(zhuǎn)換。如果文中有錯(cuò)誤或者有某些強(qiáng)制轉(zhuǎn)換的情形沒(méi)有涉及到請(qǐng)及時(shí)留言告知,我會(huì)修改并補(bǔ)充進(jìn)去。

javascript是一門(mén)非常奇特的語(yǔ)言,它有時(shí)候奇特的會(huì)讓人懷疑人生。比如讓我們看一下下面的一些奇葩例子:

 false == "0"           //true   "哇"
 false == 0             //true   "哦"
 false == ""            //true   "噢"
 false == []            //true   "啥?"
 
 0 == ""                //true   "what?"
 0 == []                //true   
 0 == "0"               //true   
 [] == "0"              //false  "why?"
 [] == ""               //true   
 
 //-----------更驚訝的是---------------

 [] == ![]              //true    "WTF!"
 [2] == 2               //true  
 "" == [null]           //true 
 0 == "
"              //true     我還能說(shuō)什么呢?
 false == "
"          //true

還有許多可以列出來(lái)嚇你一跳的例子,別懷疑我是隨便編出來(lái)騙你的。當(dāng)時(shí)我在瀏覽器運(yùn)行這些時(shí),我都懷疑我以前學(xué)得是假的js。如果要形容我當(dāng)時(shí)的表情的話,你想一下黑人小哥的表情就能明白我當(dāng)時(shí)是有多懷疑人生。
好,現(xiàn)在讓我們先喝杯水壓壓驚,暫時(shí)忘記前面那些奇葩的例子。我們首先了解一下js中有關(guān)類型轉(zhuǎn)換的知識(shí)。

類型轉(zhuǎn)換

學(xué)過(guò)js的應(yīng)該都了解js是一門(mén)弱類型語(yǔ)言。你在聲明一個(gè)變量的時(shí)候沒(méi)有告訴它是什么類型,于是在程序運(yùn)行時(shí),你可能不知不覺(jué)中就更改了變量的類型??赡苡行┦悄愎室飧牡?,另一些可能并不是你的本意,但是不管怎樣你都不可避免的會(huì)遇到類型轉(zhuǎn)換(強(qiáng)制或隱含)。讓我們看一下下面的列子:

  var a = "1";
  var b= Number(a);               // b=1; 
  +a;                             // 1;
  b + "";                         // "1";    
     

大家應(yīng)該都知道答案,很多人在代碼中或多或少都會(huì)用到這些方法,并且都明白其中發(fā)生了值的類型轉(zhuǎn)換,但是你們是否有深入了解js內(nèi)部在類型轉(zhuǎn)換時(shí)做了哪些操作呢?

ToBoolean(argument)

我們首先來(lái)了解強(qiáng)制轉(zhuǎn)換為Boolean類型時(shí),發(fā)生了什么操作。在用調(diào)用Boolean(a)或者!a等操作將值轉(zhuǎn)換為Boolean類型時(shí),js內(nèi)部會(huì)調(diào)用ToBoolean方法來(lái)進(jìn)行轉(zhuǎn)換,該方法定義了以下規(guī)則:

argument的類型 轉(zhuǎn)換的結(jié)果
Undefined false
Null false
Boolean argument
Number 如果argument是 +0、-0、NaN, 返回false; 否則返回true.
String 如果arguments是空字符串(長(zhǎng)度為0)返回false,否則返回true
Object true
Symbol(ES6新增類型) true

從這個(gè)列表中我們簡(jiǎn)單概括一下就是只要argument的值是(undefined、null、+0、-0、NaN、""(空字符串)以及false))這7個(gè)里的其中一個(gè),那轉(zhuǎn)換之后返回的是false,其他都為true。js專門(mén)把這7個(gè)值放到一個(gè)falsy列表中,其余值都放在truthy列表。

ToNumber(argument)

ToNumber顧名思義即把其它類型轉(zhuǎn)換為Number類型(js內(nèi)部調(diào)用的方法,外部無(wú)法訪問(wèn)到),ECMAScript官方也專門(mén)給出了轉(zhuǎn)換規(guī)則:

argument的類型 轉(zhuǎn)換的結(jié)果
Undefined NaN
Null +0
Boolean false為+0,true為1
Number 返回argument
Object 執(zhí)行以下步驟:讓primValue成為T(mén)oPrimitive(argument, hint Number)的返回值,再調(diào)用ToNumber(primValue)返回。
Symbol(ES6新增類型) 拋出TypeError異常.

從列表可以明顯看到少了一個(gè)String類型轉(zhuǎn)換為Number的規(guī)則。因?yàn)镾tring轉(zhuǎn)Number,js內(nèi)部有非常復(fù)雜的判斷,我這里面不詳細(xì)說(shuō)轉(zhuǎn)換的細(xì)節(jié),有興趣的可以看一ECMAScript官方的說(shuō)明。只要知道它與確定Number字面量值的算法相似,但是要注意一下細(xì)節(jié):

一個(gè)空(empty)的或只包含空格的字符串被轉(zhuǎn)換為+0。

StrWhiteSpace會(huì)轉(zhuǎn)化為+0

StrNumericLiteral前后的StrWhiteSpace會(huì)被忽略。

StrNumericLiteral前面的多個(gè)0會(huì)被忽略。

不是StringNumericLiteral的擴(kuò)展會(huì)變?yōu)镹aN。

在這里特別說(shuō)明一下
StrWhiteSpace:在js中StrWhiteSpace包含WhiteSpace(空白符)和LineTerminator(終止符)。
StrNumericLiteral:可以理解為包含Infinity和數(shù)字的字符串集合。
StringNumericLiteral:包含StrNumericLiteral和StrWhiteSpace的集合

WhiteSpace
Unicode Code Point name
U+0009 制表符
U+000B 垂直方向的制表符
U+000C 換頁(yè)符
U+0020 空格符
U+00A0 不換行空格符
U+FEFF 零寬度不換行空格符
其他種類的“Zs”(分隔符,空白) Unicode “Space_Separator”

ECMAScript WhiteSpace有意排除具有Unicode“White_Space”屬性但未在類別“Space_Separator”(“Zs”)中分類的所有代碼點(diǎn)。

Zs列表
我這邊列出了Unicode其它“Zs”的列表,感興趣的可以了解一下:

Unicode Code Point name
U+1680 OGHAM SPACE MARK
U+180E MONGOLIAN VOWEL SEPARATOR
U+2000 EN QUAD
U+2001 EM QUAD
U+2002 EN SPACE
U+2003 EM SPACE
U+2004 THREE-PER-EM SPACE
U+2005 FOUR-PER-EM SPACE
U+2006 SIX-PER-EM SPACE
U+2007 FIGURE SPACE
U+2008 PUNCTUATION SPACE
U+2009 THIN SPACE
U+200A NARROW NO-BREAK SPACE
U+202F FIGURE SPACE
U+205F MEDIUM MATHEMATICAL SPACE ?
U+3000 IDEOGRAPHIC SPACE
LineTerminator
Unicode Code Point name
U+000A 換行符
U+000D 回車
U+2028 行分隔符
U+2029 段分隔符

上面的過(guò)程說(shuō)的很抽象,不是很容易理解,我們來(lái)看一下具體的列子:

Number("");                   //0  empty
Number("    ");               //0  多個(gè)空格
Number("u0009");             //0  制表符也可以用Number("	")表示
Number(
);                            //0  換行符也可以用Number("
")或Number("u000A")表示
Number("000010");             //10 1前面的多個(gè)0被忽略
Number("    10    ");         //10 string前后多個(gè)StrWhiteSpace
Number("u000910u0009");     //10 string前后有制表符
Number("ab");                 //NaN 

StrNumericLiteral中的其它進(jìn)制的數(shù)字與十進(jìn)制有相似的規(guī)則,但轉(zhuǎn)化的Number值是十進(jìn)制下的值:

Number("0b10");               //2   (二進(jìn)制)
Number("0o17");               //15  (八進(jìn)制)
Number("0xA");                //10  (十六進(jìn)制)

還有說(shuō)明一點(diǎn)是十進(jìn)制下數(shù)字的科學(xué)計(jì)數(shù)法顯示的字符串也能通過(guò)ToNumber轉(zhuǎn)換為Number類型:

Number("1.2e+21");            //1.2e+21
Number("1.2e-21");            //1.2e-21 
ToString(argument)

轉(zhuǎn)換為String類型的規(guī)則如下:

argument的類型 轉(zhuǎn)換的結(jié)果
Undefined "undefined"
Null "null"
Boolean false為"false",true為true"
String argument
Object 執(zhí)行以下步驟:讓primValue成為T(mén)oPrimitive(argument, hint String)的返回值,再調(diào)用ToString(primValue)返回。
Symbol(ES6新增類型) 拋出TypeError異常.

同樣的在表中我也沒(méi)有列出Number類型轉(zhuǎn)換為String類型的規(guī)則,Number轉(zhuǎn)String并不是簡(jiǎn)單的在數(shù)字前后加上‘或“就行了(即使看起來(lái)是這樣),里面涉及到了復(fù)雜的數(shù)學(xué)算法,我不細(xì)說(shuō)(好吧主要是我沒(méi)有特別理解,具體算法可以看文檔),在這里我只列出幾種特殊情況:

假設(shè)Number的值為m:

如果m是NaN,返回String "NaN"。

如果m是+0或-0,返回String "0"。

如果m小于0, 返回字符串連接符"-"和ToString(-m)。

如果m是+∞,返回String "Infinity"。

ToPrimitive(input [ , PreferredType ])

我們?cè)谏厦鎀oNumber和ToString方法中注意到Object類型轉(zhuǎn)換為Number和String時(shí)都會(huì)調(diào)用ToPrimitive方法。該方法接受一個(gè)input輸入?yún)?shù)和一個(gè)可選的PreferredType參數(shù)。PreferredType是用來(lái)決定當(dāng)某個(gè)對(duì)象能夠轉(zhuǎn)換為多個(gè)基本類型時(shí)該返回什么類型??墒荰oPrimitive內(nèi)部究竟是如何操作來(lái)返回Number或String類型的呢?如果要深入探究其具體的操作步驟可能花大半天也不能完全理清,里面包含了各種方法的調(diào)用以及復(fù)雜的邏輯判斷還有各種安全檢測(cè),我不仔細(xì)深入下去。我這邊假設(shè)所有的判斷都按正常流程走,所有安全機(jī)制都通過(guò)不報(bào)錯(cuò)誤,那么一個(gè)對(duì)象轉(zhuǎn)換為Number或String就可以概括為以下幾個(gè)判斷:

一個(gè)對(duì)象上是否有@@toPrimitive方法定義,如果有調(diào)用該方法返回結(jié)果。

對(duì)象上如果沒(méi)有定義@@toPrimitive方法,則沿著該對(duì)象的原型鏈向上查找,直到找到或者[[Prototype]]為空。

如果該對(duì)象和其原型鏈上都沒(méi)有定義@@toPrimitive方法,則調(diào)用OrdinaryToPrimitive(O,hint);

hint有PreferredType決定,如果PreferredType是hint Number,hint為"number",PreferredType是hint String,hint為"string",如果沒(méi)定義,默認(rèn)hint為"number",O就是input對(duì)象。

OrdinaryToPrimitive方法的判斷是:如果hint為"string",在O上調(diào)用? "toString", "valueOf" ?。意思是在O以及原型鏈上先查找"toString"方法,找到第一個(gè)toString方法就調(diào)用toString返回結(jié)果,如果沒(méi)有就查找”valueOf“方法來(lái)返回結(jié)果。

如果hint為"number",在O上調(diào)用? "valueOf", "toString" ?。

@@toPrimitive、? "toString", "valueOf" ?和? "valueOf", "toString" ?方法調(diào)用返回一個(gè)Object類型時(shí)可能會(huì)報(bào)TypeError錯(cuò)誤

@@toPrimitive是Symbol類型,是Symbol.toPrimitive的簡(jiǎn)寫(xiě),ES6之前沒(méi)有Symbol類型,所以只需判斷toString和valueOf方法。

我這邊用幾個(gè)例子來(lái)解釋ToPrimitive的運(yùn)行過(guò)程

var a = {
    [Symbol.toPrimitive]: (hint)=>{
        if(hint==="number"){
            return 1;
        }else if(hint==="string"){
            return "Symbol.toPrimitive";
        }else if(hint==="default"){
            return 2;
        }else{
            throw TypeError("不能轉(zhuǎn)換為String和Number之外的類型值");        //防止內(nèi)部出現(xiàn)錯(cuò)誤     
        }
    },
    toString: () => "toString",
    valueOf: () => 3
}; 
Number(a);                    //1         hint為"number"
String(a);                    //"Symbol.toPrimitive"  hint為"string"
a + "1";                      //"21"      a在進(jìn)行+操作符時(shí)hint為"default",因?yàn)槌绦虿恢滥闶亲鲎址嗉舆€是數(shù)值相加
a + 1;                        //3 
+a;                           //1         此時(shí)hint為"number",為什么hint不是"default",+a實(shí)際上內(nèi)部進(jìn)行ToNumber轉(zhuǎn)換,-、*、/操作符類似  

//刪除a中Symbol.toPrimitive屬性                            
delete a[Symbol.toPrimitive];
Number(a);                    //3         調(diào)用valueOf方法
String(a);                    //"toString"   調(diào)用toString方法
a + 1;                        //4        結(jié)果不是"toString1"是因?yàn)閖s內(nèi)部先判斷valueOf方法
//刪除a中valueOf屬方法 
delete a["valueOf"];
Number(a);                    //NaN     返回的"toString"不能轉(zhuǎn)換為有效數(shù)字
String(a);                    //"toString"
1 + a;                        //"1toString"
//重寫(xiě)a中的toString方法
a.toString = () = > a;        //返回了a對(duì)象
Number(a);                    //TypeError
String(a);                    //TypeError
1 + a;                        //TypeError

上面例子看出Object類型在轉(zhuǎn)換為String和Number時(shí)有可能會(huì)出現(xiàn)各種各樣的情況。為此我們最好永遠(yuǎn)不要重寫(xiě)對(duì)象中的valueOf或者toString方法,以防出現(xiàn)意想不到的結(jié)果,如果你重寫(xiě)了方法那么你就要格外小心了。

Object.prototype.toString= () => 1;
1 + {};        //2   看到了嗎?永遠(yuǎn)不要重寫(xiě)Object中的內(nèi)置方法,最好也不要在子對(duì)象中覆蓋Object的內(nèi)置方法。

在此我們對(duì)js中強(qiáng)制轉(zhuǎn)換時(shí)發(fā)生的過(guò)程基本捋了一遍,接下來(lái)我們來(lái)了解一下相等操作符兩邊發(fā)生了什么。

Abstract Equality Comparison

ECMAScript官方對(duì)(==)操作的說(shuō)法是Abstract Equality Comparison(抽象的相等比較),它對(duì)x==y定義了下面一些規(guī)則:

如果x和y是同一類型,進(jìn)行Strict Equality Comparison x === y。

如果x是null,y是undefined,返回true。

如果x是undefined,y是null,返回true。

如果x的類型是Number,y的類型是String,進(jìn)行x==ToNumber(y)。

如果x的類型是String,y的類型是Number,進(jìn)行ToNumber(x)==y。

如果x的類型是Boolean,進(jìn)行ToNumber(x)==y。

如果y的類型是Boolean,進(jìn)行x==ToNumber(y)。

如果x的類型是String、Number或者Symbol,y的類型是Object,進(jìn)行x==ToPrimitive(y)。

如果x的類型是Object,y的類型是String、Number或者Symbol,進(jìn)行ToPrimitive(x)==y。

其他返回false

Strict Equality Comparison

Strict Equality Comparison(嚴(yán)格的相等比較)對(duì)x===y定義下列規(guī)則:

如果x和y是不是同一類型, 返回false。

如果x的類型是Number:

- 如果x或y是NaN,返回false。
- 如果x和y數(shù)值相同,返回true。
- 如果x是+0,y是-0,返回true。
- 如果x是-0,y是+0,返回true。
- 其他返回false。

如果x是Undefined類型,返回true。

如果x是Null類型,返回true。

如果x是String類型,x和y是完全相同的代碼單元序列返回true,否則false。

如果x是Boolean類型,x和y都是true或都是false,返回true,否則返回false。

如果x是Symbol類型,x和y是相同的Symbol值,返回true,否則返回false。

如果x和y是相同的對(duì)象,返回true,否則返回false。

提到(===)操作符,我們不等不說(shuō)一個(gè)方法Object.is(a,b),該方法也是比較兩個(gè)值是否一樣,但它比(===)更嚴(yán)格。它們之間的區(qū)別在于如果x和y是NaN,返回true。如果x是+0,y是-0,返回false,如果x是-0,y是+0,返回false。

驗(yàn)證

到這里類型轉(zhuǎn)換和相等比較的介紹就告一段落了,現(xiàn)在我們重新回過(guò)頭去看一下最開(kāi)始的幾個(gè)奇特例子,你會(huì)發(fā)現(xiàn)它們之間的關(guān)系比較是如此的正常。我就拿([] == ![])進(jìn)行講解,按照操作符優(yōu)先級(jí)比較,先運(yùn)行![],它的值為false,這時(shí)等式變成([] == false);按(==)的規(guī)則7對(duì)false進(jìn)行ToNumber操作,值變?yōu)?,這時(shí)等式變?yōu)椋╗] == 0);按(==)的規(guī)則9對(duì)[]進(jìn)行ToPrimitive操作,調(diào)用Array上的toString方法,返回"",這時(shí)等式變?yōu)椋?" == 0);按(==)的規(guī)則5對(duì)""進(jìn)行ToNumber操作,值變?yōu)?,這時(shí)等式是(0==0)。我們最終得出結(jié)論([] == ![])是對(duì)的。

補(bǔ)充

我們看一下下面的例子:

1 + {};               //"1[object object]"
{} + 1;               //1
({} + 1);             //"[object object]1"

我們發(fā)現(xiàn)第一和第三個(gè)表達(dá)式按照我們預(yù)期的值輸出了,但是第二個(gè)表達(dá)式卻沒(méi)有。這里要強(qiáng)調(diào)一點(diǎn):第二個(gè)表達(dá)式?jīng)]有涉及到強(qiáng)制類型轉(zhuǎn)換。他把這個(gè)表達(dá)式看成了兩個(gè),一個(gè)是塊{},還有一個(gè)是+1,把{}丟棄l,所以輸出的值1。至于1+{},js把他看成一個(gè)表達(dá)式,所以{}被強(qiáng)制轉(zhuǎn)換為"[object object]";第三個(gè)表達(dá)式加了(),使js認(rèn)為{}+1是一個(gè)整體,所以{}也被強(qiáng)制轉(zhuǎn)換了。

結(jié)束

到這里我想說(shuō)的基本就結(jié)束了。如果文中有錯(cuò)誤或者有某些強(qiáng)制轉(zhuǎn)換的情形沒(méi)有涉及到請(qǐng)及時(shí)留言告知,我會(huì)修改并補(bǔ)充進(jìn)去。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/89630.html

相關(guān)文章

  • JavaScript數(shù)據(jù)類型和他背后不得不說(shuō)故事

    摘要:基本概念中有種簡(jiǎn)單數(shù)據(jù)類型也稱為基本數(shù)據(jù)類型,存放在棧中和。在使用聲明變量但未對(duì)其加以初始化時(shí),這個(gè)變量的值就是,例如類型是第二個(gè)只有一個(gè)值的數(shù)據(jù)類型,這個(gè)特殊的值是。類型阿拉伯?dāng)?shù)字的八進(jìn)制十進(jìn)制十六進(jìn)制整數(shù)浮點(diǎn)數(shù)。 基本概念 ECMAScript 中有 5 種簡(jiǎn)單數(shù)據(jù)類型(也稱為基本數(shù)據(jù)類型,存放在棧中):Undefined、Null、Boolean、Number 和String。還...

    ASCH 評(píng)論0 收藏0
  • JavaScript數(shù)據(jù)類型和他背后不得不說(shuō)故事

    摘要:基本概念中有種簡(jiǎn)單數(shù)據(jù)類型也稱為基本數(shù)據(jù)類型,存放在棧中和。在使用聲明變量但未對(duì)其加以初始化時(shí),這個(gè)變量的值就是,例如類型是第二個(gè)只有一個(gè)值的數(shù)據(jù)類型,這個(gè)特殊的值是。類型阿拉伯?dāng)?shù)字的八進(jìn)制十進(jìn)制十六進(jìn)制整數(shù)浮點(diǎn)數(shù)。 基本概念 ECMAScript 中有 5 種簡(jiǎn)單數(shù)據(jù)類型(也稱為基本數(shù)據(jù)類型,存放在棧中):Undefined、Null、Boolean、Number 和String。還...

    avwu 評(píng)論0 收藏0
  • js 學(xué)習(xí)筆記(一)

    摘要:前言網(wǎng)上其實(shí)已經(jīng)有非常多的學(xué)習(xí)資料了,但是每個(gè)人都有自己的基礎(chǔ),所以往往是有的人講的深一點(diǎn),有的人說(shuō)的淺一點(diǎn)。講述的人們因?yàn)楹ε潞樗脑俅蔚絹?lái),而準(zhǔn)備聯(lián)合起來(lái)修建一座直通天際的高塔以傳揚(yáng)聚集四散的人類。 前言 網(wǎng)上其實(shí)已經(jīng)有非常多的js學(xué)習(xí)資料了,但是每個(gè)人都有自己的基礎(chǔ),所以往往是有的人講的深一點(diǎn),有的人說(shuō)的淺一點(diǎn)。 就我自身而言,想要匹配自己水平的找些資料,往往是十分的零碎,所以可...

    xiguadada 評(píng)論0 收藏0
  • 談?wù)?var, let, const. var 也是一種選擇

    摘要:談?wù)勔彩且环N選擇歷史故事在之前是一門(mén)被稱為沒(méi)有塊級(jí)作用域的語(yǔ)言看看代碼輸出結(jié)果權(quán)威解析這是因?yàn)楸宦暶髟诋?dāng)前函數(shù)的作用域內(nèi)不管你聲明在函數(shù)的什么位置在函數(shù)執(zhí)行之前解析器會(huì)掃描當(dāng)前函數(shù)作用域并將以和開(kāi)頭的語(yǔ)句的變量名添加到當(dāng)前函數(shù)作用域內(nèi)這意味 談?wù)?var, let, const. var 也是一種選擇 歷史故事 在 ES6 之前, JavaScript 是一門(mén)被稱為沒(méi)有塊級(jí)作用域的語(yǔ)言...

    BoYang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<