国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

第五章 基本引用類型(下)——原始值包裝類型、

這篇具有很好參考價(jià)值的文章主要介紹了第五章 基本引用類型(下)——原始值包裝類型、。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

5.3 原始值包裝類型

????????為了方便操作原始值,ECMAScript提供了3種特殊的引用類型:Boolean、Number和String。這些類型具有本章介紹的其他引用類型一樣的特點(diǎn),但也具有與各自原始類型對應(yīng)的特殊行為。每當(dāng)用到某個(gè)原始值的方法或?qū)傩詴r(shí),后臺都會創(chuàng)建一個(gè)相應(yīng)原始包裝類型的對象,從而暴露出操作原始值的各種方法。來看下面的例子:

let s1 = "some text";
let s2 = s1.substring(2);

????????在這里,s1是一個(gè)包含字符串的變量,它是一個(gè)原始值。第二行緊接著在s1上調(diào)用了substring()方法,并把結(jié)果保存在s2中。我們知道,原始值本身不是對象,因此邏輯上不應(yīng)該有方法。而實(shí)際上這個(gè)例子又確實(shí)按照預(yù)期運(yùn)行了。這是因?yàn)楹笈_進(jìn)行了很多處理,從而實(shí)現(xiàn)了上述操
作。具體來說,當(dāng)?shù)诙性L問s1時(shí),是以讀模式訪問的,也就是要從內(nèi)存中讀取變量保存的值。在以讀模式訪問字符串值的任何時(shí)候,后臺都會執(zhí)行以下3步:?

(1) 創(chuàng)建一個(gè)String類型的實(shí)例;
(2) 調(diào)用實(shí)例上的特定方法;
(3) 銷毀實(shí)例。

可以把這3步想象成執(zhí)行了如下3行ECMAScript代碼:

let s1 = new String("some text");
let s2 = s1.substring(2);
s1 = null;

????????這種行為可以讓原始值擁有對象的行為。對布爾值和數(shù)值而言,以上3步也會在后臺發(fā)生,只不過使用的是Boolean和Number包裝類型而已。?

????????引用類型與原始值包裝類型的主要區(qū)別在于對象的生命周期。在通過new實(shí)例化引用類型后,得到的實(shí)例會在離開作用域時(shí)被銷毀,而自動創(chuàng)建的原始值包裝對象則只存在于訪問它的那行代碼執(zhí)行期間。這意味著不能在運(yùn)行時(shí)給原始值添加屬性和方法。比如下面的例子:

let s1 = "some text";
s1.color = "red";
console.log(s1.color); // undefined

?????????這里的第二行代碼嘗試給字符串s1添加了一個(gè)color屬性??墒牵谌写a訪問color屬性時(shí),它卻不見了。原因就是第二行代碼運(yùn)行時(shí)會臨時(shí)創(chuàng)建一個(gè)String對象,而當(dāng)?shù)谌写a執(zhí)行時(shí),這個(gè)對象已經(jīng)被銷毀了。實(shí)際上,第三行代碼在這里創(chuàng)建了自己的String對象,但這個(gè)對象沒有color屬性。

????????可以顯式地使用Boolean、Number和String構(gòu)造函數(shù)創(chuàng)建原始值包裝對象。不過應(yīng)該在確實(shí)必要時(shí)再這么做,否則容易讓開發(fā)者疑惑,分不清它們到底是原始值還是引用值。在原始值包裝類型的實(shí)例上調(diào)用typeof會返回"object",所有原始值包裝對象都會轉(zhuǎn)換為布爾值true。

????????另外,Object構(gòu)造函數(shù)作為一個(gè)工廠方法,能夠根據(jù)傳入值的類型返回相應(yīng)原始值包裝類型的實(shí)例。比如:

let obj = new Object("some text");
console.log(obj instanceof String); // true

?????????如果傳給Object的是字符串,則會創(chuàng)建一個(gè)String的實(shí)例。如果是數(shù)值,則會創(chuàng)建Number的實(shí)例。布爾值則會得到Boolean的實(shí)例。

????????注意,使用new調(diào)用原始值包裝類型的構(gòu)造函數(shù),與調(diào)用同名的轉(zhuǎn)型函數(shù)并不一樣。例如:

let value = "25";
let number = Number(value); // 轉(zhuǎn)型函數(shù)
console.log(typeof number); // "number"
let obj = new Number(value); // 構(gòu)造函數(shù)
console.log(typeof obj); // "object"

????????在這個(gè)例子中,變量number中保存的是一個(gè)值為25的原始數(shù)值,而變量obj中保存的是一個(gè)Number的實(shí)例。?

????????雖然不推薦顯式創(chuàng)建原始值包裝類型的實(shí)例,但它們對于操作原始值的功能是很重要的。每個(gè)原始值包裝類型都有相應(yīng)的一套方法來方便數(shù)據(jù)操作。

5.3.1 Boolean

????????Boolean是對應(yīng)布爾值的引用類型。要創(chuàng)建一個(gè)Boolean對象,就使用Boolean構(gòu)造函數(shù)并傳入true或false,如下例所示:

let booleanObject = new Boolean(true);

????????Boolean的實(shí)例會重寫valueOf()方法,返回一個(gè)原始值true或false。toString()方法被調(diào)用時(shí)也會被覆蓋,返回字符串"true"或"false"。不過,Boolean對象在ECMAScript中用得很少。不僅如此,它們還容易引起誤會,尤其是在布爾表達(dá)式中使用Boolean對象時(shí),比如:

let falseObject = new Boolean(false);
let result = falseObject && true;
console.log(result); // true

let falseValue = false;
result = falseValue && true;
console.log(result); // false

?????????在這段代碼中,我們創(chuàng)建一個(gè)值為false的Boolean對象。然后,在一個(gè)布爾表達(dá)式中通過&&操作將這個(gè)對象與一個(gè)原始值true組合起來。在布爾算術(shù)中,false && true等于false??墒?,這個(gè)表達(dá)式是對falseObject對象而不是對它表示的值(false)求值。前面剛剛說過,所有對象在布爾表達(dá)式中都會自動轉(zhuǎn)換為true,因此falseObject在這個(gè)表達(dá)式里實(shí)際上表示一個(gè)true值。那么true && true當(dāng)然是true。

????????除此之外,原始值和引用值(Boolean對象)還有幾個(gè)區(qū)別。首先,typeof操作符對原始值返回"boolean",但對引用值返回"object"。同樣,Boolean對象是Boolean類型的實(shí)例,在使用instaceof操作符時(shí)返回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對象之間的區(qū)別非常重要,強(qiáng)烈建議永遠(yuǎn)不要使用后者。

5.3.2 Number

????????Number是對應(yīng)數(shù)值的引用類型。要創(chuàng)建一個(gè)Number對象,就使用Number構(gòu)造函數(shù)并傳入一個(gè)數(shù)值,如下例所示:

let numberObject = new Number(10);

????????與Boolean類型一樣,Number類型重寫了valueOf()、toLocaleString()和toString()方法。valueOf()方法返回Number對象表示的原始數(shù)值,另外兩個(gè)方法返回?cái)?shù)值字符串。toString()方法可選地接收一個(gè)表示基數(shù)的參數(shù),并返回相應(yīng)基數(shù)形式的數(shù)值字符串,如下所示:

let num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"

????????除了繼承的方法,Number類型還提供了幾個(gè)用于將數(shù)值格式化為字符串的方法。
????????toFixed()方法返回包含指定小數(shù)點(diǎn)位數(shù)的數(shù)值字符串,如:

let num = 10;
console.log(num.toFixed(2)); // "10.00"

????????這里的toFixed()方法接收了參數(shù)2,表示返回的數(shù)值字符串要包含兩位小數(shù)。結(jié)果返回值為"10.00",小數(shù)位填充了0。如果數(shù)值本身的小數(shù)位超過了參數(shù)指定的位數(shù),則四舍五入到最接近的小數(shù)位:

let num = 10.005;
console.log(num.toFixed(2)); // "10.01"

????????toFixed()自動舍入的特點(diǎn)可以用于處理貨幣。不過要注意的是,多個(gè)浮點(diǎn)數(shù)值的數(shù)學(xué)計(jì)算不一定得到精確的結(jié)果。比如,0.1 + 0.2 =0.30000000000000004。

????????另一個(gè)用于格式化數(shù)值的方法是toExponential(),返回以科學(xué)記數(shù)法(也稱為指數(shù)記數(shù)法)表示的數(shù)值字符串。與toFixed()一樣,toExponential()也接收一個(gè)參數(shù),表示結(jié)果中小數(shù)的位數(shù)。來看下面的例子:

