javascript
《JavaScript高级程序设计(第四版)》红宝书学习笔记(第五章:基本引用类型,原始值包装类型,单例内置对象)
第五章:基本引用類型
引用值(或者對象)是某個特定引用類型的實例。新對象通過使用new操作符后跟一個構造函數(constructor)來創建。
5.1 Date
這里不對Date進行詳細深入,僅基于書本列出一些常用方法。更多方法和用法請參考:https://www.runoob.com/jsref/jsref-obj-date.html。
1)基于其他其他日期和時間創建日期對象:
Date.parse 和 Date.UTC
Date.now 返回表示方法執行日期和時間的毫秒數。
2)繼承的方法:
Date類型重寫了 toLocaleString() 、toString() 、valueOf() 方法。
-
toLocaleString() 方法返回與瀏覽器運行的本地環境一致的日期和時間。
-
toString() 方法通常返回帶時區信息的日期和時間。
-
valueOf 被重寫后返回的是日期的毫秒表示。
3)日期格式化方法:
Date類型有幾個專門用于格式化日期的方法,它們都會返回字符串:
-
toDateString()顯示日期中的周幾、月、日、年(格式特定于實現);
-
toTimeString() 顯示日期中的時、分、秒和時區(格式特定于實現);
-
toLocaleDateString() 顯示日期中的周幾、月、日、年(格式特定于實現和地區);tolocaleTimeString()顯示日期中的時、分、秒(格式特定于實現);
-
toUTCString() 顯示完整的UTC日期(格式特定于實現)。
這些方法的輸出與tolocaleString()和 tostring()一樣,會因瀏覽器而異。因此不能用于在用戶界面上一致地顯示日期。
注意 還有一個方法叫 toGMTString(),這個方法跟 toUTCString() 是一樣的,目的
是為了向后兼容。不過,規范建議新代碼使用 toUTCString()。
4)日期/時間組件方法。
5.2 RegExp `
ECMAScript通過RegExp類型支持正則表達式。正則表達式使用類似Perl的簡潔語法來創建:
let expression = /pattern/flags;
這個正則表達式的pattern(模式)可以是任何簡單或復雜的正則表達式,包括字符類、限定符、分組、向前查找和反向引用。每個正則表達式可以帶零個或多個 flags(標記),用于控制正則表達式的行為。下面給出了表示匹配模式的標記。
-
g:全局模式,表示查找字符串的全部內容,而不是找到第一個匹配的內容就結束。
-
i:不區分大小寫,表示在查找匹配時忽略pattern和字符串的大小寫。
-
m:多行模式,表示查找到一行文本末尾時會繼續查找。
-
y:粘附模式,表示只查找從1astIndex開始及之后的字符串。
-
u:Unicode模式,啟用Unicode匹配。
-
s:dotA11模式,表示元字符,匹配任何字符(包括\n或\r)。
5.2.1 RegExp實例屬性
每個RegExp實例都有下列屬性,提供有關模式的各方面信息。
+ global:布爾值,表示是否設置了g標記。
+ ignoreCase:布爾值,表示是否設置了1標記。
+ unicode:布爾值,表示是否設置了u標記。
+ sticky:布爾值,表示是否設置了y標記
+ astIndex:整數,表示在源字符串中下一次搜索的開始位置,始終從0開始。
+ multiline:布爾值,表示是否設置了m標記。
+ dotA11:布爾值,表示是否設置了。標記。
+ source:正則表達式的字面量字符串(不是傳給構造函數的模式字符串),沒有開頭和結尾的斜杠。
+ f1ags:正則表達式的標記字符串。始終以字面量而非傳入構造函數的字符串模式形式返回(沒5有前后斜杠)。
通過這些屬性可以全面了解正則表達式的信息,不過實際開發中用得并不多,因為模式聲明中包含這些信息。
5.2.2 實例方法
1)exex()
Regexp實例的主要方法是 exec() ,主要用于配合捕獲組使用。
這個方法只接收一個參數,即要應用模式的字符串。如果找到了匹配項,則返回包含第一不匹配信息的數組;如果沒找到匹配項,則返回null。
返回的數組雖然是Array的實例,但包含兩個額外的屬性:index和input。index是字符串中匹配模式的起始位置,input是要查找的字符串。這個數組的第一個元素是匹配整個模式的字符串,其他元素是與表達式中的捕獲組匹配的字符串。如果模式中沒有捕獲組,則數組只包含一個元素。
這里不再繼續討論 exec() 方法的使用,可以自行百度詳細用法。或者這里我搜了兩篇說的還不錯的文章:
https://blog.csdn.net/qq_35087256/article/details/79966865 | https://segmentfault.com/a/1190000018864720
2)test()
正則表達式的另一個方法是 test(),接收一個字符串參數。如果輸入的文本與模式匹配,則參數返回true,否則返回false。
這個方法適用于只想測試模式是否匹配,而不需要實際匹配內容的情況。test() 經常用在if語句中:
let text ="000-00-0000°; let pattern/\d{3}-\d{2}-\d{4}/; if (pattern.test(text))console.log ("The pattern was matched.");在這個例子中,正達式用于測試特定的數值序列。如果輸入的文本與模式匹配,則顯示匹配成功的消息。這個用法常用于驗證用戶輸入,此時我們只在乎輸入是否有效,不關心為什么無效。
無論正則表達式是怎么創建的,繼承的方法 toLocaleString() 和 toString()都返回正則表達式的字面量表示。
注意:正則表達式的 valueOf()方法返回正則表達式本身。
5.2.3 RegExp構造函數屬性
這里不進行討論。
注意:RegExp構造函數的所有屬性都沒有任何Web標準出處,因此不要在生成環境中使用它們。
5.3 原始值包裝類型
為了方便操作原始值,ECMAScript提供了3種特殊的引用類型:Boolean、Number和String。
這些類型具有本章介紹的其他引用類型一樣的特點,但也具有與各自原始類型對應的特殊行為。每當用到某個原始值的方法或屬性時,后臺都會創建一個相應原始包裝類型的對象,從而暴露出操作原始值的各種方法。來看下面的例子:
let sl = "some text"; let s2 = sl.substring(2);在這里,s1是一個包含字符串的變量,它是一個原始值。第二行緊接著在s1上調用了substring()方法,并把結果保存在s2中。我們知道,原始值本身不是對象,因此邏輯上不應該有方法。而實際上這個例子又確實按照預期運行了。這是因為后臺進行了很多處理,從而實現了上述操作。具體來說,當第二行訪問s1時,是以讀模式訪問的,也就是要從內存中讀取變量保存的值。
在以讀模式訪問字符串值的任何時候,后臺都會執行以下3步:
創建一個string類型的實例;
調用實例上的特定方法;
銷毀實例。
可以把這3步想象成執行了如下3行ECMAScript代碼:
let s1 = new String("some text"); let s2 = s1.substring(2); s1 = null;這種行為可以讓原始值擁有有對象的行為,對布爾值和數值而言,以上3步也會在后臺發生,只不過使用的是Boolean和Number包裝類型而已。
引用類型與原始值包裝類型的主要區別在于對象的生命周期,在通過new實例化引用類型后,得到的實例會在離開作用域時被銷毀,而自動創建的原始值包裝對象則只存在于訪同它的那行代碼執行期間。
這意味著不能在運行時給原始值添加屬性和方法。比如下面的倒子:
可以顯式地使用Boolean、Number和string構造函數創建原始值包裝對象。不過應該在確實必要時再這么做,否則容易讓開發者疑惑,分不清它們到底是原始值還是引用值。在原始值包裝類型的實例上調用 typeof 會返回object,所有原始值包裝對象都會轉換為布爾值true。
另外,Object 構透函數作為一個工廠方法,能夠根據傳入值的類型返回相應原始值包裝類型的實例。比如:
let obj = new Object ("sometext"); console.log(obj intanceof string); //-> true //如果傳給Object的是字符串,則會創建一個String的實例。如果是數值,則會創建Number的實例。布爾值則會得到Boolean的實例。注意,使用new調用原始值包裝類型的構造函數,與調用同名的轉型函數并不一樣。例如:
let value = "25"; let number = Number(value); // 轉型函數 console.log(typeof number), //-> "number” let obj = new Number(value); // 構造函數 console.log(typeof obj); //-> "object"雖然不推薦顯式創建原始值包裝類型的實例,但它們對于操作原始值的功能是很重要的。每個原值包裝類型都有相應的一套方法來方便數據操作。
5.3.1 Boolean
Boolean是對應布爾值的引用類型。要創建一個Boolean對象,就使用Boolean構造函數并傳true或false,如下例所示:
let booleanobject = new Boolean(true);
Boolean 的實例會重寫 valueOf()方法,返回一個原始值true 或 false。toString()方法被調用時也會被覆蓋,返回字符串"true"或"false"。不過,Boolean對象在ECMAScript中用得很少。不僅如此,它們還容易引起誤會,尤其是在布爾表達式中使用Boolean對象時。
除此之外,原始值和引用值(Boolean對象)還有幾個區別。首先,typeof操作符對原始值返回“boolean”,但對引用值返回“object”。同樣,Boolean對象是 Boolean 類型的實例,在使用 instanceof 操作符時返回true,但對原始值則返回false,如下所示:
console.log(typeof falseObject); //-> object console.log(typeof falseValue); //-> boolean console.log(falseObject instanceof Boolean); //-> true console.log(falseValue instanceof Boolean): //-> false理解原始布爾值和Boolean對象之間的區別非常重要,強烈建議永遠不要使用后者。
5.3.2 Number `
Number是對應數值的引用類型。要創建一個Number對象,就使用Number構造函數并傳入一個數值,如下例所示:
let NumberObject = new Number(10);
1)繼承的方法:
與Boolean 類型一樣,Number 類型重寫了 valueOf() 、totocalestring() 和toString() 方法。
valueOf()方法返回Number對象表示的原始數值,另外兩個方法返回數值字符串。
toString() 方法可選地接收一個表示基數的參數,并返回相應基數形式的數值字符串。
2)將數值格式化為字符串的方法:
toFixed() 方法返回包含指定小數點位數的數值字符串,如:
let num = 10; console.log(num.toFixed(2)); //-> "10.00"如果數值本身的小數位超過了參數指定的位數,則四舍五入到最接近的小數位。這個特點可以用于處理貨幣。但是要注意,多個浮點數值的數學計算不一定得到精確的結果。
注意:toFixed()方法可以表示有0-20個小數位的數值。某些瀏覽器可能支持更大的范圍,但這是通常被支持的范圍。
toExponential(),返回以科學記數法(也稱為指數記數法)表示的數值字符串。與toFixed()一樣,該方法也接收一個參數,表示結果中小數的位。
toPrecision()方法會給根據情況返回最合理的驗出結果。可能是固定長度,也可能是科學記數形式。這個方法接收一個參數,表示結果中數字的總位數(不包含指數)。來看幾個例子:
let num = 99; console.log(num.toprecision(1)); //-> "1e+2" console.log(num.toPrecision(2)); //-> "99" console.log(num.toPrecision(3)); //-> "99.0"在這個例子中,首先要用1位數字表示數值99,得到“1e+2”,也就是100。因為99不能只用1數字來精確表示,所以這個方法就將它舍入為100,這樣就可以只用1位數字(及其科學記數法形式)來表示了。
用2位數字表示99得到“99”,用3位數字則是“99.0”。本質上,toPrecision() 方法根據數值和精度來決定調用toFixed()還是toExponentia1()。為了以正確的小數位精確表示數值,這3個方法都會向上或向下s舍入。
注意 toprecisionl()方法可以表示帶1-21個小數位的數值。某些瀏覽器可能支持更大的范圍,但這是通常被支持的范圍。
3)isInteger() 方法與安全整數 *
ES6新增了 Number.isInteger() 方法,用于辨別一個數值是否保存為整數。
IEEE 754數值格式有一個特殊的數值范圍,在這個范圍內二進制值可以表示一個整數值。這個數值范圍從Number.MIN_SAFE_INTEGER (-2^53+1) 到 Number.MAX_SAFE_INTEGER (2^53-1)。對超出這個范圍的數值,即使嘗試保存為整數,IEEE754 編碼格式也意味著二進制值可能會表示一個完全不同的數值。為了鑒別整數是否在這個范圍內,可以使用 Number.isSafeInteger()方法:
console.log(Number.isSafeInteger(-1 * (2 ** 53))); //-> false console.log(Number.isSafeInteger(-1 * (2 ** 53) + 1)); //-> true console.log(Number.isSafeInteger(2 ** 53)); //-> false console.log(Number.isSafeInteger((2 ** 53) - 1)); //-> true5.3.3 String `
String 是對應字符串的引用類型。要創建一個String對象,使用String構造函數并傳入一個數值,如下例所示:
let stringobject = new String(hello wor1d");
String對象的方法可以在所有字符串原始值上調用。3個繼承的方法 valueOf()、toLocaleString()和toString()都返回對象的原始字符串值。每個String對象都有一個1ength屬性,表示字符串中字符的數量。
1. JavaScript字符
1)16位碼元字符
JavaScript字符串由16位碼元(code unit)組成。對多數字符來說,每16位碼元對應一個字符。換句話說,字符串的 1ength屬性表示字符串包含多少16位碼元;
let message ="abcde"; console.log(message.length); //-> 5charAt () 方法返回給定索引位置的字符,由傳給方法的整數參數指定。具體來說,這個方法查找指定索引位置的16位碼元,并返回該碼元對應的字符。
charCodeAt()為法可以查看指定碼元的字將編碼碼,這個方法返回指定索引位置的碼元值,索引以整數指定:
let message = 'abcde'; // Unicode "Latin small letter C" 的編碼是 U+0063 console.log(message.charCodeAt(2)); //-> 99//十進制 99 等于十六進制 63 console.log(99 === 0x63); //-> truefromcharCode()方法用于根據價定的UTF-16碼元創建字符串中的字符。這個方法可以接受任意多個數值,并返回將所有數值對應的字符拼接起來的字符申:
//Unicode "Latin small letter a" 的編碼是 U+0061 //Unicode "Latin small letter b" 的編碼是 U+0062 //Unicode "Latin small letter C" 的編碼是 U+0063 //Unicode "Latin small letter d" 的編碼是 U+0064 console.log(String.fromCharCode(0x61,0x62,0x63,0x64)); //-> abcd//0x0061 === 97 //0x0062 === 98 //0x0063 === 99 //0x0064 === 100 console.log (String.fromCharCode(97,98,99,100); //-> abcdJavaScript字符串使用了兩種Unicode編碼混合的策略:UCS-2和UTF-16。對于可以采用16位編碼的字符(U+0000 ~ U+FFFF),這兩種編碼實際上是一樣的。
2)代理對
對于U+0000-U+FFFF范圍內的字符,length、 charAt()、charCodeAt()和 fromCharCode() 返回的結果都跟預期是一樣的。這是因為在這個范圍內,每個字符都是用16位表示的,而這幾個方法也都基于16位碼元完成操作。只要字符編碼大小與碼元大小一一對應,這些方法就能如期工作。
這個對應關系在擴展到 Unicode增補字符平面時就不成立了。問題很簡單,即16位只能唯一表示65536個字符。這對于大多數語言字符集是足夠了,在 Unicode中稱為基本多語言平面(BMP)。為了表示更多的字符,Unicode采用了一個策略,即每個字符使用另外16位去選擇一個增補平面。
這種每字符使用兩個16位碼元的策略稱為代理對。
在涉及增補平面的字符時,前面討論的字符串方法就會出問題。比如,下面的例子中使用了一個笑臉表情符號,也就是一個使用代理對編碼的字符:
//"smiling face with smiling eyes"表情符號的編碼是 U+1F60A //0x1F60A === 128522 let message="ab?de"; console.log(message.length); //-> 6 console.log(message.charAt(1)); //-> b console.log(message.charAt(2)); //-> <?> console.log(message.charAt(3)); //-> <?> console.log(message.charAt(4)); //-> dconsole.log(message.charCodeAt(1)); //-> 98 console.log(message.charCodeAt(2)); //-> 55357 console.log(message.charCodeAt(3)); //-> 56842 console.log(message.charCodeAt(4)); //-> 100console.log(String.fromCodePoint(0x1F60A)); //-> ?console.log(String.fromCharCode(97,98,55357,56842,100,101)); //-> ab?de這些方法仍然將16位碼元當作一個字符,事實上索引2和索引3對應的碼元應該被看成一個代理對,只對應一個字符。fromCharCode()方法仍然返回正確的結果,因為它實際上是基于提供的二進制表示直接組合成字符串。瀏覽器可以正確解析代理對(由兩個碼元構成),并正確地將其識別為一個Unicode笑臉字符。
為正確解析既包含單碼元字符又包含代理對字符的字符串,可以使用 codePointAt() 來代替 charCodeAt() 。跟使用charCodeAt()時類似,codePointAt() 接收16位碼元的索引并返回該索引位置上的碼點(code point)。碼點是Unicode中一個字符的完整標識。比如,“ c ”的碼點是0x0063,而“ ? ”的碼點是0x1F60A。碼點可能是16位,也可能是32位,而 codePointAt()方法可以從指定碼元位置識別完整的碼點。
let message = "ab?de"; console.log(message.codePointAt(1)); //-> 98 console.log(message.codePointAt(2)); //-> 128522 console.log(measage.codePointAt(3)); //-> 56842 console.log(message.codePointAt(4)); //-> 100注意,如果傳入的碼元索引并非代理對的開頭,就會返回錯誤的碼點。這種錯誤只有檢測單個字的時候才會出現,可以通過從左到右按正確的碼元數遍歷字符串來規避。迭代字符串可以智能地識別理對的碼點:
console.log([..."ab?de"]); //-> ["a","b","?","d","e"]fromCharCode()也有一個對應的 fromcodePoint()。 這個方法接收任意數量的碼點,返回對應字符拼接起來的字符串:
console.log(String.fromCharCode(97,98,55357,56842,100,101)); //-> ab?de console.log(String.fromCodePoint(97,98,128522,100,101)); //-> ab?de2. normalize()方法
某些 Unicode字符可以有多種編碼方式。有的字符既可以通過一個BMP字符表示,也可以通過代理對表示。
該方法主要是為字符串應用四種規范化形式:NFD、NFC,NFKD和NFKC。至于這四種形式具體含義和該方法的使用,因為應用情況的罕見這里不作總結。可以自行百度了解。
3. 字符串操作方法
concat() 用于將一個或多個字符串拼接成一個新字符串。
slice()、substr()、subString() 用于從字符串中提取子字符串。
4. 字符串位置方法
indexOf() 和 lastIndexOf() 用于在字符串中定位子字符串。
5. 字符串包含方法 *
ES6新增了3個用于判斷字符串中是否包含另一個字符串的方法:startsWith() 、 endWith() 和 includes()。
6. trim()方法
ECMAScript在所有字符串上都提供了 trim() 方法。這個方法會創建字符串的一個副本,刪除前、后所有空格符,再返回結果。原始字符串不會受影響。
trimLeft() 和 trimRight() 方法分別用于從字符串開始和結尾清理空格符。
7. repeat()方法
ECMAScript在所有字符串上都提供了 repeat() 方法。這個方法接收一個整數參數,表示要將字符串復制多少次,然后返回拼接所有副本后的結果。
8. padStart() 和 padEnd() 方法
padStart() 和 padEnd() 方法會復制字符串,如果小于指定長度,則在相應一邊填充字符,直至滿足長度條件。這兩個方法的第一個參數是長度,第二個參數是可選的填充字符串,默認為空格(U+0020)。
9. 字符串迭代與解構
字符中的原型上暴露了一個@@iterator方法,表示可以迭代字符串的每個字符。可以像下面這樣手動使用迭代器:
let message = "abc"; let stringIterator = message[Symbol.iterator]();console.log(stringIterator.next()); //-> (value:"a", done:false) console.log(stringIterator.next()); //-> (value:"b", done:false) console.log(stringIterator.next()); //-> (value:"c", done:false) console.log(stringIterator.next()); //-> (value: undefined, done: true)在for-of循環中可以通過這個迭代器按序訪問每個字符:
for (const c of "abcde"){console.log(c); }//-> a //-> b //-> c //-> d //-> e有了這個迭代器之后,字符串就可以通過解構操作符來解構了。比如,可以更方便地把字符串分割為字符數組:
let message = "abcde"; console.log([...message]); //-> ["a","b","c","d","e"]10.字符串大小寫轉換
下一組方法涉及大小寫轉換,包括4個方法:toLowerCase()、tolocaleLowerCase() 、toUpperCase() 和 toLocaleCase()。
11. 字符串模式匹配方法
匹配字符串:match() 。本質上與RegExp對象的 exec() 方法相同。
查找字符串:search() 。
替換字符串:replace() 。
拆分字符串:split() 。
12. localeCompare() 方法
5.4 單例內置對象
ECMA-262對內置對象的定義是“任何由ECMAScript實現提供、與宿主環境無關,并在ECMAScript程序開始執行時就存在的對象”。這就意味著,開發者不用顯式地實例化內置對象,因為它們已經實例化好了。前面我們已經接觸了大部分內置對象,包括Object、Array和String。本節介紹 ECMA-262定義的另外兩個單例內置對象:G1obal和Math。
5.4.1 Global
G1obal對象是ECMAScript中最特別的對象,因為代碼不會顯式地訪問它。ECMA-262規定G1obal對象為一種兜底對象,它所針對的是不屬于任何對象的屬性和方法。
事實上,不存在全局變量或全局函數這種東西。在全局作用域中定義的變量和函數都會變成G1obal對象的屬性。
本書前面介紹的函數,包括 isNaN()、isFinite()、parseInt()和 parseFloat(),實際上都是G1obal對象的方法。除了這些,G1obal對象上還有另外一些方法。
1. URL編碼方法
encodeURI()和 encodeURIComponent()方法用于編碼統一資源標識符(URI),以便傳給瀏覽器。有效的URI不能包含某些字符,比如空格。使用URI編碼方法來編碼URI可以讓瀏覽器能夠理解它們,同時又以特殊的UTF-8編碼替換掉所有無效字符。
ecnodeURI()方法用于對整個URI進行編碼,比如 “www.wrox.com/illegal value.js”。而encodeURIComponent()方法用于編碼URI中單獨的組件,比如前面URL中的 “i11egal value.js”。
2. eval() 方法
這個方法可能是整個ECMAScript語言中最強大的了。這個方法就是一個完整的ECMAScript解釋器,它接收一個參數,即一個要執行的ECMAScript字符串:
eval("alert('hello')""); //等價于下面的語句 alert("hello");當解釋器發現 eval() 調用時,會將參數解釋為實際的ES語句,然后將其插入到該位置。通過 eval() 執行的代碼屬于該調用所在的上下文,被執行的代碼與該上下文擁有相同的作用域鏈。這意味著定義在包含上下文中的變量可以在 eval() 調用內部被引用,比如下面這個例子:
let msg = "hello world"; eval("console.log(msg)"); //-> "hello world"通過 eval() 定義的任何變量和函數都不會被提升,這是因為在解析代碼的時候,它們是被包含在一個字符串中的。它們只是在 eval() 執行的時候才會被創建。
在嚴格模式下,在 eval() 內部創建的變量和函數無法被外部訪問。
注意:解釋代碼字符串的能力是非常強大的,但也非常危險。在使用 eval()的時候必須極為慎重,特別是在解釋用戶輸入的內容時。因為這個方法會對XSS利用暴露出很大的攻擊面。惡意用戶可能插入會導致你網站或應用崩潰的代碼。
3. Global對象屬性
這里不再列出。
4. window對象
雖然ECMA-262沒有規定直接訪問Global對象的方式,但瀏覽器將window對象實現為Global對象的代理。因此,所有全局作用域中聲明的變量和函數都變成了window的屬性。
window對象會在第12章更加詳細的介紹。
5.4.2 Math
這里不再介紹。
總結
以上是生活随笔為你收集整理的《JavaScript高级程序设计(第四版)》红宝书学习笔记(第五章:基本引用类型,原始值包装类型,单例内置对象)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学之路(3)-机器学习(3)-机器学习
- 下一篇: 数据产品-数据可视化大作“数据大屏”