javascript
《JavaScript高级程序设计(第四版)》红宝书学习笔记(1)
個(gè)人對(duì)第四版紅寶書(shū)的學(xué)習(xí)筆記。不適合小白閱讀。這是part1,包含原書(shū)第二章(HTML中的Javascript)和第三章(語(yǔ)言基礎(chǔ))。持續(xù)更新,其他章節(jié)筆記看我主頁(yè)。
(記 * 的表示是ES6新增的知識(shí)點(diǎn),記 · 表示包含新知識(shí)點(diǎn))。
新增知識(shí)點(diǎn)如下:let聲明、const聲明、模板字面量(字符串)、Symbol數(shù)據(jù)類(lèi)型、for-of 循環(huán)語(yǔ)句。
第二章:HTML中的Javascript
2.1 <script>元素
<script>標(biāo)簽的八個(gè)屬性
-
async:可選。表示應(yīng)該立即開(kāi)始下載腳本,但不能阻止其它頁(yè)面動(dòng)作,比如下載資源或等待其他腳本加載。使用該屬性可以異步執(zhí)行腳本。只對(duì)外部腳本文件有效。
-
charset:可選。使用src屬性指定的代碼字符集。基本不會(huì)使用。
-
crossorigin:可選。配置相關(guān)的CORS(跨資源共享)設(shè)置。默認(rèn)不使用CORS。corssorigin="anonymous"配置文件請(qǐng)求不必設(shè)置憑據(jù)標(biāo)志。corssorigin="use-credentials"設(shè)置憑據(jù)標(biāo)志,意味著出戰(zhàn)請(qǐng)求會(huì)包含憑據(jù)。
-
defer:可選。表示在文檔解析和顯示完成后再執(zhí)行腳本是沒(méi)有問(wèn)題的。只對(duì)外部腳本文件有效。IE7及以前可以對(duì)行內(nèi)腳本指定該屬性。使用該屬性推遲執(zhí)行腳本。
-
integrity:可選。允許對(duì)比接收到的資源和指定的加密簽名以驗(yàn)證子資源完整性(SRI)。如果接收到資源的簽名與這個(gè)屬性指定的簽名不匹配,則頁(yè)面會(huì)報(bào)錯(cuò),腳本不會(huì)執(zhí)行。該屬性可以用于確保內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN,Content Delivery Network)不會(huì)提供惡意內(nèi)容。
-
language:已廢棄。
-
src:可選。表示包含要執(zhí)行的代碼的外部文件。
-
type:可選。代替language,表示代碼塊中腳本語(yǔ)言的內(nèi)容類(lèi)型(也稱(chēng)MIME類(lèi)型)。按慣例始終都會(huì)是text/javascript。Javascript文件的MIME類(lèi)型通常是"application/x-javascript",不過(guò)給type屬性這個(gè)值有可能導(dǎo)致腳本被忽略。在非IE瀏覽器有效的其他值還有"application/javascript"和"application/ecmascript"。如果這個(gè)值是module,則代碼會(huì)被當(dāng)成ES6模塊,且只有這時(shí)候代碼中才能出現(xiàn)import和export關(guān)鍵字。
三個(gè)注意點(diǎn):
使用<script>標(biāo)簽寫(xiě)行內(nèi)代碼時(shí)不要出現(xiàn)這種情況:
<script>console.log("</script>"); <!--這里</script>會(huì)被當(dāng)作結(jié)束標(biāo)簽,甚至你會(huì)發(fā)現(xiàn)寫(xiě)在注釋中也會(huì)被當(dāng)作結(jié)束標(biāo)簽無(wú)法解析,因此我這里用了html的注釋法--> </script>可以使用轉(zhuǎn)義字符解決這一問(wèn)題:
<script>console.log("<\/script>"); //這里可以正常使用,注釋里這樣使用:<\/script> 也可以,從代碼沒(méi)有高亮可以看出來(lái) </script>該元素最為強(qiáng)大同時(shí)也備受爭(zhēng)議的特性是:它可以包含來(lái)自外部域的Javascript文件。它的src屬性可以是一個(gè)完整的url,而且這個(gè)url指向的資源可以跟包含它的HTML頁(yè)面不再同一個(gè)域中。例如:
<script src="http://www.somewhere.com/afile.js"></script>
瀏覽器解析時(shí)會(huì)向src屬性指定的路徑發(fā)送一個(gè)GET請(qǐng)求,以取得相應(yīng)資源。這個(gè)初始的請(qǐng)求不受瀏覽器同源策略限制,但返回的Javascript受限。(當(dāng)然,該請(qǐng)求仍然受HTTP/HTTPS)協(xié)議的限制。
來(lái)自外部的代碼會(huì)被當(dāng)成加載他它的頁(yè)面的一部分來(lái)加載和解析。**這個(gè)能力可以讓我們通過(guò)不同的域分發(fā)JavaScript。**不過(guò)同時(shí),引用他人服務(wù)器的文件時(shí)必須格外小心,因?yàn)榭赡軙?huì)有惡意的程序員替換這個(gè)文件。integrity屬性可以防范,但不是所有瀏覽器都支持。
一般瀏覽器會(huì)按照<script>在頁(yè)面中的順序依次解釋它們,只要沒(méi)有使用defer和async屬性的話。另外最好將標(biāo)簽位置放在頁(yè)面底部(<body>之后)。
###2.2 行內(nèi)代碼與外部文件
雖然不是明確的強(qiáng)制性規(guī)則,但通常認(rèn)為最佳實(shí)踐是盡可能將Javascript代碼放在外部文件中。原因如下:
- 可維護(hù)性。使用一個(gè)目錄保存所有的JavaScript文件總會(huì)比分散在很多HTML頁(yè)面容易維護(hù)。
- 緩存。瀏覽器會(huì)根據(jù)特點(diǎn)的設(shè)置緩存所有外部鏈接的JavaScript文件,這意味著如果兩個(gè)頁(yè)面都用到同一個(gè)文件,則該文件只需被下載依次。
- 適應(yīng)未來(lái)。
2.3 文檔模式
IE5.5發(fā)明了文檔模式的概念,即可以通過(guò)doctype切換文檔模式。最初的文檔模式有兩種:混雜模式(quirks mode)和標(biāo)準(zhǔn)模式(standards mode)。后來(lái)出現(xiàn)了第三種文檔模式:準(zhǔn)文檔模式(almost standards mode)。只作了解。
2.4 <noscript>元素
針對(duì)早期瀏覽器不支持JavaScript的問(wèn)題,提出的一個(gè)頁(yè)面優(yōu)雅降級(jí)的處理方案:
<noscript><p>該頁(yè)面不支持JavaScript,請(qǐng)更換瀏覽器。</p> </noscript><noscript>元素可以包含任何出現(xiàn)在<body>中的HTML元素,<script>除外。出現(xiàn)下列兩種情況下,瀏覽器將顯示包含在該元素中的內(nèi)容:
- 瀏覽器不支持腳本;
- 瀏覽器對(duì)腳本的支持被關(guān)閉。
第三章:語(yǔ)言基礎(chǔ)
3.1 變量聲明
3.1.1 var聲明
var聲明的變量不初始化的情況下,該變量會(huì)保存特殊的值undefined。
var聲明廚初始化后的變量,后續(xù)可以改變類(lèi)型。
1)var聲明作用域
使用var操作符定義的變量會(huì)成為包含它的函數(shù)的局部變量,該變量在函數(shù)退出時(shí)被銷(xiāo)毀。而在局部作用域中省略var操作符聲明,該變量會(huì)作為全局變量:
function test(){var m1="hi";m2="hi"; } test(); console.log(m1); //-> 出錯(cuò)! console.log(m2); //-> hi2)var聲明提升
使用var關(guān)鍵字時(shí),聲明的變量會(huì)自動(dòng)提升(hoist)到函數(shù)作用域頂部。此外,反復(fù)多次使用var聲明同一個(gè)變量也沒(méi)有問(wèn)題。
function test(){console.log(age);var age=18; }/*上述代碼在ECMAScript中運(yùn)行時(shí)會(huì)看成等價(jià)如下代碼:*/ function test(){var age;console.log(age);age=18; } //因此調(diào)用該方法,結(jié)果會(huì)如下: test(); //-> undefined3.1.2 let聲明 *
let和var差不多,但有著很大的區(qū)別,最明顯的區(qū)別是:let聲明的范圍是塊作用域,而var是函數(shù)作用域。
if(true){var name1='matt'; } console.log(name1); //-> matt if(true){let name2='mat'; } console.log(name2); //-> ReferenceError:name沒(méi)有定義這里name2之所以不能再if塊外部被引用,是因?yàn)樗淖饔糜騼H限于該塊內(nèi)部。塊作用域是函數(shù)作用域的子集,因此適用于var的作用域限制同樣適用于let。
let也不允許同一個(gè)塊作用域中出現(xiàn)冗余聲明。
var age; var age;let age; let age; //-> SyntaxError; 標(biāo)識(shí)符age已經(jīng)聲明過(guò)了對(duì)聲明冗余報(bào)錯(cuò)不會(huì)因混用let和var而受影響。它們聲明的不是不同類(lèi)型的變量,只是指出變量在相關(guān)作用域如今存在。
var name; let name; //-> SyntaxErrorlet age; var age; //-> SyntaxError1)暫時(shí)性死區(qū)
let和var另一個(gè)重要的區(qū)別,就是let聲明的變量不會(huì)再作用域中被提升。
在let聲明之前的執(zhí)行瞬間被稱(chēng)為“暫時(shí)性死區(qū)”(temporal dead zone),在此階段引用任何后面才聲明的變量都會(huì)拋出ReferenceError。
2)全局聲明
let在全局作用域中聲明的變量不會(huì)成為window 對(duì)象的屬性(var聲明的變量則會(huì))。不過(guò)let聲明仍然是在全局作用域中發(fā)生的,相應(yīng)變量會(huì)在頁(yè)面的生命周期內(nèi)存續(xù)。
var name='matt'; console.log(window.name); //-> 'matt'let age=18; console.log(window.age); //-> undefined3)條件聲明
使用var聲明時(shí),由于聲明會(huì)被提升,JavaScript引擎會(huì)自動(dòng)將多余的聲明在作用域頂部合并為一個(gè)聲明。因?yàn)閘et的作用域是塊,所以不可能檢查前面是否已經(jīng)使用let聲明過(guò)同名變量,同時(shí)也就不可能在沒(méi)有聲明的情況下聲明它。
4)for循環(huán)中的let聲明
在let出現(xiàn)之前,for循環(huán)定義的迭代變量會(huì)滲透到循環(huán)體外部:
for(var i=0;i<5;i++){//循環(huán)體 } console.log(i); //-> 5而let就解決了這一問(wèn)題,因?yàn)榈闅v的作用域僅限于for循環(huán)塊內(nèi)部。
for(let i=0;i<5;i++){//循環(huán)體 } console.log(i); //-> ReferenceError:i沒(méi)有定義另外在使用var的時(shí)候,最常見(jiàn)的問(wèn)題就是對(duì)迭代變量的奇特聲明和修改:
for(var i=0;i<5;i++){setTimeout(() => console.log(i),0); //-> 5、5、5、5、5 } //因?yàn)樵谕顺鲅h(huán)時(shí),迭代變量保存的都是導(dǎo)致循環(huán)退出的值:5。之后執(zhí)行超時(shí)邏輯時(shí),所有的i都是同一個(gè)變量。而在使用let聲明迭代變量時(shí),JavaScript引擎在后臺(tái)會(huì)為每個(gè)迭代循環(huán)聲明一個(gè)新的迭代變量。
for(let i=0;i<5;i++){setTimeout(() => console.log(i),0); //-> 1、2、3、4、5 }這種行為適用于于所有風(fēng)格的for循環(huán),包括for-in和for-of循環(huán)。
3.1.3 const聲明 *
const的行為與let基本相同,唯一一個(gè)重要的區(qū)別是用它聲明變量是時(shí)必須同時(shí)初始化變量,且后續(xù)嘗試修改const變量會(huì)報(bào)錯(cuò)。
const聲明的限制只適用于它指向的變量的引用。換言之,如果const變量引用的是一個(gè)對(duì)象,那么修改這個(gè)對(duì)象內(nèi)部的屬性并不違反const的限制。
另外const不能用來(lái)聲明迭代變量(因?yàn)榈兞繒?huì)自增或自減)。但const可以用來(lái)聲明一個(gè)不會(huì)被修改的for循環(huán)變量,也就是說(shuō),每次迭代只是創(chuàng)建一個(gè)新變量。
let i=0; for(const j=7;i<5;++i){console.log(j); //-> 7,7,7,7,7 }for(const key in {a:1,b:2}){console.log(key); //-> a,b }for(const value of [1,2,3,4,5]){console.log(value); //-> 1,2,3,4,5 } //可以看出,這對(duì)for-of和for-in循環(huán)特別有意義3.2 數(shù)據(jù)類(lèi)型
3.2.1 typeof操作符
使用typeof操作符檢驗(yàn)數(shù)據(jù)類(lèi)型。可以用來(lái)區(qū)分函數(shù)和對(duì)象。
alert(typeof 95); alert(typeof(95)); //看的出來(lái)typeof操作符也可以使用參數(shù) alert(typeof null); //會(huì)返回object,因?yàn)樘厥庵祅ull被認(rèn)為是一個(gè)對(duì)空對(duì)象的引用3.2.2 Undefined類(lèi)型
在聲明變量但未對(duì)其加以初始化時(shí),這個(gè)變量的值就是undefined。
而當(dāng)使用typeof操作符檢驗(yàn)一個(gè)未聲明的變量時(shí),返回的值也是undefined。
3.2.3 Null類(lèi)型
邏輯上,null值表示一個(gè)空對(duì)象指針。所以使用typeof檢驗(yàn)null會(huì)返回object。
ECMA-262規(guī)定 undefined==null 返回 true。
無(wú)論什么情況都不需要將變量值顯式地設(shè)置undefined,但對(duì)null不適用。換言之,只要意在保存的對(duì)象還沒(méi)有真正保存對(duì)象,就應(yīng)該明確地讓該變量保存null。
3.2.4 Boolean類(lèi)型
? true、false是區(qū)分大小寫(xiě)的。
布爾類(lèi)型轉(zhuǎn)換Boolean()
? 使用**Boolean()**函數(shù)將對(duì)應(yīng)的值轉(zhuǎn)化為布爾值。下面是轉(zhuǎn)化規(guī)則:
| Boolean | true | false |
| String | 任何非空字符串 | 空字符串 |
| Number | 任何非零數(shù)字值(包括無(wú)窮值) | 0和NaN |
| Object | 任何對(duì)象 | null |
| Undefined | n/a(不適用) | undefined |
3.2.5 Numer類(lèi)型
除了十進(jìn)制表示以外,整數(shù)還可以通過(guò)八進(jìn)制或十六進(jìn)制的字面值表示。
其中,八進(jìn)制字面值的第一位必須是零(0)。如果字面值中的數(shù)值超出了范圍,那么前導(dǎo)零將被忽略,后面的數(shù)值將被當(dāng)作十進(jìn)制數(shù)值解析。注意,八進(jìn)制字面量在嚴(yán)格模式下是無(wú)效的,會(huì)導(dǎo)致支持的Js引擎拋出錯(cuò)誤。
十六進(jìn)制字面值的前兩位必須是0x,后跟任何十六進(jìn)制數(shù)(09及AF)。其中,字母A~F可以大寫(xiě),也可以小寫(xiě)。
進(jìn)行算術(shù)計(jì)算時(shí),所有的八進(jìn)制和十六進(jìn)制數(shù)最終都將被轉(zhuǎn)化為十進(jìn)制數(shù)。
1)浮點(diǎn)數(shù)
由于保存浮點(diǎn)數(shù)值需要的內(nèi)存空間是整數(shù)的兩倍,因此ECMAScript會(huì)不失時(shí)機(jī)地將浮點(diǎn)數(shù)值轉(zhuǎn)化為整數(shù)值。如果小數(shù)點(diǎn)后沒(méi)有跟任何數(shù)字(如 1.)或浮點(diǎn)數(shù)值本身表示的就是一個(gè)整數(shù)(如1.0),就會(huì)將其轉(zhuǎn)化為整數(shù)。
默認(rèn)情況下,ECMAScript會(huì)將那些小數(shù)點(diǎn)后面帶有6個(gè)0以上的浮點(diǎn)數(shù)值轉(zhuǎn)化為以e表示法表示的數(shù)值。
浮點(diǎn)數(shù)值的最高精度是17位小數(shù),但在進(jìn)行算術(shù)計(jì)算時(shí)其精確度遠(yuǎn)不如整數(shù)。例如,0.1+0.2的結(jié)果不是0.3,而是0.30000000000000004(小數(shù)點(diǎn)后一共17位)。這個(gè)舍入誤差會(huì)導(dǎo)致無(wú)法測(cè)試指定的浮點(diǎn)數(shù)值。例如:
if(a + b == 0.3){ //不要做這樣的測(cè)試!alert("You get 0.3"); } //在這個(gè)例子中,我們測(cè)試的是兩個(gè)數(shù)的和是不是等于0.3。若這兩個(gè)數(shù)是0.05和0.25,或者是0.15和0.15都不會(huì)有問(wèn)題。因此,永遠(yuǎn)不要測(cè)試某個(gè)特定的浮點(diǎn)數(shù)2)數(shù)值范圍
由于內(nèi)存限制,ECMAScript無(wú)法保存世界上所有數(shù)據(jù)。ECMAScript能夠表示的最小數(shù)值保存在Number.MIN_VALUE中——在大多數(shù)瀏覽器中,這個(gè)值是5e-324;能夠表示的最大數(shù)值保存在Number.MAX_VALUE中——在大多數(shù)瀏覽器中,這個(gè)值是1.7976931348623157e+308。如果這個(gè)數(shù)值是正數(shù),則會(huì)轉(zhuǎn)化為**Infinity**。
要想確定一個(gè)數(shù)值是不是有窮的(換言之,是不是位于最小和最大數(shù)值之間),可以使用isFinite()函數(shù)。這個(gè)函數(shù)在參數(shù)位于最小與最大數(shù)值之間會(huì)返回true。
3)NaN
? NaN,即非數(shù)值(Not a Number)是一個(gè)特殊的數(shù)值,這個(gè)數(shù)值用于表示一個(gè)本來(lái)要返回?cái)?shù)值的操作數(shù)未返回?cái)?shù)值的情況(這樣不會(huì)拋出錯(cuò)誤)。例如在其他編程語(yǔ)言中,任何數(shù)除以0都會(huì)導(dǎo)致錯(cuò)誤,但在ECMAScript中會(huì)返回NaN,因此不會(huì)影響其他代碼執(zhí)行。
? NaN有兩個(gè)特點(diǎn)。首先,任何涉及NaN的操作都會(huì)返回NaN。其次,NaN與任何值都不相等,包括NaN本身。
? 針對(duì)NaN,ECMAScript定義了一個(gè)函數(shù)isNaN()函數(shù)。這個(gè)函數(shù)接受一個(gè)參數(shù)(可以是任意類(lèi)型),函數(shù)會(huì)幫我們確定該參數(shù)是否“不是數(shù)值”。函數(shù)在接受到值后,會(huì)嘗試將該值轉(zhuǎn)化為數(shù)值,某些不是數(shù)值的值會(huì)直接轉(zhuǎn)化為數(shù)值,例如字符串"10"或Boolean值。任何不能被轉(zhuǎn)化為數(shù)值的值都會(huì)導(dǎo)致函數(shù)返回true。
alert(isNaN(NaN)); //true alert(isNan(10)); //false(10是一個(gè)數(shù)值) alert(isNan("10")); //false(會(huì)轉(zhuǎn)化為數(shù)值10) alert(isNan("blue")); //true(無(wú)法轉(zhuǎn)化為數(shù)值) alert(isNan(true)); //false(可以被轉(zhuǎn)化為數(shù)值1)? 而對(duì)于isNaN(),它也適用于對(duì)象。在基于對(duì)象調(diào)用該函數(shù)時(shí),會(huì)首先調(diào)用對(duì)象的valueOf()方法,然后確定該方法返回的值是否可以轉(zhuǎn)化為數(shù)值。如果不能,則基于這個(gè)返回值再調(diào)用 toString() 方法,再測(cè)試返回值。
4)數(shù)值轉(zhuǎn)換
? 有三個(gè)函數(shù)可以將非數(shù)值轉(zhuǎn)化為數(shù)值:Number()、parseInt()和parseFloat()。第一個(gè)轉(zhuǎn)型函數(shù)Number()可以用于任何數(shù)據(jù)類(lèi)型,而另兩個(gè)函數(shù)則專(zhuān)門(mén)用于將字符串轉(zhuǎn)換成數(shù)值。
Number()函數(shù)
-
如果是布爾值,true和false將分別轉(zhuǎn)換為1和0。
-
如果數(shù)字值,只是簡(jiǎn)單的傳入與返回。
-
如果是null,返回0。
-
如果是undefined,返回NaN。
-
如果是字符串,遵循下列規(guī)則:
- 如果字符串中只包含數(shù)字,則將其轉(zhuǎn)化為十進(jìn)制,如"12"會(huì)轉(zhuǎn)化為12,"0123"轉(zhuǎn)化為123(注意,前導(dǎo)的零被忽略了)。
- 如果字符串中包含有效的浮點(diǎn)格式,如"1.1",則將其轉(zhuǎn)化為對(duì)應(yīng)的浮點(diǎn)數(shù)值(同樣會(huì)忽略前導(dǎo)零)
- 如果字符串中包含有效的十六進(jìn)制格式,則將其轉(zhuǎn)化為相同大小的十進(jìn)制整數(shù);
- 如果為空字符串,轉(zhuǎn)化為0;
- 如果字符串包含上述格式之外的字符,則轉(zhuǎn)換為NaN。
-
如果是對(duì)象,則調(diào)用對(duì)象的valueOf()方法,然后依照前面的規(guī)則轉(zhuǎn)換返回的值。如果結(jié)果是NaN,則調(diào)用對(duì)象的toString()方法,然后再次依照前面的規(guī)則轉(zhuǎn)換返回的字符串值。
ParseInt()函數(shù)
由于Number()函數(shù)在轉(zhuǎn)化時(shí)比較復(fù)雜且不夠合理,因此在處理整數(shù)的時(shí)候更常用的是parseInt()函數(shù)。該函數(shù)會(huì)忽略字符串前的空格,直至找到第一個(gè)非空格字符。如果第一個(gè)字符不是數(shù)字或者負(fù)號(hào),函數(shù)就會(huì)返回NaN;也就是說(shuō),parseInt()函數(shù)對(duì)空字符串會(huì)返回NaN(而Number()函數(shù)會(huì)返回0)。如果第一個(gè)字符是數(shù)字字符,則會(huì)繼續(xù)解析第二個(gè)字符直到全部解析完畢或者遇到了第一個(gè)非數(shù)字字符。
如果字符串中的第一個(gè)字符是數(shù)字字符,parseInt()也能識(shí)別各種整數(shù)格式。也就是說(shuō),如果字符串以"0x"開(kāi)頭且后跟數(shù)字字符,就會(huì)將其當(dāng)作十六進(jìn)制整數(shù),如果字符串以"0"開(kāi)頭且后跟數(shù)字字符,則會(huì)將其解析為八進(jìn)制數(shù)。
【注】對(duì)于八進(jìn)制數(shù)如 070 ,ES3和ES5存在分歧,ES3會(huì)解析為56,而ES5會(huì)解析為0。在ES5 JS引擎中,parseInt()函數(shù)已不再具備解析八進(jìn)制的能力,因此前導(dǎo)零無(wú)效,解析為0。嚴(yán)格模式下同樣如此。
【續(xù)】為消除可能產(chǎn)生的困惑,可以為這個(gè)函數(shù)提供第二個(gè)參數(shù):轉(zhuǎn)換時(shí)使用的基數(shù)(即多少進(jìn)制)。例如:
var num=parseInt("0xAF",16); //175 //而實(shí)際上如果指定了16進(jìn)制,字符串可以不帶前面的0x。var num=parseInt("AF"); //NaN var num=parseInt("AF",16); //175 //指定基數(shù)會(huì)影響轉(zhuǎn)換的輸出結(jié)果 var num1=parseInt("10",2); //2 var num2=parseInt("10",8); //8為了避免解析的錯(cuò)誤,建議無(wú)論在什么情況下都明確指定基數(shù)。
parseFloat()函數(shù)
該函數(shù)同parseInt()函數(shù)類(lèi)似,也是從第一個(gè)字符位置開(kāi)始解析每個(gè)字符,同樣解析到字符串結(jié)尾,或者解析到遇見(jiàn)一個(gè)無(wú)效的浮點(diǎn)數(shù)字符為止。也就是說(shuō),字符串中的第一個(gè)小數(shù)點(diǎn)是有效的,而第二個(gè)小數(shù)點(diǎn)就無(wú)效了。
除第一個(gè)小數(shù)點(diǎn)有效之外,parseFloat()函數(shù)與parseInt()的第二個(gè)區(qū)別在于它始終都會(huì)忽略前導(dǎo)的零。parseFloat()函數(shù)對(duì)于十六進(jìn)制格式字符串則始終會(huì)轉(zhuǎn)換成0。parseFloat()沒(méi)有指定進(jìn)制第二參數(shù)的用法。且若字符串包含的是一個(gè)可解析為整數(shù)的數(shù)(沒(méi)有小數(shù)點(diǎn)或者小數(shù)點(diǎn)后都為0),則會(huì)返回整數(shù)。
3.2.6 String類(lèi)型
字符串的表達(dá)方式:可以使用雙引號(hào)("")、單引號(hào)(’’)和反引號(hào)(``)表示。
1)字符字面量(轉(zhuǎn)義序列)
| \n | 換行 |
| \t | 制表 |
| \b | 空格 |
| \r | 回車(chē) |
| \f | 換頁(yè) |
| \` \" \’ | 字符串標(biāo)志符號(hào) |
| \xnn | 以十六進(jìn)制編碼nn表示的一個(gè)字符(其中n為0~F) |
| \unnnn | 以十六進(jìn)制編碼nnnn表示的一個(gè)Unicode字符 |
**一個(gè)轉(zhuǎn)義序列表示一個(gè)字符。**字符串的長(zhǎng)度可以使用length屬性獲取。
如果字符串中包含雙字節(jié)字符,那么length屬性返回的值可能不是準(zhǔn)確的字符數(shù)。第五章會(huì)具體討論如何解決這個(gè)問(wèn)題。
2)字符串的特點(diǎn)
ES中的字符串不可變。字符串一旦創(chuàng)建,它們的值就不能改變。要改變某個(gè)變量保存的字符串,首先要銷(xiāo)毀原來(lái)的字符串,然后再用另一個(gè)包含新值的字符串填充改變量。
3)轉(zhuǎn)換為字符串
toString()
要將一個(gè)值轉(zhuǎn)換為字符串有兩種方式。第一種是使用幾乎每個(gè)值都有的toString()方法。數(shù)值、布爾值、對(duì)象和字符串值(字符串調(diào)用該方法返回字符串的一個(gè)副本)都有該方法。但null和undefined值沒(méi)有該方法。
多數(shù)情況下,調(diào)用toString()方法不必傳遞參數(shù)。但是,在調(diào)用數(shù)值的toString()方式時(shí),可以傳遞一個(gè)參數(shù):輸出數(shù)值的基數(shù)。默認(rèn)情況,該方法以十進(jìn)制格式返回?cái)?shù)值的字符串表示。通過(guò)傳遞基數(shù),可以輸出其他任意有效進(jìn)制格式的表示。
String()
在不知道要轉(zhuǎn)換的值是不是null或undefined的情況下,可以使用String()方法,這個(gè)函數(shù)能夠?qū)⑷魏晤?lèi)型的值轉(zhuǎn)化為字符串。該方法遵循下列轉(zhuǎn)換規(guī)則:
- 如果值有toString()方法,則調(diào)用該方法(無(wú)參數(shù))并返回相應(yīng)結(jié)果;
- 如果值為null,則返回"null";
- 如果值為undefined,則返回"undefined"。
4)模板字面量 *
ES6新增了使用模板字面量定義字符串的能力。與使用單引號(hào)和雙引號(hào)不同,模板字面量保留換行字符,可以跨行定義字符串:
let str1='first line\nsecond line'; let str2=`first line second line`; console.log(str1); /*-> first linesecond line */ console.log(str2); /*-> first linesecond line */ console.log(str1===str2); //-> true顧名思義,模板字面量在定義模板時(shí)特別有用。如下html模板:
let pageHTML=` <div><a href="#"><span>Jake</span></a> </div> `; //這里可以注意,這里字符串其實(shí)是以換行符開(kāi)始的。如果打印 console.log(pageHTML[0]==='\n'); //-> 結(jié)果會(huì)是true但同時(shí),因?yàn)?strong>模板字面量會(huì)保持反引號(hào)內(nèi)部的空格,因此使用時(shí)需格外小心。(這些空格也算一個(gè)字符)
5)字符串插值 *
模板字面量最常用的一個(gè)特性是支持字符串插值,也就是可以在一個(gè)連續(xù)定義中插入一個(gè)或多個(gè)值。技術(shù)上來(lái)說(shuō),模板字面量不是字符串,而是一種特殊的Javascript句法表達(dá)式,只不過(guò)求值之后得到的是字符串。模板字面量在定義時(shí)立即求值并轉(zhuǎn)化為字符串實(shí)例,任何插入的變量也會(huì)從它們最近的作用域中取值。
使用${}實(shí)現(xiàn)字符串插值:
let name='Jack',age=18; let str=`My name is ${name}, I'm ${age} years old`;所有插入的值都會(huì)使用toString()強(qiáng)制轉(zhuǎn)型為字符串,任何JS表達(dá)式都可以用于插值(也就是說(shuō)函數(shù)和方法也可以)。嵌套的模板字符串無(wú)需轉(zhuǎn)義:
console.log('Hello, ${'world'} !'); //-> Hello, world!此外,模板也可以插入自己之前的值:
let val=''; function append(){val=`${val}abc`;console.log(val); } append(); //-> abc append(); //-> abcabc append(); //-> abcabcabc6)模板字面量標(biāo)簽函數(shù) *
模板字面量也支持定義標(biāo)簽函數(shù)(tag function),通過(guò)標(biāo)簽函數(shù)可以自定義插值行為。標(biāo)簽函數(shù)會(huì)接受被插值記號(hào)分隔后的模板和對(duì)每個(gè)表達(dá)式求值的結(jié)果。
標(biāo)簽函數(shù)本身是一個(gè)常規(guī)函數(shù),通過(guò)前綴到字面量來(lái)應(yīng)用自定義行為,如下所示。標(biāo)簽函數(shù)接收到的參數(shù)依次是原始字符串?dāng)?shù)組和對(duì)每個(gè)表達(dá)式求值的結(jié)果。這個(gè)函數(shù)的返回值是對(duì)模板字面量求值得到的字符串。
let a=6,b=9; function simpleTag(strings,aValExression,bValExression,sumValExpression){console.log(strings);console.log(aValExression);console.log(bValExression);console.log(sumValExpression);return 'foobar'; }let untaggedResult=`${a} + ${b} = ${a+b}`; let taggedResult=simpleTag`${a} + ${b} = ${a+b}`; // ["", " + ", " = ", ""] 這里是插值未生效的原始字符串?dāng)?shù)組 // 6 這里是第一個(gè)插值表達(dá)式的結(jié)果,也就是 a = 6 //9 第二個(gè)插值表達(dá)式的結(jié)果,也就是 b = 9 //15 第三個(gè)插值表達(dá)式的結(jié)果,也就是 a+b = 15console.log(untaggedResult); //-> "6 + 9 = 15" console.log(taggedResult); //-> "foobar"因?yàn)楸磉_(dá)式的參數(shù)的數(shù)量是可變的,所以通常應(yīng)該使用剩余操作符(rest operator)將它們收集到數(shù)組中:
let a=6,b=9; function simpleTag(strings,...expressions){console.log(strings);for(const exp of expressions){console.log(exp);}return 'foobar'; } //調(diào)用結(jié)果同上,不贅述對(duì)于有n個(gè)插值的模板字面量。傳給標(biāo)簽函數(shù)的表達(dá)式參數(shù)個(gè)數(shù)始終是n,加上第一個(gè)參數(shù)則傳給標(biāo)簽函數(shù)的參數(shù)始終是n+1。因此,如果想把這些字符串和對(duì)表達(dá)式求值的結(jié)果拼接起來(lái)作為默認(rèn)返回的字符串,可以這樣做:
let a=6,b=9; function zipTag(strings,...expressions){return strings[0] + expressions.map((e,i) => `${e}${strings[i+1]}`).join('');//map():參數(shù)1表示當(dāng)前元素的值;參數(shù)2表示當(dāng)前元素的索引值//join():按照給定的字符串作為分隔符拼接整個(gè)數(shù)組//拼接思路:先將原始字符串?dāng)?shù)組的第一個(gè)元素單獨(dú)拿出來(lái);將保存插值表達(dá)式結(jié)果的數(shù)組用map遍歷,返回的值為 “當(dāng)前插值表達(dá)式結(jié)果” + “對(duì)應(yīng)的下一個(gè)原始數(shù)組字符串” 所產(chǎn)生的表達(dá)式,最后用join拼接。/*例子的拼接:第一個(gè)原始字符串元素:"" ;插值表達(dá)式數(shù)組:第一次遍歷:6 + " + "; -> 返回 "6 + "第二次遍歷:9 + " = "; -> 返回 "9 + "第三次遍歷:15 + ""; -> 返回 "15"join拼接:"6 + 9 = 15"*/ } let untaggedResult=`${a} + ${b} = ${a+b}`; let taggedResult=zipTag`${a} + ${b} = ${a+b}`;console.log(untaggedResult); //-> "6 + 9 = 15" console.log(taggedResult); //-> "6 + 9 = 15"7)原始字符串 *
使用模板字面量也可以直接獲取原始的模板字面量?jī)?nèi)容(如換行符和Unicode字符),而不是被轉(zhuǎn)換后的字符表示。為此,可以使用默認(rèn)的String.raw標(biāo)簽函數(shù):
console.log(`\u00A9`); //-> ? 對(duì)應(yīng)的Unicode字符:版權(quán)符 console.log(String.raw`\u00A9`); //-> \u00A9注意:原字符串中自帶轉(zhuǎn)義序列如換行符,可以直接獲取到。但是對(duì)實(shí)際的換行行為無(wú)用,它們不會(huì)被轉(zhuǎn)換成轉(zhuǎn)義序列的形式。
另外,可以通過(guò)標(biāo)簽函數(shù)的第一個(gè)參數(shù)(即字符串?dāng)?shù)組)的**.raw屬性**取得每個(gè)字符串的原始內(nèi)容。
function printRaw(strings){for(const rawStr of strings.raw)console.log(rawStr); } printRaw`\u00A9 ${'and'} \n`; //-> \u00A9 返回的是原始內(nèi)容,而非對(duì)應(yīng)的Unicode字符 //-> \n3.2.7 symbol類(lèi)型 *
Symbol(符號(hào))是ES6新增的數(shù)據(jù)類(lèi)型。符號(hào)是原始值,且符號(hào)實(shí)例是唯一、不可變的。符號(hào)的用途是確保對(duì)象屬性使用唯一標(biāo)識(shí)符,不會(huì)發(fā)生屬性沖突的危險(xiǎn)。
1)符號(hào)的基本用法 *
符號(hào)需要使用Symbol()函數(shù)初始化。typeof操作符返回symobol:
let sym=Symbol(); console.log(typeof sym); //-> symbol可以傳入一個(gè)字符串參數(shù)作為對(duì)符號(hào)的描述。符號(hào)沒(méi)有字面量語(yǔ)法。
Symbol()函數(shù)不能用作構(gòu)造函數(shù),與new關(guān)鍵字一起使用。這樣避免創(chuàng)建符號(hào)包裝對(duì)象,像使用Boolean、String、Number一樣。如果確實(shí)想使用符號(hào)包裝對(duì)象,可以借用Object函數(shù)。
let sym=new Symbol(); console.log(sym);//-> TypeError: Symbol is not a constructorlet mySym=Symbol(); let myWrappedSym=Object(mySym); //使用Object()創(chuàng)建符號(hào)包裝對(duì)象 console.log(typeof myWrappedSym); //-> object2)使用全局符號(hào)注冊(cè)表 *
如果運(yùn)行時(shí)的不同部分需要共享和重用符號(hào)實(shí)例,那么可以用一個(gè)字符串作為鍵,在全局符號(hào)注冊(cè)表中創(chuàng)建并重用符號(hào)。使用Symbol.for()函數(shù)。
Symbol.for()對(duì)每個(gè)字符串鍵都執(zhí)行冪等操作。第一次使用某個(gè)字符串調(diào)用時(shí),它會(huì)檢查全局運(yùn)行時(shí)注冊(cè)表,發(fā)現(xiàn)不存在對(duì)應(yīng)的符號(hào),于是就會(huì)生成一個(gè)新符號(hào)實(shí)例并添加到注冊(cè)表中。后續(xù)使用相同的字符串的調(diào)用同樣會(huì)檢查注冊(cè)表,發(fā)現(xiàn)存在與該字符串對(duì)應(yīng)的符號(hào),然后就會(huì)返回該符號(hào)實(shí)例。
let fooGlobalSymbol=Symbol.for('foo'); //創(chuàng)建新符號(hào) let otherFooGlobalSymbol=symbol.for('foo'); //重用已有符號(hào) console.log(fooGlobalSymbol===otherFooGlobalSymbol);//->true//但是要注意,即便采用相同的符號(hào)描述,在全局注冊(cè)表中定義的符號(hào)和使用SYmbol()定義的符號(hào)也并不等同: let localSymbol=Symbol('foo'); console.log(localSymbol===fooGlobalSymbol); //-> false全局注冊(cè)表中的符號(hào)必須使用字符串鍵來(lái)創(chuàng)建,因此傳給Symbol.for()的任何值都會(huì)被轉(zhuǎn)換為字符串。注冊(cè)表中使用的鍵也會(huì)同時(shí)被用作符號(hào)描述。
還可以使用Symbol.keyFor()來(lái)查詢(xún)?nèi)肿?cè)表,這個(gè)方法接收符號(hào),返回該全局符號(hào)對(duì)應(yīng)的字符串鍵。若查詢(xún)的不是全局符號(hào),則返回undefined。若查詢(xún)的不是符號(hào),則會(huì)拋出TypeError。
3)使用符號(hào)作為屬性 *
凡是可以使用字符串或數(shù)值作為屬性的地方,都可以使用符號(hào)。這就包括了對(duì)象字面量屬性和Object.defineProperty() / object.definedProperties()定義的屬性。對(duì)象字面量只能在計(jì)算屬性語(yǔ)法中使用符號(hào)作為屬性。
let s1 = Symbol('foo'),s2 = Symbol('bar'),s3 = Symbol('baz'),s4 = Symbol('qux');let o = {[s1]:'foo val' } //也可以這樣寫(xiě):o[s1]=‘foo val'; console.log(o); //-> { Symbol(foo): foo val }Object.defineProperty(o, s2, {value: 'bar val'}); console.log(o); //-> {Symbol(foo): foo val, Symbol(bar): bar val}Object,defineProperties(o,{[s3]:{value:'baz val'},[s4):{value:'qux val'} }); console.log (o): /* -> {Symbol(foo): foo val, Symbol(bar): bar val,Symbol(baz): baz val, Symbol(qux): qux val} */object.getOwnPropertyNames()返回對(duì)象實(shí)例的常規(guī)屬性數(shù)組,而Object.getOwnPropertySymbols()返回對(duì)象實(shí)例的符號(hào)屬性數(shù)組。這兩個(gè)方法的返回值彼此互斥。Object.getOwnPropertyDescriptors()會(huì)返回同時(shí)包含常規(guī)和符號(hào)屬性描述符的對(duì)象。Reflect.ownKeys()會(huì)返回兩種類(lèi)型的鍵:
let s1 = Symbol('foo'),s2 = Symbol('bar'); let o = {[s1]: 'foo val',[s2]: 'bar val',baz: 'baz val',qux: 'qux val' } console.log (Object.getOwnPropertySymbols(o)); //-> [symbol(foo),Symbol(bar)] 只返回符號(hào)屬性數(shù)組console.log(Object.getOwnPropertyNames(o)); //-> ["baz","qux"] 只返回常規(guī)屬性數(shù)組,與上互斥console.log(Object.getOwnPropertyDescriptors (o)); //-> {baz: (...), qux: (...), Symbol(foo): (...), Symbol (bar):(...)} 常規(guī)屬性和符號(hào)屬性都返回了console.log(Reflect.ownkeys(o)); //-> ["baz",“qux”,Symbol(foo),Symbol (bar)] 返回的是常規(guī)屬性和符號(hào)屬性?xún)煞N的鍵因?yàn)榉?hào)屬性是對(duì)內(nèi)存中符號(hào)的一個(gè)引用,所以直接創(chuàng)建并用作屬性的符號(hào)不會(huì)丟失。但是,如果沒(méi)有顯式地保存對(duì)這些屬性的引用,那么必須遍歷對(duì)象的所有符號(hào)屬性才能找到相應(yīng)的屬性鍵:
//和上面不同,這里直接在對(duì)象中使用Symbol()創(chuàng)建了符號(hào)實(shí)例作為屬性,而沒(méi)有顯式的保存這些實(shí)例 let o = {[Symbol('foo')]: 'foo val',[Symbol('bar')]: 'bar val' } console.log(o); //-> (Symbol(foo):'foo val', Symbol(bar): 'bar val')let barSymbol = Object.getOwnPropertySymbols(o).find((symbol) => symbol.tostring().match(/bar/)); congole.log(barSymbol); //-> Symbol(bar)4)常用內(nèi)置符號(hào) *
ECMAScript 6 也引入了一批常用內(nèi)置符號(hào)(well-known symbol ),用于暴露語(yǔ)言?xún)?nèi)部行為,開(kāi)發(fā)者可以直接訪問(wèn)、重寫(xiě)或模擬這些行為。這些內(nèi)置符號(hào)都以Symbol工廠函數(shù)字符串屬性的形式存在。
這些內(nèi)置符號(hào)最重要的用途之一是重新定義它們,從而改變?cè)Y(jié)構(gòu)的行為。比如,我們知道for-of 循環(huán)會(huì)在相關(guān)對(duì)象上使用Symbol.iterator屬性,那么就可以通過(guò)在自定義對(duì)象上重新定義Symbol.iterator的值,來(lái)改變for-of在迭代該對(duì)象時(shí)的行為。
這些內(nèi)置符號(hào)也沒(méi)有什么特別之處,它們就是全局函數(shù)symbol的普通字符串屬性,指向一個(gè)符號(hào)的實(shí)例。所有內(nèi)置符號(hào)屬性都是不可寫(xiě)、不可枚舉、不可配置的。
注意:在提到ECMAScript規(guī)范時(shí),經(jīng)常會(huì)引用符號(hào)在規(guī)范中的名稱(chēng),前綴為@@。比如,@@giterator 指的就是Symbol.iterator。
PS:后續(xù)一些ES6內(nèi)置的Symbol值,也即是常用內(nèi)置符號(hào),將不在此提及。這里有篇CSDN上簡(jiǎn)單的總結(jié):JavaScriptES6內(nèi)置的Symbol值。(文章缺少書(shū)中提及的另一個(gè)內(nèi)置符號(hào):Symbol.asyncIterator。但是由于該屬性是ES2018規(guī)范的,因此只有版本非常新的瀏覽器才支持,所以也沒(méi)必要全了解。用到的話就百度吧。)
3.2.8 Obejct類(lèi)型
使用 new Object() 新建一個(gè)對(duì)象。(可以省略括號(hào),但不推薦)
Obeject類(lèi)型的每個(gè)實(shí)例都具有下列屬性和方法:
- constructor:用于創(chuàng)建當(dāng)前對(duì)象的函數(shù)。
- hasOwnProperty(propertyName):用于判斷當(dāng)前對(duì)象實(shí)例中(不是原型)是否存在給定的屬性。其中,作為參數(shù)的屬性名必須以字符串形式存在(例如: o.hasOwnProperty("name"))。
- isPrototypeOf(object):用于檢查傳入的對(duì)象是否是另一個(gè)對(duì)象的原型。
- propertyIsEnumerable(*propertyName*):用于檢查給定的屬性是否能夠使用for-in語(yǔ)句來(lái)枚舉。參數(shù)必須以字符串形式存在。
- toLocalString():返回對(duì)象的字符串表示,該字符串與執(zhí)行環(huán)境的地區(qū)對(duì)應(yīng)。
- toString():返回對(duì)象的字符串表示。
- valueOf():返回對(duì)象的字符串、數(shù)值或布爾值表示。通常與toString()方法的返回值相同。
3.3 操作符
3.3.1 一元操作符
1)遞增遞減操作符 ++ / –
遞增遞減操作符直接照搬自C語(yǔ)言,且分為前置型和后置型。
使用前置型時(shí),變量的值都是在語(yǔ)句被求值以前改變的**。且由于前置遞增和遞減操作與執(zhí)行語(yǔ)句的優(yōu)先級(jí)相等,因此整個(gè)語(yǔ)句會(huì)從左至右被求值。例如:
var num1 = 2; var num2 = 20; var num3 = --num1 + num2; //21 var num4 = num1 + num2; //21后置型遞增和遞減操作都是在包含它們的語(yǔ)句被求值后才執(zhí)行的。
var num1 = 2; var num2 = 20; var num3 = num1-- + num2; //22,此時(shí)--還未執(zhí)行 var num4 = num1 + num2; //21,使用了num1遞減后的值這些操作符適用任何類(lèi)型的值。在應(yīng)用不同的值時(shí),遵循下列規(guī)則:
- 應(yīng)用一個(gè)包含有效數(shù)字字符的字符串時(shí),先將其轉(zhuǎn)化為數(shù)字值,再執(zhí)行加減1的操作。字符串變量變成數(shù)值變量。
- 應(yīng)用一個(gè)不包含有效數(shù)字的字符串時(shí),將變量的值設(shè)置為NaN。字符串變量變?yōu)閿?shù)值變量。
- 應(yīng)用布爾值false時(shí),先將其轉(zhuǎn)化為0再執(zhí)行加減1的操作。布爾值變量變?yōu)閿?shù)值變量。
- 應(yīng)用布爾值true時(shí),先將其轉(zhuǎn)化為1再執(zhí)行加減1的操作。布爾值變量變?yōu)閿?shù)值變量。
- 應(yīng)用于對(duì)象時(shí),(后面第5章會(huì)詳細(xì)介紹)先調(diào)用對(duì)象的valueOf()方法以取得一個(gè)可供操作的值。然后對(duì)該值應(yīng)用前述規(guī)則。如果結(jié)果是NaN,則調(diào)用toString()方法后再應(yīng)用前述規(guī)則。對(duì)象變量變成數(shù)值變量。
2)一元加減操作符 + / -
一元加操作符以一個(gè)加號(hào)表示,放在數(shù)值前不會(huì)產(chǎn)生任何影響。但應(yīng)用在非數(shù)值時(shí),該操作符會(huì)像Number()轉(zhuǎn)型函數(shù)一樣對(duì)這個(gè)值進(jìn)行轉(zhuǎn)換。
一元減操作符應(yīng)用于數(shù)值時(shí),該值會(huì)變成負(fù)數(shù)。應(yīng)用于非數(shù)值時(shí),遵循與一元加操作符相同的規(guī)則,最后將值轉(zhuǎn)化為負(fù)數(shù)。
3.3.2 位操作符
位操作符用于數(shù)值的底層操作,即按內(nèi)存中表示數(shù)值的位來(lái)操作數(shù)值。位操作符并不直接操作64位的值,而是先將64位轉(zhuǎn)化為32位的整數(shù),然后執(zhí)行操作,最后再將結(jié)果轉(zhuǎn)換回64位。
對(duì)于有符號(hào)的整數(shù),32位中的前31位表示整數(shù)的值。第三十二位(即符號(hào)位)表示數(shù)值的符號(hào):0表示正數(shù),1表示負(fù)數(shù)。正數(shù)以純二進(jìn)制格式存儲(chǔ)。
負(fù)數(shù)同樣以二進(jìn)制碼存儲(chǔ),但使用的格式是二進(jìn)制補(bǔ)碼。計(jì)算補(bǔ)碼步驟:
1)按位非(NOT)
按位非操作符由一個(gè)波浪線(~)表示,執(zhí)行按位非的結(jié)果就是返回?cái)?shù)值的反碼。
var num1 = 25; //二進(jìn)制00000000000000000000000000011001 var num2 = ~num1; //二進(jìn)制1111111111111111111111111100110 alert(num2); //-26按位非操作的本質(zhì):操作數(shù)的負(fù)值減1。但相比負(fù)值減1的操作,由于按位非是在數(shù)值表示的最底層執(zhí)行操作,因此操作速度更快。
2)按位與(AND)
按位與操作符由一個(gè)和號(hào)字符(&)表示,它有兩個(gè)操作符數(shù)。從本質(zhì)上講,按位與操作就是將兩個(gè)數(shù)值的每一位對(duì)齊,然后根據(jù)***對(duì)應(yīng)位都是1時(shí)才返回1,任何一位是0,結(jié)果都是0***的規(guī)則,對(duì)相同位置上的兩個(gè)數(shù)執(zhí)行AND操作。例如:
var result = 25 & 3; alert(result); //1//底層操作:25 = 0000 0000 0000 0000 0000 0000 0001 10013 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------- AND = 0000 0000 0000 0000 0000 0000 0000 00013)按位或(OR)
按位或操作符由一個(gè)豎線符號(hào)(|)表示。同樣也有兩個(gè)操作數(shù)。根據(jù)***有一個(gè)位是1的情況下就返回1,只有兩個(gè)都是0的情況下才返回0***的規(guī)則執(zhí)行OR操作。
var result = 25 | 3; alert(result); //274)按位異或(XOR)
按位異或由一個(gè)插入符號(hào)(^)表示。也有兩個(gè)操作數(shù)。遵循兩個(gè)數(shù)值***對(duì)應(yīng)位上只有一個(gè)1時(shí)才返回1,如果對(duì)應(yīng)的兩位都是1或都是0,則返回0***的規(guī)則。
5)左移
左移操作符由兩個(gè)小于號(hào)(<<)表示,這個(gè)操作符會(huì)將數(shù)值的所有位向左移動(dòng)指定的位數(shù)。例如:
var oldValue = 2; //二進(jìn)制碼10 var newValue = oldValue << 5; //二進(jìn)制碼1000000 //向左位移后,原數(shù)值的右側(cè)多出了5個(gè)空位,左移操作會(huì)以0填充這些空位。注意:左移不會(huì)影響操作數(shù)的符號(hào)位。換言之,-2左移5位的結(jié)果是-64而非64。
6)右移
有符號(hào)的右移操作符由兩個(gè)大于號(hào)(>>)表示。這個(gè)操作符會(huì)將數(shù)值向右移動(dòng)5位,但保留符號(hào)位(即正負(fù)號(hào)標(biāo)記)。
同樣,在移位過(guò)程中,原數(shù)值也會(huì)出現(xiàn)空位,而這次的空位出現(xiàn)在原數(shù)值的左側(cè)、符號(hào)位的右側(cè)。而此時(shí)ECMAScript會(huì)用符號(hào)位的值來(lái)填充所有的空位。
7)無(wú)符號(hào)右移
無(wú)符號(hào)右移操作符以三個(gè)大于號(hào)(>>>)表示。這個(gè)操作符會(huì)將數(shù)值的所有32位都向右移動(dòng)。對(duì)正數(shù)來(lái)說(shuō),無(wú)符號(hào)右移與有符號(hào)右移相同。
對(duì)于負(fù)數(shù),無(wú)符號(hào)右移是以0填充空位而非以符號(hào)位的值。其次,無(wú)符號(hào)右移操作符會(huì)把負(fù)數(shù)的二進(jìn)制碼當(dāng)成正數(shù)的二進(jìn)制碼。由于負(fù)數(shù)以其絕對(duì)值的二進(jìn)制補(bǔ)碼形式表示,因此會(huì)導(dǎo)致無(wú)符號(hào)右移后的結(jié)果非常大。例如:
var oldValue = -64;//等于二進(jìn)制111111111111111111111111000000 var newValue = oldValue >>> 5; //等于十進(jìn)制134217726 //這里無(wú)符號(hào)右移操作符會(huì)將這個(gè)二進(jìn)制碼當(dāng)成正數(shù)的二進(jìn)制碼,換算成十進(jìn)制就是4294967232,將其右移5位,結(jié)果就變成了000001111111111111111111111110,即十進(jìn)制的134217726。3.3.3 布爾操作符
1)邏輯非
邏輯非操作符由一個(gè)感嘆號(hào)(!)表示。無(wú)論這個(gè)值是什么數(shù)據(jù)類(lèi)型,這個(gè)操作符都會(huì)返回一個(gè)布爾值然后對(duì)其求反。
同時(shí)使用兩個(gè)邏輯非操作符,實(shí)際上就會(huì)模擬Boolean()轉(zhuǎn)型函數(shù)的行為。
2)邏輯與
邏輯與操作符由兩個(gè)和號(hào)(&&)表示。邏輯與可以應(yīng)用在任何類(lèi)型的操作數(shù),在有一個(gè)操作數(shù)不是布爾值的情況下,遵循下列規(guī)則:
- 第一個(gè)操作數(shù)是對(duì)象,此時(shí)返回第二個(gè)操作數(shù);
- 第二個(gè)操作數(shù)是對(duì)象,則只有在第一個(gè)操作數(shù)的求值結(jié)果位是true的情況下才會(huì)返回該對(duì)象;
- 如果兩個(gè)操作數(shù)都是對(duì)象,則返回第二個(gè)操作數(shù);
- 如果有一個(gè)操作數(shù)是null / NaN / undefined ,則返回null / NaN / undefined 。
邏輯與操作屬于短路操作。即若第一個(gè)操作數(shù)求值結(jié)果為false,就不會(huì)對(duì)第二個(gè)數(shù)進(jìn)行求值了。
3)邏輯或
邏輯或操作符由兩個(gè)豎線符號(hào)(||)表示。邏輯或在有一個(gè)操作數(shù)不是布爾值的情況下遵循下列規(guī)則:
- 第一個(gè)操作數(shù)是對(duì)象,則返回第一個(gè)操作數(shù);
- 第一個(gè)操作數(shù)的求值結(jié)果為false,則返回第二個(gè)操作數(shù);
- 如果兩個(gè)操作數(shù)都是對(duì)象,則返回第一個(gè)對(duì)象
- 如果兩個(gè)操作數(shù)都是null / NaN / undefined ,則返回null / NaN / undefined 。
**邏輯或同屬短路操作。**即若第一個(gè)操作數(shù)求值結(jié)果為true,就不會(huì)對(duì)第二個(gè)數(shù)進(jìn)行求值了。
我們可以利用邏輯或這一行為來(lái)避免為變量賦null或undefined值。例如:
var myObject = preferredObject || backupObject;//在這個(gè)例子中,變量myObject將被賦予等號(hào)后面兩個(gè)值中一個(gè)。變量preferredObject中包含優(yōu)先賦給變量myObject的值,變量backupObject負(fù)責(zé)在preferredObject中不包含有效值的情況下提供后備值。PS:后面還有乘性操作符、加性操作符、關(guān)系操作符、相等操作符、賦值操作符、逗號(hào)操作符,就不再贅述。只要知道:
乘性操作符、加性操作符、關(guān)系操作符在操作數(shù)為非數(shù)值的情況下,執(zhí)行運(yùn)算時(shí)都可以在后臺(tái)轉(zhuǎn)換不同的數(shù)據(jù)類(lèi)型。
相等操作符:簡(jiǎn)單來(lái)說(shuō):相等( == )和不相等( != )操作符在操作數(shù)類(lèi)型不同時(shí)會(huì)先進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換再比較;而全等( === )和全不等( !== )僅作比較而不會(huì)轉(zhuǎn)換。當(dāng)然,涉及到對(duì)象的時(shí)候會(huì)復(fù)雜點(diǎn),但是這里也沒(méi)必要多做討論。
在賦值時(shí)使用逗號(hào)操作符分隔值,最終會(huì)返回表達(dá)式中的最后一個(gè)值(這種使用場(chǎng)景并不多見(jiàn),但確實(shí)存在):
let num = (5,1,2,3,0); //-> num的值會(huì)是0
3.4 語(yǔ)句
if、for、while、do-while、break、continue、switch語(yǔ)句這里不再提及。
因?yàn)椴煌扑]with語(yǔ)句,所以這里也不再提及。with語(yǔ)句在嚴(yán)格模式下會(huì)報(bào)錯(cuò)。
3.4.1 循環(huán)語(yǔ)句 `
for-in語(yǔ)句
語(yǔ)法如下:
for(property in expression) statement-
定義迭代變量時(shí)推薦使用const(就如之前使用一樣);
-
for-in不能保證返回對(duì)象屬性的順序;
-
如果要迭代的變量是null和undefined。則不執(zhí)行循環(huán)體。
for-of語(yǔ)句
語(yǔ)法如下:
for(property of expression) statement- 定義迭代變量推薦使用const;
- for-of循環(huán)會(huì)按照可迭代對(duì)象的next()方法產(chǎn)生值得順序迭代元素。可迭代對(duì)象會(huì)在第7章介紹。
- 若嘗試迭代變量的不支持迭代,則語(yǔ)句會(huì)拋出錯(cuò)誤。
注:ES2018對(duì)for-of語(yǔ)句,增加了for-await-of 循環(huán),以支持生成期約(promise)的異步可迭代對(duì)象。(這個(gè)新增循環(huán)和前面提到的常用內(nèi)置符號(hào)Symbol.asyncIterator有關(guān)系,可以自行了解)
3.4.2 標(biāo)簽語(yǔ)句
使用label語(yǔ)句可以在代碼中添加標(biāo)簽,以便將來(lái)使用。語(yǔ)法:
label : statement //實(shí)例: start : for (var i = 0; i < count; i++){alert(i); } //該例子中定義的start標(biāo)簽可以在將來(lái)由break或continue語(yǔ)句引用。加標(biāo)簽的語(yǔ)句一般都要與for語(yǔ)句等循環(huán)語(yǔ)句配合使用。3.5 函數(shù)
函數(shù)體中語(yǔ)句碰到return語(yǔ)句會(huì)立即停止執(zhí)行并退出,后續(xù)代碼不會(huì)被執(zhí)行。return語(yǔ)句也可以不帶返回值。這時(shí)候,函數(shù)會(huì)立即停止并返回undefined。這種用法最常用于提前終止函數(shù)執(zhí)行。
嚴(yán)格模式下對(duì)函數(shù)有一些限制,若發(fā)生以下情況,會(huì)發(fā)生語(yǔ)法錯(cuò)誤:
- 不能把函數(shù)命名為eval或arguments;
- 不能把函數(shù)的參數(shù)命名為eval或arguments;
- 不能出現(xiàn)兩個(gè)命名參數(shù)同名的情況。
第10章會(huì)更詳細(xì)的介紹函數(shù)。
總結(jié)
以上是生活随笔為你收集整理的《JavaScript高级程序设计(第四版)》红宝书学习笔记(1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C#数据结构-队列
- 下一篇: KMP算法的Next数组详解(转)