let num = 10;
console.log(num.toExponential(1)); // "1.0e+1"

????????這段代碼的輸出為"1.0e+1"。一般來說,這么小的數(shù)不用表示為科學(xué)記數(shù)法形式。如果想得到數(shù)值最適當(dāng)?shù)男问?,那么可以使用toPrecision()。

????????toPrecision()方法會根據(jù)情況返回最合理的輸出結(jié)果,可能是固定長度,也可能是科學(xué)記數(shù)法形式。這個(gè)方法接收一個(gè)參數(shù),表示結(jié)果中數(shù)字的總位數(shù)(不包含指數(shù))。來看幾個(gè)例子:

let num = 99;
console.log(num.toPrecision(1)); // "1e+2"
console.log(num.toPrecision(2)); // "99"
console.log(num.toPrecision(3)); // "99.0"

????????在這個(gè)例子中,首先要用1位數(shù)字表示數(shù)值99,得到"1e+2",也就是100。因?yàn)?9不能只用1位數(shù)字來精確表示,所以這個(gè)方法就將它舍入為100,這樣就可以只用1位數(shù)字(及其科學(xué)記數(shù)法形式)來表示了。用2位數(shù)字表示99得到"99",用3位數(shù)字則是"99.0"。本質(zhì)上,toPrecision()方法會根據(jù)數(shù)值和精度來決定調(diào)用toFixed()還是toExponential()。為了以正確的小數(shù)位精確表示數(shù)值,這3個(gè)方法都會向上或向下舍入。

????????與Boolean對象類似,Number對象也為數(shù)值提供了重要能力。但是,考慮到兩者存在同樣的潛在問題,因此并不建議直接實(shí)例化Number對象。在處理原始數(shù)值和引用數(shù)值時(shí),typeof和instacnceof操作符會返回不同的結(jié)果,如下所示:

let numberObject = new Number(10);
let numberValue = 10;
console.log(typeof numberObject); // "object"
console.log(typeof numberValue); // "number"
console.log(numberObject instanceof Number); // true
console.log(numberValue instanceof Number); // false

????????原始數(shù)值在調(diào)用typeof時(shí)始終返回"number",而Number對象則返回"object"。類似地,Number對象是Number類型的實(shí)例,而原始數(shù)值不是。

????????isInteger()方法與安全整數(shù)
????????ES6新增了Number.isInteger()方法,用于辨別一個(gè)數(shù)值是否保存為整數(shù)。有時(shí)候,小數(shù)位的0可能會讓人誤以為數(shù)值是一個(gè)浮點(diǎn)值:

console.log(Number.isInteger(1)); // true
console.log(Number.isInteger(1.00)); // true
console.log(Number.isInteger(1.01)); // false
5.3.3 string

????????String是對應(yīng)字符串的引用類型。要創(chuàng)建一個(gè)String對象,使用String構(gòu)造函數(shù)并傳入一個(gè)數(shù)值,如下例所示:

let stringObject = new String("hello world");

????????String對象的方法可以在所有字符串原始值上調(diào)用。3個(gè)繼承的方法valueOf()、toLocaleString()和toString()都返回對象的原始字符串值。

????????每個(gè)String對象都有一個(gè)length屬性,表示字符串中字符的數(shù)量。來看下面的例子:

let stringValue = "hello world";
console.log(stringValue.length); // "11"

????????這個(gè)例子輸出了字符串"hello world"中包含的字符數(shù)量:11。注意,即使字符串中包含雙字節(jié)字符(而不是單字節(jié)的ASCII字符),也仍然會按單字符來計(jì)數(shù)。

????????String類型提供了很多方法來解析和操作字符串。

1、JavaScript字符

????????JavaScript字符串由16位碼元(code unit)組成。對多數(shù)字符來說,每16位碼元對應(yīng)一個(gè)字符。換句話說,字符串的length屬性表示字符串包含多少16位碼元:

let message = "abcde";
console.log(message.length); // 5

????????此外,charAt()方法返回給定索引位置的字符,由傳給方法的整數(shù)參數(shù)指定。具體來說,這個(gè)方法查找指定索引位置的16位碼元,并返回該碼元對應(yīng)的字符:

let message = "abcde";
console.log(message.charAt(2)); // "c"

????????JavaScript字符串使用了兩種Unicode編碼混合的策略:UCS-2和UTF-16。對于可以采用16位編碼的字符(U+0000~U+FFFF),這兩種編碼實(shí)際上是一樣的。

????????使用charCodeAt()方法可以查看指定碼元的字符編碼。這個(gè)方法返回指定索引位置的碼元值,索引以整數(shù)指定。比如:

let message = "abcde";

// Unicode "Latin small letter C"的編碼是U+0063
console.log(message.charCodeAt(2)); // 99

// 十進(jìn)制99等于十六進(jìn)制63
console.log(99 === 0x63); // true

?????????fromCharCode()方法用于根據(jù)給定的UTF-16碼元創(chuàng)建字符串中的字符。這個(gè)方法可以接受任意多個(gè)數(shù)值,并返回將所有數(shù)值對應(yīng)的字符拼接起來的字符串:

// 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
// Unicode "Latin small letter E"的編碼是U+0065

console.log(String.fromCharCode(0x61, 0x62, 0x63, 0x64, 0x65)); // "abcde"

// 0x0061 === 97
// 0x0062 === 98
// 0x0063 === 99
// 0x0064 === 100
// 0x0065 === 101

console.log(String.fromCharCode(97, 98, 99, 100, 101)); // "abcde"

?????????對于U+0000~U+FFFF范圍內(nèi)的字符,length、charAt()、charCodeAt()和fromCharCode()返回的結(jié)果都跟預(yù)期是一樣的。這是因?yàn)樵谶@個(gè)范圍內(nèi),每個(gè)字符都是用16位表示的,而這幾個(gè)方法也都基于16位碼元完成操作。只要字符編碼大小與碼元大小一一對應(yīng),這些方法就能如期工作。

????????這個(gè)對應(yīng)關(guān)系在擴(kuò)展到Unicode增補(bǔ)字符平面時(shí)就不成立了。問題很簡單,即16位只能唯一表示65 536個(gè)字符。這對于大多數(shù)語言字符集是足夠了,在Unicode中稱為基本多語言平面(BMP)。為了表示更多的字符,Unicode采用了一個(gè)策略,即每個(gè)字符使用另外16位去選擇一個(gè)增補(bǔ)平面。這種每個(gè)字符使用兩個(gè)16位碼元的策略稱為代理對。

????????在涉及增補(bǔ)平面的字符時(shí),前面討論的字符串方法就會出問題。比如,下面的例子中使用了一個(gè)笑臉表情符號,也就是一個(gè)使用代理對編碼的字符:

// "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)); // d

console.log(message.charCodeAt(1)); // 98
console.log(message.charCodeAt(2)); // 55357
console.log(message.charCodeAt(3)); // 56842
console.log(message.charCodeAt(4)); // 100

console.log(String.fromCodePoint(0x1F60A)); // ?

console.log(String.fromCharCode(97, 98, 55357, 56842, 100, 101)); // ab?de

????????這些方法仍然將16位碼元當(dāng)作一個(gè)字符,事實(shí)上索引2和索引3對應(yīng)的碼元應(yīng)該被看成一個(gè)代理對,只對應(yīng)一個(gè)字符。fromCharCode()方法仍然返回正確的結(jié)果,因?yàn)樗鼘?shí)際上是基于提供的二進(jìn)制表示直接組合成字符串。瀏覽器可以正確解析代理對(由兩個(gè)碼元構(gòu)成),并正確地將其識別為一個(gè)Unicode笑臉字符。

????????為正確解析既包含單碼元字符又包含代理對字符的字符串,可以使用codePointAt()來代替charCodeAt()。跟使用charCodeAt()時(shí)類似,codePointAt()接收16位碼元的索引并返回該索引位置上的碼點(diǎn)(code point)。碼點(diǎn)是Unicode中一個(gè)字符的完整標(biāo)識。比如,"c"的碼點(diǎn)是0x0063,而"?"的碼點(diǎn)是0x1F60A。碼點(diǎn)可能是16位,也可能是32位,而codePointAt()方法可以從指定碼元位置識別完整的碼點(diǎn)。

let message = "ab?de";

console.log(message.codePointAt(1)); // 98
console.log(message.codePointAt(2)); // 128522
console.log(message.codePointAt(3)); // 56842
console.log(message.codePointAt(4)); // 100

????????注意,如果傳入的碼元索引并非代理對的開頭,就會返回錯(cuò)誤的碼點(diǎn)。這種錯(cuò)誤只有檢測單個(gè)字符的時(shí)候才會出現(xiàn),可以通過從左到右按正確的碼元數(shù)遍歷字符串來規(guī)避。迭代字符串可以智能地識別代理對的碼點(diǎn):

console.log([..."ab?de"]); // ["a", "b", "?", "d", "e"]

????????與charCodeAt()有對應(yīng)的codePointAt()一樣,fromCharCode()也有一個(gè)對應(yīng)的fromCodePoint()。這個(gè)方法接收任意數(shù)量的碼點(diǎn),返回對應(yīng)字符拼接起來的字符串:

console.log(String.fromCharCode(97, 98, 55357, 56842, 100, 101)); // ab?de
console.log(String.fromCodePoint(97, 98, 128522, 100, 101)); // ab?de
2、normalize()方法

????????某些Unicode字符可以有多種編碼方式。有的字符既可以通過一個(gè)BMP字符表示,也可以通過一個(gè)代理對表示。比如:

// U+00C5:上面帶圓圈的大寫拉丁字母A
console.log(String.fromCharCode(0x00C5)); // ?

// U+212B:長度單位“?!?console.log(String.fromCharCode(0x212B)); // ?

// U+004:大寫拉丁字母A
// U+030A:上面加個(gè)圓圈
console.log(String.fromCharCode(0x0041, 0x030A)); // ?

?????????比較操作符不在乎字符看起來是什么樣的,因此這3個(gè)字符互不相等。

let a1 = String.fromCharCode(0x00C5),
    a2 = String.fromCharCode(0x212B),
    a3 = String.fromCharCode(0x0041, 0x030A);

console.log(a1, a2, a3); // ?, ?, ?

console.log(a1 === a2); // false
console.log(a1 === a3); // false
console.log(a2 === a3); // false

?????????為解決這個(gè)問題,Unicode提供了4種規(guī)范化形式,可以將類似上面的字符規(guī)范化為一致的格式,無論底層字符的代碼是什么。這4種規(guī)范化形式是:NFD(Normalization Form D)、
NFC(Normalization Form C)、NFKD(Normalization Form KD)和NFKC(Normalization Form KC)??梢允褂胣ormalize()方法對字符串應(yīng)用上述規(guī)范化形式,使用時(shí)需要傳入表示哪種形式的字符串:"NFD"、"NFC"、"NFKD"或"NFKC"。

????????通過比較字符串與其調(diào)用normalize()的返回值,就可以知道該字符串是否已經(jīng)規(guī)范化了:

let a1 = String.fromCharCode(0x00C5),
    a2 = String.fromCharCode(0x212B),
    a3 = String.fromCharCode(0x0041, 0x030A);

// U+00C5是對0+212B進(jìn)行NFC/NFKC規(guī)范化之后的結(jié)果
console.log(a1 === a1.normalize("NFD")); // false
console.log(a1 === a1.normalize("NFC")); // true
console.log(a1 === a1.normalize("NFKD")); // false
console.log(a1 === a1.normalize("NFKC")); // true

// U+212B是未規(guī)范化的
console.log(a2 === a2.normalize("NFD")); // false
console.log(a2 === a2.normalize("NFC")); // false
console.log(a2 === a2.normalize("NFKD")); // false
console.log(a2 === a2.normalize("NFKC")); // false

// U+0041/U+030A是對0+212B進(jìn)行NFD/NFKD規(guī)范化之后的結(jié)果
console.log(a3 === a3.normalize("NFD")); // true
console.log(a3 === a3.normalize("NFC")); // false
console.log(a3 === a3.normalize("NFKD")); // true
console.log(a3 === a3.normalize("NFKC")); // false

????????選擇同一種規(guī)范化形式可以讓比較操作符返回正確的結(jié)果:?

let a1 = String.fromCharCode(0x00C5),
    a2 = String.fromCharCode(0x212B),
    a3 = String.fromCharCode(0x0041, 0x030A);

console.log(a1.normalize("NFD") === a2.normalize("NFD")); // true
console.log(a2.normalize("NFKC") === a3.normalize("NFKC")); // true
console.log(a1.normalize("NFC") === a3.normalize("NFC")); // true
3、字符串操作方法

????????本節(jié)介紹幾個(gè)操作字符串值的方法。首先是concat(),用于將一個(gè)或多個(gè)字符串拼接成一個(gè)新字符串。來看下面的例子:

let stringValue = "hello ";
let result = stringValue.concat("world");

console.log(result); // "hello world"
console.log(stringValue); // "hello"

????????在這個(gè)例子中,對stringValue調(diào)用concat()方法的結(jié)果是得到"hello world",但stringValue的值保持不變。concat()方法可以接收任意多個(gè)參數(shù),因此可以一次性拼接多個(gè)字符串,如下所示:

let stringValue = "hello ";
let result = stringValue.concat("world", "!");

console.log(result); // "hello world!"
console.log(stringValue); // "hello"

????????這個(gè)修改后的例子將字符串"world"和"!"追加到了"hello "后面。雖然concat()方法可以拼接字符串,但更常用的方式是使用加號操作符(+)。而且多數(shù)情況下,對于拼接多個(gè)字符串來說,使用加號更方便。

????????ECMAScript提供了3個(gè)從字符串中提取子字符串的方法:slice()、substr()和substring()。這3個(gè)方法都返回調(diào)用它們的字符串的一個(gè)子字符串,而且都接收一或兩個(gè)參數(shù)。第一個(gè)參數(shù)表示子字符串開始的位置,第二個(gè)參數(shù)表示子字符串結(jié)束的位置。對slice()和substring()而言,第二個(gè)參數(shù)是提取結(jié)束的位置(即該位置之前的字符會被提取出來)。對substr()而言,第二個(gè)參數(shù)表示返回的子字符串?dāng)?shù)量。任何情況下,省略第二個(gè)參數(shù)都意味著提取到字符串末尾。與concat()方法一樣,slice()、substr()和substring()也不會修改調(diào)用它們的字符串,而只會返回提取到的原始新字符串值。來看下面的例子:

let stringValue = "hello world";
console.log(stringValue.slice(3)); // "lo world"
console.log(stringValue.substring(3)); // "lo world"
console.log(stringValue.substr(3)); // "lo world"
console.log(stringValue.slice(3, 7)); // "lo w"
console.log(stringValue.substring(3,7)); // "lo w"
console.log(stringValue.substr(3, 7)); // "lo worl"

????????在這個(gè)例子中,slice()、substr()和substring()是以相同方式被調(diào)用的,而且多數(shù)情況下返回的值也相同。如果只傳一個(gè)參數(shù)3,則所有方法都將返回"lo world",因?yàn)?hello"中"l"位置為3。如果
傳入兩個(gè)參數(shù)3和7,則slice()和substring()返回"lo w"(因?yàn)?world"中"o"在位置7,不包含),而substr()返回"lo worl",因?yàn)榈诙€(gè)參數(shù)對它而言表示返回的字符數(shù)。

????????當(dāng)某個(gè)參數(shù)是負(fù)值時(shí),這3個(gè)方法的行為又有不同。比如,slice()方法將所有負(fù)值參數(shù)都當(dāng)成字符串長度加上負(fù)參數(shù)值。

????????而substr()方法將第一個(gè)負(fù)參數(shù)值當(dāng)成字符串長度加上該值,將第二個(gè)負(fù)參數(shù)值轉(zhuǎn)換為0。substring()方法會將所有負(fù)參數(shù)值都轉(zhuǎn)換為0。看下面的例子:

let stringValue = "hello world";

console.log(stringValue.slice(-3)); // "rld"
console.log(stringValue.substring(-3)); // "hello world"
console.log(stringValue.substr(-3)); // "rld"
console.log(stringValue.slice(3, -4)); // "lo w"
console.log(stringValue.substring(3, -4)); // "hel"
console.log(stringValue.substr(3, -4)); // "" (empty string)

????????這個(gè)例子明確演示了3個(gè)方法的差異。在給slice()和substr()傳入負(fù)參數(shù)時(shí),它們的返回結(jié)果相同。這是因?yàn)?3會被轉(zhuǎn)換為8(長度加上負(fù)參數(shù)),實(shí)際上調(diào)用的是slice(8)和substr(8)。而substring()方法返回整個(gè)字符串,因?yàn)?3會轉(zhuǎn)換為0。

????????在第二個(gè)參數(shù)是負(fù)值時(shí),這3個(gè)方法各不相同。slice()方法將第二個(gè)參數(shù)轉(zhuǎn)換為7,實(shí)際上相當(dāng)于調(diào)用slice(3, 7),因此返回"low"。而substring()方法會將第二個(gè)參數(shù)轉(zhuǎn)換為0,相當(dāng)于調(diào)
用substring(3, 0),等價(jià)于substring(0, 3),這是因?yàn)檫@個(gè)方法會將較小的參數(shù)作為起點(diǎn),將較大的參數(shù)作為終點(diǎn)。對substr()來說,第二個(gè)參數(shù)會被轉(zhuǎn)換為0,意味著返回的字符串包含零個(gè)字符,因而會返回一個(gè)空字符串。

4、字符串位置方法

????????有兩個(gè)方法用于在字符串中定位子字符串:indexOf()和lastIndexOf()。這兩個(gè)方法從字符串中搜索傳入的字符串,并返回位置(如果沒找到,則返回-1)。兩者的區(qū)別在于,indexOf()方法從字符串開頭開始查找子字符串,而lastIndexOf()方法從字符串末尾開始查找子字符串。來看下面的例子:

let stringValue = "hello world";
console.log(stringValue.indexOf("o")); // 4
console.log(stringValue.lastIndexOf("o")); // 7

????????這里,字符串中第一個(gè)"o"的位置是4,即"hello"中的"o"。最后一個(gè)"o"的位置是7,即"world"中的"o"。如果字符串中只有一個(gè)"o",則indexOf()和lastIndexOf()返回同一個(gè)位置。

????????這兩個(gè)方法都可以接收可選的第二個(gè)參數(shù),表示開始搜索的位置。這意味著,indexOf()會從這個(gè)參數(shù)指定的位置開始向字符串末尾搜索,忽略該位置之前的字符;lastIndexOf()則會從這個(gè)參數(shù)指定的位置開始向字符串開頭搜索,忽略該位置之后直到字符串末尾的字符。下面看一個(gè)例子:

let stringValue = "hello world";
console.log(stringValue.indexOf("o", 6)); // 7
console.log(stringValue.lastIndexOf("o", 6)); // 4

????????在傳入第二個(gè)參數(shù)6以后,結(jié)果跟前面的例子恰好相反。這一次,indexOf()返回7,因?yàn)樗鼜奈恢?(字符"w")開始向后搜索字符串,在位置7找到了"o"。而lastIndexOf()返回4,因?yàn)樗鼜奈恢?開始反向搜索至字符串開頭,因此找到了"hello"中的"o"。像這樣使用第二個(gè)參數(shù)并循環(huán)調(diào)用indexOf()或lastIndexOf(),就可以在字符串中找到所有的目標(biāo)子字符串,如下所示:

let stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
let positions = new Array();
let pos = stringValue.indexOf("e");

while(pos > -1) {
    positions.push(pos);
    pos = stringValue.indexOf("e", pos + 1);
}

console.log(positions); // [3,24,32,35,52]

????????這個(gè)例子逐步增大開始搜索的位置,通過indexOf()遍歷了整個(gè)字符串。首先取得第一個(gè)"e"的位置,然后進(jìn)入循環(huán),將上一次的位置加1再傳給indexOf(),確保搜索到最后一個(gè)子字符串實(shí)例之后。每個(gè)位置都保存在positions數(shù)組中,可供以后使用。

5、字符串包含方法

????????ECMAScript 6增加了3個(gè)用于判斷字符串中是否包含另一個(gè)字符串的方法:startsWith()、endsWith()和includes()。這些方法都會從字符串中搜索傳入的字符串,并返回一個(gè)表示是否包含的布爾值。它們的區(qū)別在于,startsWith()檢查開始于索引0的匹配項(xiàng),endsWith()檢查開始于索引(string.length -substring.length)的匹配項(xiàng),而includes()檢查整個(gè)字符串:

let message = "foobarbaz";

console.log(message.startsWith("foo")); // true
console.log(message.startsWith("bar")); // false

console.log(message.endsWith("baz")); // true
console.log(message.endsWith("bar")); // false

console.log(message.includes("bar")); // true
console.log(message.includes("qux")); // false

????????startsWith()和includes()方法接收可選的第二個(gè)參數(shù),表示開始搜索的位置。如果傳入第二個(gè)參數(shù),則意味著這兩個(gè)方法會從指定位置向著字符串末尾搜索,忽略該位置之前的所有字符。下面是一個(gè)例子:

let message = "foobarbaz";

console.log(message.startsWith("foo")); // true
console.log(message.startsWith("foo", 1)); // false

console.log(message.includes("bar")); // true
console.log(message.includes("bar", 4)); // false

?????????endsWith()方法接收可選的第二個(gè)參數(shù),表示應(yīng)該當(dāng)作字符串末尾的位置。如果不提供這個(gè)參數(shù),那么默認(rèn)就是字符串長度。如果提供這個(gè)參數(shù),那么就好像字符串只有那么多字符一樣:

let message = "foobarbaz";

console.log(message.endsWith("bar")); // false
console.log(message.endsWith("bar", 6)); // true
6、trim()方法

????????ECMAScript在所有字符串上都提供了trim()方法。這個(gè)方法會創(chuàng)建字符串的一個(gè)副本,刪除前、后所有空格符,再返回結(jié)果。比如:

let stringValue = " hello world ";
let trimmedStringValue = stringValue.trim();

console.log(stringValue); // " hello world "
console.log(trimmedStringValue); // "hello world"

????????由于trim()返回的是字符串的副本,因此原始字符串不受影響,即原本的前、后空格符都會保留。

?????????另外,trimLeft()和trimRight()方法分別用于從字符串開始和末尾清理空格符。

7、repeat()方法

????????ECMAScript在所有字符串上都提供了repeat()方法。這個(gè)方法接收一個(gè)整數(shù)參數(shù),表示要將字符串復(fù)制多少次,然后返回拼接所有副本后的結(jié)果。

let stringValue = "na ";
console.log(stringValue.repeat(16) + "batman");
// na na na na na na na na na na na na na na na na batman
8、padStart()和padEnd()方法

????????padStart()和padEnd()方法會復(fù)制字符串,如果小于指定長度,則在相應(yīng)一邊填充字符,直至滿足長度條件。這兩個(gè)方法的第一個(gè)參數(shù)是長度,第二個(gè)參數(shù)是可選的填充字符串,默認(rèn)為空格
(U+0020)。

let stringValue = "foo";

console.log(stringValue.padStart(6)); // " foo"
console.log(stringValue.padStart(9, ".")); // "......foo"

console.log(stringValue.padEnd(6)); // "foo "
console.log(stringValue.padEnd(9, ".")); // "foo......"

????????可選的第二個(gè)參數(shù)并不限于一個(gè)字符。如果提供了多個(gè)字符的字符串,則會將其拼接并截?cái)嘁云ヅ渲付ㄩL度。此外,如果長度小于或等于字符串長度,則會返回原始字符串。

let stringValue = "foo";

console.log(stringValue.padStart(8, "bar")); // "barbafoo"
console.log(stringValue.padStart(2)); // "foo"

console.log(stringValue.padEnd(8, "bar")); // "foobarba"
console.log(stringValue.padEnd(2)); // "foo"
9、字符串迭代與解構(gòu)

????????字符串的原型上暴露了一個(gè)@@iterator方法,表示可以迭代字符串的每個(gè)字符。可以像下面這樣手動使用迭代器:

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循環(huán)中可以通過這個(gè)迭代器按序訪問每個(gè)字符:

for (const c of "abcde") {
    console.log(c);
}
// a
// b
// c
// d
// e

?????????有了這個(gè)迭代器之后,字符串就可以通過解構(gòu)操作符來解構(gòu)了。比如,可以更方便地把字符串分割為字符數(shù)組:

let message = "abcde";

console.log([...message]); // ["a", "b", "c", "d", "e"]
?10、字符串大小寫轉(zhuǎn)換

????????下一組方法涉及大小寫轉(zhuǎn)換,包括4個(gè)方法:toLowerCase()、toLocaleLowerCase()、toUpperCase()和toLocaleUpperCase()。toLowerCase()和toUpperCase()方法是原來
就有的方法,與java.lang.String中的方法同名。toLocaleLowerCase()和toLocaleUpperCase()方法旨在基于特定地區(qū)實(shí)現(xiàn)。在很多地區(qū),地區(qū)特定的方法與通用的方法是一樣的。但在少數(shù)語言中(如土耳其語),Unicode大小寫轉(zhuǎn)換需應(yīng)用特殊規(guī)則,要使用地區(qū)特定的方法才能實(shí)現(xiàn)正確轉(zhuǎn)換。下面是幾個(gè)例子:

let stringValue = "hello world";
console.log(stringValue.toLocaleUpperCase()); // "HELLO WORLD"
console.log(stringValue.toUpperCase());       // "HELLO WORLD"
console.log(stringValue.toLocaleLowerCase()); // "hello world"
console.log(stringValue.toLowerCase());       // "hello world"

????????這里,toLowerCase()和toLocaleLowerCase()都返回hello world,而toUpperCase()和toLocaleUpperCase()都返回HELLO WORLD。通常,如果不知道代碼涉及什么語言,則最好使用地區(qū)特定的轉(zhuǎn)換方法。

11、字符串模式匹配方法

????????String類型專門為在字符串中實(shí)現(xiàn)模式匹配設(shè)計(jì)了幾個(gè)方法。第一個(gè)就是match()方法,這個(gè)方法本質(zhì)上跟RegExp對象的exec()方法相同。match()方法接收一個(gè)參數(shù),可以是一個(gè)正則表達(dá)式字符串,也可以是一個(gè)RegExp對象。來看下面的例子:

let text = "cat, bat, sat, fat";
let pattern = /.at/;

// 等價(jià)于pattern.exec(text)
let matches = text.match(pattern);
console.log(matches.index); // 0
console.log(matches[0]); // "cat"
console.log(pattern.lastIndex); // 0

????????match()方法返回的數(shù)組與RegExp對象的exec()方法返回的數(shù)組是一樣的:第一個(gè)元素是與整個(gè)模式匹配的字符串,其余元素則是與表達(dá)式中的捕獲組匹配的字符串(如果有的話)。

????????另一個(gè)查找模式的字符串方法是search()。這個(gè)方法唯一的參數(shù)與match()方法一樣:正則表達(dá)式字符串或RegExp對象。這個(gè)方法返回模式第一個(gè)匹配的位置索引,如果沒找到則返回-1。search()始終從字符串開頭向后匹配模式??聪旅娴睦樱?/p>

let text = "cat, bat, sat, fat";
let pos = text.search(/at/);
console.log(pos); // 1

????????這里,search(/at/)返回1,即"at"的第一個(gè)字符在字符串中的位置。?

????????為簡化子字符串替換操作,ECMAScript提供了replace()方法。這個(gè)方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)可以是一個(gè)RegExp對象或一個(gè)字符串(這個(gè)字符串不會轉(zhuǎn)換為正則表達(dá)式),第二個(gè)參數(shù)可以是一個(gè)字符串或一個(gè)函數(shù)。如果第一個(gè)參數(shù)是字符串,那么只會替換第一個(gè)子字符串。要想替換所有子字符串,第一個(gè)參數(shù)必須為正則表達(dá)式并且?guī)謽?biāo)記,如下面的例子所示:

let text = "cat, bat, sat, fat";
let result = text.replace("at", "ond");
console.log(result); // "cond, bat, sat, fat"

result = text.replace(/at/g, "ond");
console.log(result); // "cond, bond, sond, fond"

?????????在這個(gè)例子中,字符串"at"先傳給replace()函數(shù),而替換文本是"ond"。結(jié)果是"cat"被修改為"cond",而字符串的剩余部分保持不變。通過將第一個(gè)參數(shù)改為帶全局標(biāo)記的正則表達(dá)式,字符串中的所有"at"都被替換成了"ond"。

????????第二個(gè)參數(shù)是字符串的情況下,有幾個(gè)特殊的字符序列,可以用來插入正則表達(dá)式操作的值。ECMA-262中規(guī)定了下表中的值。

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

????????使用這些特殊的序列,可以在替換文本中使用之前匹配的內(nèi)容,如下面的例子所示:

let text = "cat, bat, sat, fat";
result = text.replace(/(.at)/g, "word ($1)");
console.log(result); // word (cat), word (bat), word (sat), word (fat)

?????????這里,每個(gè)以"at"結(jié)尾的詞都會被替換成"word"后跟一對小括號,其中包含捕獲組匹配的內(nèi)容$1。

????????replace()的第二個(gè)參數(shù)可以是一個(gè)函數(shù)。在只有一個(gè)匹配項(xiàng)時(shí),這個(gè)函數(shù)會收到3個(gè)參數(shù):與整個(gè)模式匹配的字符串、匹配項(xiàng)在字符串中的開始位置,以及整個(gè)字符串。在有多個(gè)捕獲組的情況下,每個(gè)匹配捕獲組的字符串也會作為參數(shù)傳給這個(gè)函數(shù),但最后兩個(gè)參數(shù)還是與整個(gè)模式匹配的開始位置和原始字符串。這個(gè)函數(shù)應(yīng)該返回一個(gè)字符串,表示應(yīng)該把匹配項(xiàng)替換成什么。使用函數(shù)作為第二個(gè)參數(shù)可以更細(xì)致地控制替換過程,如下所示:

function htmlEscape(text) {
    return text.replace(/[<>"&]/g, function(match, pos, originalText) {
        switch(match) {
            case "<":
                return "&lt;";
            case ">":
                return "&gt;";
            case "&":
                return "&amp;";
            case "\"":
                return "&quot;";
        }
    });
}
console.log(htmlEscape("<p class=\"greeting\">Hello world!</p>"));
// "&lt;p class=&quot;greeting&quot;&gt;Hello world!</p>"

?????????這里,函數(shù)htmlEscape()用于將一段HTML中的4個(gè)字符替換成對應(yīng)的實(shí)體:小于號、大于號、和號,還有雙引號(都必須經(jīng)過轉(zhuǎn)義)。實(shí)現(xiàn)這個(gè)任務(wù)最簡單的辦法就是用一個(gè)正則表達(dá)式查找這些字符,然后定義一個(gè)函數(shù),根據(jù)匹配的每個(gè)字符分別返回特定的HTML實(shí)體。

????????最后一個(gè)與模式匹配相關(guān)的字符串方法是split()。這個(gè)方法會根據(jù)傳入的分隔符將字符串拆分成數(shù)組。作為分隔符的參數(shù)可以是字符串,也可以是RegExp對象。(字符串分隔符不會被這個(gè)方法當(dāng)成正則表達(dá)式。)還可以傳入第二個(gè)參數(shù),即數(shù)組大小,確保返回的數(shù)組不會超過指定大小。來看下面的例子:

let colorText = "red,blue,green,yellow";
let colors1 = colorText.split(","); // ["red", "blue", "green", "yellow"]
let colors2 = colorText.split(",", 2); // ["red", "blue"]
let colors3 = colorText.split(/[^,]+/); // ["", ",", ",", ",", ""]

?????????在這里,字符串colorText是一個(gè)逗號分隔的顏色名稱符串。調(diào)用split(",")會得到包含這些顏色名的數(shù)組,基于逗號進(jìn)行拆分。要把數(shù)組元素限制為2個(gè),傳入第二個(gè)參數(shù)2即可。最后,使用正則
表達(dá)式可以得到一個(gè)包含逗號的數(shù)組。注意在最后一次調(diào)用split()時(shí),返回的數(shù)組前后包含兩個(gè)空字符串。這是因?yàn)檎齽t表達(dá)式指定的分隔符出現(xiàn)在了字符串開頭("red")和末尾("yellow")。

12、localeCompare()方法

?????????最后一個(gè)方法是localeCompare(),這個(gè)方法比較兩個(gè)字符串,返回如下3個(gè)值中的一個(gè)。

  • 如果按照字母表順序,字符串應(yīng)該排在字符串參數(shù)前頭,則返回負(fù)值。(通常是-1,具體還要看與實(shí)際值相關(guān)的實(shí)現(xiàn)。)
  • 如果字符串與字符串參數(shù)相等,則返回0。
  • 如果按照字母表順序,字符串應(yīng)該排在字符串參數(shù)后頭,則返回正值。(通常是1,具體還要看與實(shí)際值相關(guān)的實(shí)現(xiàn)。)

????????下面是一個(gè)例子:

let stringValue = "yellow";
console.log(stringValue.localeCompare("brick")); // 1
console.log(stringValue.localeCompare("yellow")); // 0
console.log(stringValue.localeCompare("zoo")); // -1

?????????在這里,字符串"yellow"與3個(gè)不同的值進(jìn)行了比較:"brick"、"yellow"和"zoo"。"brick"按字母表順序應(yīng)該排在"yellow"前頭,因此localeCompare()返回1。"yellow"等于"yellow",因此"localeCompare()"返回0。最后,"zoo"在"yellow"后面,因此localeCompare()返回-1。強(qiáng)調(diào)一
下,因?yàn)榉祷氐木唧w值可能因具體實(shí)現(xiàn)而異,所以最好像下面的示例中一樣使用localeCompare():

function determineOrder(value) {
    let result = stringValue.localeCompare(value);
    if (result < 0) {
        console.log(`The string 'yellow' comes before the string '${value}'.`);
    } else if (result > 0) {
        console.log(`The string 'yellow' comes after the string '${value}'.`);
    } else {
        console.log(`The string 'yellow' is equal to the string '${value}'.`);
    }
}
determineOrder("brick");
determineOrder("yellow");
determineOrder("zoo");

????????這樣一來,就可以保證在所有實(shí)現(xiàn)中都能正確判斷字符串的順序了。?

????????localeCompare()的獨(dú)特之處在于,實(shí)現(xiàn)所在的地區(qū)(國家和語言)決定了這個(gè)方法如何比較字符串。在美國,英語是ECMAScript實(shí)現(xiàn)的標(biāo)準(zhǔn)語言,localeCompare()區(qū)分大小寫,大寫字母排在小寫字母前面。但其他地區(qū)未必是這種情況。

13、HTML方法

????????早期的瀏覽器開發(fā)商認(rèn)為使用JavaScript動態(tài)生成HTML標(biāo)簽是一個(gè)需求。因此,早期瀏覽器擴(kuò)展了規(guī)范,增加了輔助生成HTML標(biāo)簽的方法。下表總結(jié)了這些HTML方法。不過,這些方法基本上已經(jīng)沒有人使用了,因?yàn)榻Y(jié)果通常不是語義化的標(biāo)記。

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

?

5.4 單例內(nèi)置對象

????????ECMA-262對內(nèi)置對象的定義是“任何由ECMAScript實(shí)現(xiàn)提供、與宿主環(huán)境無關(guān),并在ECMAScript程序開始執(zhí)行時(shí)就存在的對象”。這就意味著,開發(fā)者不用顯式地實(shí)例化內(nèi)置對象,因?yàn)樗鼈円呀?jīng)實(shí)例化好了。前面我們已經(jīng)接觸了大部分內(nèi)置對象,包括Object、Array和String。本節(jié)介紹ECMA-262定義的另外兩個(gè)單例內(nèi)置對象:Global和Math。

5.4.1 Global

????????Global對象是ECMAScript中最特別的對象,因?yàn)榇a不會顯式地訪問它。ECMA-262規(guī)定Global對象為一種兜底對象,它所針對的是不屬于任何對象的屬性和方法。事實(shí)上,不存在全局變量或全局函數(shù)這種東西。在全局作用域中定義的變量和函數(shù)都會變成Global對象的屬性 。本書前面介紹的函數(shù),包括isNaN()、isFinite()、parseInt()和parseFloat(),實(shí)際上都是Global對象的方法。除了這些,Global對象上還有另外一些方法。

1、URL編碼方式

????????encodeURI()和encodeURIComponent()方法用于編碼統(tǒng)一資源標(biāo)識符(URI),以便傳給瀏覽器。有效的URI不能包含某些字符,比如空格。使用URI編碼方法來編碼URI可以讓瀏覽器能夠理解它們,同時(shí)又以特殊的UTF-8編碼替換掉所有無效字符。

????????ecnodeURI()方法用于對整個(gè)URI進(jìn)行編碼,比如"www.wrox.com/illegal value.js"。而encodeURIComponent()方法用于編碼URI中單獨(dú)的組件,比如前面URL中的"illegal value.js"。這兩個(gè)方法的主要區(qū)別是,encodeURI()不會編碼屬于URL組件的特殊字符,比如冒號、斜杠、問號、井號,而encodeURIComponent()會編碼它發(fā)現(xiàn)的所有非標(biāo)準(zhǔn)字符。來看下面的例子:

let uri = "http://www.wrox.com/illegal value.js#start";
// "http://www.wrox.com/illegal%20value.js#start"
console.log(encodeURI(uri));

// "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start"
console.log(encodeURIComponent(uri));

????????這里使用encodeURI()編碼后,除空格被替換為%20之外,沒有任何變化。而encodeURIComponent()方法將所有非字母字符都替換成了相應(yīng)的編碼形式。這就是使用encodeURI()編碼整個(gè)URI,但只使用encodeURIComponent()編碼那些會追加到已有URI后面的字符串的原因。

注意 ????????一般來說,使用encodeURIComponent()應(yīng)該比使用encodeURI()的頻率更高,這是因?yàn)榫幋a查詢字符串參數(shù)比編碼基準(zhǔn)URI的次數(shù)更多。

????????與encodeURI()和encodeURIComponent()相對的是decodeURI()和decodeURIComponent()。decodeURI()只對使用encodeURI()編碼過的字符解碼。例如,%20會被替換為空格,但%23不會被替換為井號(#),因?yàn)榫柌皇怯蒭ncodeURI()替換的。類似地,decodeURIComponent()解碼所有被encodeURIComponent()編碼的字符,基本上就是解碼所有特殊值。來看下面的例子:

let uri = "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start";

// http%3A%2F%2Fwww.wrox.com%2Fillegal value.js%23start
console.log(decodeURI(uri));

// http:// www.wrox.com/illegal value.js#start
console.log(decodeURIComponent(uri));

????????這里,uri變量中包含一個(gè)使用encodeURIComponent()編碼過的字符串。首先輸出的是使用decodeURI()解碼的結(jié)果,可以看到只用空格替換了%20。然后是使用decodeURIComponent()解碼的結(jié)果,其中替換了所有特殊字符,并輸出了沒有包含任何轉(zhuǎn)義的字符串。(這個(gè)字符串不是有效的URL。)

注意 ????????URI方法encodeURI()、encodeURIComponent()、decodeURI()和
decodeURIComponent()取代了escape()和unescape()方法,后者在ECMA-262第3版中就已經(jīng)廢棄了。URI方法始終是首選方法,因?yàn)樗鼈儗λ蠻nicode字符進(jìn)行編碼,而原來的方法只能正確編碼ASCII字符。不要在生產(chǎn)環(huán)境中使用escape()和unescape()。

2、eval()方法

????????最后一個(gè)方法可能是整個(gè)ECMAScript語言中最強(qiáng)大的了,它就是eval()。這個(gè)方法就是一個(gè)完整的ECMAScript解釋器,它接收一個(gè)參數(shù),即一個(gè)要執(zhí)行的ECMAScript(JavaScript)字符串。來看一個(gè)例子:

eval("console.log('hi')");

?上面這行代碼的功能與下一行等價(jià):

console.log("hi");

????????當(dāng)解釋器發(fā)現(xiàn)eval()調(diào)用時(shí),會將參數(shù)解釋為實(shí)際的ECMAScript語句,然后將其插入到該位置。通過eval()執(zhí)行的代碼屬于該調(diào)用所在上下文,被執(zhí)行的代碼與該上下文擁有相同的作用域鏈。這意味著定義在包含上下文中的變量可以在eval()調(diào)用內(nèi)部被引用,比如下面這個(gè)例子:

let msg = "hello world";
eval("console.log(msg)"); // "hello world"

????????這里,變量msg是在eval()調(diào)用的外部上下文中定義的,而console.log()顯示了文本"hello world"。這是因?yàn)榈诙写a會被替換成一行真正的函數(shù)調(diào)用代碼。類似地,可以在eval()內(nèi)部
定義一個(gè)函數(shù)或變量,然后在外部代碼中引用,如下所示:? ? ?

eval("function sayHi() { console.log('hi'); }");
sayHi();

?????????這里,函數(shù)sayHi()是在eval()內(nèi)部定義的。因?yàn)樵撜{(diào)用會被替換為真正的函數(shù)定義,所以才可能在下一行代碼中調(diào)用sayHi()。對于變量也是一樣的:

eval("let msg = 'hello world';");
console.log(msg); // Reference Error: msg is not defined

????????通過eval()定義的任何變量和函數(shù)都不會被提升,這是因?yàn)樵诮馕龃a的時(shí)候,它們是被包含在一個(gè)字符串中的。它們只是在eval()執(zhí)行的時(shí)候才會被創(chuàng)建。

msg是一個(gè)普通變量,它的作用域是eval()函數(shù)的局部作用域,eval()執(zhí)行完畢后msg就被銷毀了,無法在外部被訪問。而sayHi函數(shù)是一種特殊的對象,eval()執(zhí)行完畢后它不會被立即銷毀,而是會在垃圾回收機(jī)制運(yùn)行并將其銷毀之前一直存在在內(nèi)存中,所以可以在外部訪問

????????通過eval()定義的任何變量和函數(shù)都不會被提升,這是因?yàn)樵诮馕龃a的時(shí)候,它們是被包含在一個(gè)字符串中的。它們只是在eval()執(zhí)行的時(shí)候才會被創(chuàng)建。
????????在嚴(yán)格模式下,在eval()內(nèi)部創(chuàng)建的變量和函數(shù)無法被外部訪問。換句話說,最后兩個(gè)例子會報(bào)錯(cuò)。同樣,在嚴(yán)格模式下,賦值給eval也會導(dǎo)致錯(cuò)誤:?

"use strict";
eval = "hi"; // 導(dǎo)致錯(cuò)誤

注意 ????????解釋代碼字符串的能力是非常強(qiáng)大的,但也非常危險(xiǎn)。在使用eval()的時(shí)候必須極為慎重,特別是在解釋用戶輸入的內(nèi)容時(shí)。因?yàn)檫@個(gè)方法會對XSS利用暴露出很大的攻擊面。惡意用戶可能插入會導(dǎo)致你網(wǎng)站或應(yīng)用崩潰的代碼。

3、Global對象屬性

????????Global對象有很多屬性,其中一些前面已經(jīng)提到過了。像undefined、NaN和Infinity等特殊值都是Global對象的屬性。此外,所有原生引用類型構(gòu)造函數(shù),比如Object和Function,也都是Global對象的屬性。下表列出了所有這些屬性。

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

4、window對象

?????????雖然ECMA-262沒有規(guī)定直接訪問Global對象的方式,但瀏覽器將window對象實(shí)現(xiàn)為Global對象的代理。因此,所有全局作用域中聲明的變量和函數(shù)都變成了window的屬性。來看下面的例子:

var color = "red";

function sayColor() {
    console.log(window.color);
}

window.sayColor(); // "red"

????????這里定義了一個(gè)名為color的全局變量和一個(gè)名為sayColor()的全局函數(shù)。在sayColor()內(nèi)部,通過window.color訪問了color變量,說明全局變量變成了window的屬性。接著,又通過window對象直接調(diào)用了window.sayColor()函數(shù),從而輸出字符串。

????????另一種獲取Global對象的方式是使用如下的代碼:

let global = function() {
    return this;
}();

????????這段代碼創(chuàng)建一個(gè)立即調(diào)用的函數(shù)表達(dá)式,返回了this的值。如前所述,當(dāng)一個(gè)函數(shù)在沒有明確(通過成為某個(gè)對象的方法,或者通過call()/apply())指定this值的情況下執(zhí)行時(shí),this值等于Global對象。因此,調(diào)用一個(gè)簡單返回this的函數(shù)是在任何執(zhí)行上下文中獲取Global對象的通用方式。

5.4.2? Math

????????ECMAScript提供了Math對象作為保存數(shù)學(xué)公式、信息和計(jì)算的地方。Math對象提供了一些輔助計(jì)算的屬性和方法。

注意 ????????Math對象上提供的計(jì)算要比直接在JavaScript實(shí)現(xiàn)的快得多,因?yàn)镸ath對象上的計(jì)算使用了JavaScript引擎中更高效的實(shí)現(xiàn)和處理器指令。但使用Math計(jì)算的問題是精度會因?yàn)g覽器、操作系統(tǒng)、指令集和硬件而異。

1、Math對象屬性

????????Math對象有一些屬性,主要用于保存數(shù)學(xué)中的一些特殊值。下表列出了這些屬性。

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

2、min()和max()方法

????????Math對象也提供了很多輔助執(zhí)行簡單或復(fù)雜數(shù)學(xué)計(jì)算的方法。min()和max()方法用于確定一組數(shù)值中的最小值和最大值。這兩個(gè)方法都接收任意多個(gè)參數(shù),如下面的例子所示:

let max = Math.max(3, 54, 32, 16);
console.log(max); // 54

let min = Math.min(3, 54, 32, 16);
console.log(min); // 3

????????在3、54、32和16中,Math.max()返回54,Math.min()返回3。使用這兩個(gè)方法可以避免使用額外的循環(huán)和if語句來確定一組數(shù)值的最大最小值。

????????要知道數(shù)組中的最大值和最小值,可以像下面這樣使用擴(kuò)展操作符:

let values = [1, 2, 3, 4, 5, 6, 7, 8];
let max = Math.max(...val);
3、舍入方法

????????接下來是用于把小數(shù)值舍入為整數(shù)的4個(gè)方法:Math.ceil()、Math.floor()、Math.round()和Math.fround()。這幾個(gè)方法處理舍入的方式如下所述。

  • Math.ceil()方法始終向上舍入為最接近的整數(shù)。
  • Math.floor()方法始終向下舍入為最接近的整數(shù)。
  • Math.round()方法執(zhí)行四舍五入。
  • Math.fround()方法返回?cái)?shù)值最接近的單精度(32位)浮點(diǎn)值表示。

????????以下示例展示了這些方法的用法:

console.log(Math.ceil(25.9)); // 26
console.log(Math.ceil(25.5)); // 26
console.log(Math.ceil(25.1)); // 26

console.log(Math.round(25.9)); // 26
console.log(Math.round(25.5)); // 26
console.log(Math.round(25.1)); // 25

console.log(Math.fround(0.4)); // 0.4000000059604645
console.log(Math.fround(0.5)); // 0.5
console.log(Math.fround(25.9)); // 25.899999618530273

console.log(Math.floor(25.9)); // 25
console.log(Math.floor(25.5)); // 25
console.log(Math.floor(25.1)); // 25

????????對于25和26(不包含)之間的所有值,Math.ceil()都會返回26,因?yàn)樗冀K向上舍入。Math.round()只在數(shù)值大于等于25.5時(shí)返回26,否則返回25。最后,Math.floor()對所有25和26(不包含)之間的值都返回25。

4、random()方法

????????Math.random()方法返回一個(gè)0~1范圍內(nèi)的隨機(jī)數(shù),其中包含0但不包含1。對于希望顯示隨機(jī)名言或隨機(jī)新聞的網(wǎng)頁,這個(gè)方法是非常方便的??梢曰谌缦鹿绞褂肕ath.random()從一組整數(shù)中隨機(jī)選擇一個(gè)數(shù):

number = Math.floor(Math.random() * total_number_of_choices + first_possible_value

????????這里使用了Math.floor()方法,因?yàn)镸ath.random()始終返回小數(shù),即便乘以一個(gè)數(shù)再加上一個(gè)數(shù)也是小數(shù)。因此,如果想從1~10范圍內(nèi)隨機(jī)選擇一個(gè)數(shù),代碼就是這樣的:

let num = Math.floor(Math.random() * 10 + 1);

????????這樣就有10個(gè)可能的值(1~10),其中最小的值是1。如果想選擇一個(gè)2~10范圍內(nèi)的值,則代碼就要寫成這樣:

let num = Math.floor(Math.random() * 9 + 2);

????????2~10只有9個(gè)數(shù),所以可選總數(shù)(total_number_of_choices)是9,而最小可能的值(first_possible_value)是2。很多時(shí)候,通過函數(shù)來算出可選總數(shù)和最小可能的值可能更方便,比如:

function selectFrom(lowerValue, upperValue) {
    let choices = upperValue - lowerValue + 1;
    return Math.floor(Math.random() * choices + lowerValue);
}
let num = selectFrom(2,10);
console.log(num); // 2~10范圍內(nèi)的值,其中包含2和10

????????這里的函數(shù)selectFrom()接收兩個(gè)參數(shù):應(yīng)該返回的最小值和最大值。通過將這兩個(gè)值相減再加1得到可選總數(shù),然后再套用上面的公式。于是,調(diào)用selectFrom(2,10)就可以從2~10(包含)范圍內(nèi)選擇一個(gè)值了。使用這個(gè)函數(shù),從一個(gè)數(shù)組中隨機(jī)選擇一個(gè)元素就很容易,比如:

let colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"];
let color = colors[selectFrom(0, colors.length-1)];

????????在這個(gè)例子中,傳給selecFrom()的第二個(gè)參數(shù)是數(shù)組長度減1,即數(shù)組最大的索引值。

注意 ????????Math.random()方法在這里出于演示目的是沒有問題的。如果是為了加密而需要生成隨機(jī)數(shù)(傳給生成器的輸入需要較高的不確定性),那么建議使用window.crypto.getRandomValues()。

5、其他方法

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

第五章 基本引用類型(下)——原始值包裝類型、,JavaScript高級程序設(shè)計(jì),javascript,ecmascript

5.5 小結(jié)

????????JavaScript中的對象稱為引用值,幾種內(nèi)置的引用類型可用于創(chuàng)建特定類型的對象。

  • 引用值與傳統(tǒng)面向?qū)ο缶幊陶Z言中的類相似,但實(shí)現(xiàn)不同。
  • Date類型提供關(guān)于日期和時(shí)間的信息,包括當(dāng)前日期、時(shí)間及相關(guān)計(jì)算。
  • RegExp類型是ECMAScript支持正則表達(dá)式的接口,提供了大多數(shù)基礎(chǔ)的和部分高級的正則表達(dá)式功能。

????????JavaScript比較獨(dú)特的一點(diǎn)是,函數(shù)實(shí)際上是Function類型的實(shí)例,也就是說函數(shù)也是對象。因?yàn)楹瘮?shù)也是對象,所以函數(shù)也有方法,可以用于增強(qiáng)其能力。
????????由于原始值包裝類型的存在,JavaScript中的原始值可以被當(dāng)成對象來使用。有3種原始值包裝類型:Boolean、Number和String。它們都具備如下特點(diǎn)。

  • 每種包裝類型都映射到同名的原始類型。
  • 以讀模式訪問原始值時(shí),后臺會實(shí)例化一個(gè)原始值包裝類型的對象,借助這個(gè)對象可以操作相應(yīng)的數(shù)據(jù)。
  • 涉及原始值的語句執(zhí)行完畢后,包裝對象就會被銷毀。

????????當(dāng)代碼開始執(zhí)行時(shí),全局上下文中會存在兩個(gè)內(nèi)置對象:Global和Math。其中,Global對象在大多數(shù)ECMAScript實(shí)現(xiàn)中無法直接訪問。不過,瀏覽器將其實(shí)現(xiàn)為window對象。所有全局變量和函數(shù)都是Global對象的屬性。Math對象包含輔助完成復(fù)雜計(jì)算的屬性和方法。文章來源地址http://www.zghlxwxcb.cn/news/detail-795339.html

到了這里,關(guān)于第五章 基本引用類型(下)——原始值包裝類型、的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • C++[第五章]--指針和引用

    引用就是別名,引用定義時(shí)必須初始化: int a; int b=a; //b即為a的別名 如果不是形參,必須初始化,引用某一變量 指針和c一樣; this指針 在類的成員函數(shù)中使用,表示當(dāng)前對象; C++11 新增了一種引用 類型 引用名 = 右值表達(dá)式; 引入右值引用如: 編譯器允許我們?yōu)?num 左值建立

    2024年02月15日
    瀏覽(23)
  • C++從入門到精通 第五章(指針與引用)

    C++從入門到精通 第五章(指針與引用)

    (1)C++程序在執(zhí)行時(shí),將內(nèi)存大方向劃分為 4個(gè)區(qū)域 : ①代碼區(qū): 存放函數(shù)體的二進(jìn)制代碼 ,由操作系統(tǒng)進(jìn)行管理的。 ②全局區(qū): 存放全局變量和靜態(tài)變量以及常量 。 ③棧區(qū): 由編譯器自動分配釋放 , 存放函數(shù)的參數(shù)值 、 局部變量等 。 ④堆區(qū): 由程序員分配和釋放

    2024年02月20日
    瀏覽(23)
  • [JavaScript] 第五章 函數(shù)、事件處理、作用域

    [JavaScript] 第五章 函數(shù)、事件處理、作用域

    春花秋月何時(shí)了,往事知多少。此付費(fèi)專欄不要訂閱,不要訂閱,聽人勸。 ??作者主頁:青花鎖 ??簡介:Java領(lǐng)域優(yōu)質(zhì)創(chuàng)作者??、Java微服務(wù)架構(gòu)公號作者?? ??簡歷模板、學(xué)習(xí)資料、面試題庫、技術(shù)互助 ??文末獲取聯(lián)系方式 ?? [Java項(xiàng)目實(shí)戰(zhàn)] 介紹Java組件安裝、使用;手

    2024年02月03日
    瀏覽(23)
  • 第五章 Django 數(shù)據(jù)模型系統(tǒng)(基本使用)

    第五章 Django 數(shù)據(jù)模型系統(tǒng)(基本使用)

    第一章 Django 基本使用 第二章 Django URL路由系統(tǒng) 第三章 Django 視圖系統(tǒng) 第四章 Django 模板系統(tǒng) 第五章 Django 數(shù)據(jù)模型系統(tǒng)(基本使用) 第六章 Django 數(shù)據(jù)模型系統(tǒng)(多表操作) 第七章 Django 用戶認(rèn)證與會話技術(shù) 第八章 Django CSRF防護(hù) 靜態(tài)網(wǎng)站和動態(tài)網(wǎng)站是兩種不同類型的網(wǎng)站,它們

    2024年02月04日
    瀏覽(29)
  • 基本數(shù)據(jù)類型和包裝類型 使用規(guī)范

    基本數(shù)據(jù)類型和包裝類型 使用規(guī)范

    自動裝箱(Autoboxing)和拆箱(Unboxing)是Java中用于在基本數(shù)據(jù)類型和其對應(yīng)的包裝類型之間進(jìn)行轉(zhuǎn)換的機(jī)制。這兩種機(jī)制的目的是使代碼更簡潔和易讀,同時(shí)提供類型安全性。 1、自動裝箱:將基本數(shù)據(jù)類型的值自動封裝成相應(yīng)的包裝類型對象。例如,將 int 轉(zhuǎn)換為 Integer,將

    2024年02月09日
    瀏覽(21)
  • JavaScript Es6_2筆記 (深入對象 + 內(nèi)置構(gòu)造函數(shù) + 包裝類型)+包含實(shí)例方法

    了解面向?qū)ο缶幊痰幕A(chǔ)概念及構(gòu)造函數(shù)的作用,體會 JavaScript 一切皆對象的語言特征,掌握常見的對象屬性和方法的使用。 了解面向?qū)ο缶幊讨械囊话愀拍?能夠基于構(gòu)造函數(shù)創(chuàng)建對象 理解 JavaScript 中一切皆對象的語言特征 理解引用對象類型值存儲的的

    2024年02月12日
    瀏覽(52)
  • JAVA包裝類和基本數(shù)據(jù)類型------JAVA入門基礎(chǔ)教程

    public class WrapperTest { ? ? public static void main(String[] args) ? ? { ? ? ? ? int i1 = 10; ? ? ? ? Integer i11 = new Integer(i1); ? ? ? ? System.out.println(i11); ? ? ? ? float f1 = 12.3F; ? ? ? ? f1 = 32.2F; ? ? ? ? Float ff1 = new Float(f1); ? ? ? ? System.out.println(ff1); ? ? ? ? String s1 = \\\"32.1\\\"; ? ? ? ? F

    2024年02月02日
    瀏覽(24)
  • Java 中有了基本類型為什么還需要包裝類?

    Java 中有8種基本數(shù)據(jù)類型,這些基本類型又都有對應(yīng)的包裝類。 分類 基本數(shù)據(jù)類型 包裝類 布爾型 boolean Boolean 整型 byte Byte short Short int Integer long Long 字符型 char Character 浮點(diǎn)型 float Float double Double 因?yàn)?Java 是一種面向?qū)ο笳Z言,很多地方都需要使用對象而不是基本數(shù)據(jù)類型。

    2024年02月14日
    瀏覽(26)
  • 網(wǎng)絡(luò)_TCP/IP_第五章_交換機(jī)的基本原理與配置_實(shí)驗(yàn)案例二:交換機(jī)的基本配置

    網(wǎng)絡(luò)_TCP/IP_第五章_交換機(jī)的基本原理與配置_實(shí)驗(yàn)案例二:交換機(jī)的基本配置

    如圖5.18所示、兩臺交換機(jī)互連,并與四臺計(jì)算機(jī)連接在一起,設(shè)備之間接口的連接情況如表5-4所示。 圖 5-18 實(shí)驗(yàn)案例二示意圖 表5-4 設(shè)備之間接口的連接情況 ? ? ? ? 通過命令查看MAC地址表,觀察各個(gè)接口對應(yīng)的MAC地址;配置交換機(jī)互連接口的雙工模式及速率,觀察在雙工模式

    2024年04月10日
    瀏覽(21)
  • == 和 equles()基于字符串、基本數(shù)據(jù)類型、包裝類應(yīng)用的不同和原理

    == 和 equles()基于字符串、基本數(shù)據(jù)類型、包裝類應(yīng)用的不同和原理

    對于 == 和 equals() 大家都很熟悉,大多也知道結(jié)論,但是運(yùn)用的時(shí)候,有時(shí)候根據(jù)結(jié)論來,完全是摸不著頭腦,所以我在這系統(tǒng)的簡述一下關(guān)于兩者在基本數(shù)據(jù)類型、字符串類型、包裝類這三個(gè)應(yīng)用上的區(qū)別。 下面我主要是說 == 的運(yùn)用,因?yàn)閑quals()對于字符串就是比較內(nèi)容,

    2024年03月19日
    瀏覽(32)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包