您好,登錄后才能下訂單哦!
這篇文章主要介紹JavaScript原始值與包裝對(duì)象的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
1、js屬于一種解釋性腳本語(yǔ)言;2、在絕大多數(shù)瀏覽器的支持下,js可以在多種平臺(tái)下運(yùn)行,擁有著跨平臺(tái)特性;3、js屬于一種弱類(lèi)型腳本語(yǔ)言,對(duì)使用的數(shù)據(jù)類(lèi)型未做出嚴(yán)格的要求,能夠進(jìn)行類(lèi)型轉(zhuǎn)換,簡(jiǎn)單又容易上手;4、js語(yǔ)言安全性高,只能通過(guò)瀏覽器實(shí)現(xiàn)信息瀏覽或動(dòng)態(tài)交互,從而有效地防止數(shù)據(jù)的丟失;5、基于對(duì)象的腳本語(yǔ)言,js不僅可以創(chuàng)建對(duì)象,也能使用現(xiàn)有的對(duì)象。
原始類(lèi)型也被稱(chēng)為“基本類(lèi)型”。
目前在 JavaScript 中有以下幾種原始類(lèi)型:
string(字符串)
number(數(shù)字)
boolean(布爾)
null(空)
undefined(未定義)
bigint(大整數(shù),ES6)
symbol(標(biāo)志?ES6)
? 如下:
typeof 'chenpipi'; // "string" typeof 12345; // "number" typeof true; // "boolean" typeof null; // "object" typeof undefined; // "undefined" typeof 12345n; // "bigint" typeof Symbol(); // "symbol"
? 特別注意
typeof null 雖然返回 "object",但是這不代表 null 就是對(duì)象,這其實(shí)是 JavaScript 的一個(gè) Bug,且從 JavaScript 誕生以來(lái)便如此。
在 JavaScript 最初的實(shí)現(xiàn)中,JavaScript 中的值是由一個(gè)表示類(lèi)型的標(biāo)簽和實(shí)際數(shù)據(jù)值表示的。對(duì)象的類(lèi)型標(biāo)簽是 0。由于 null 代表的是空指針(大多數(shù)平臺(tái)下值為 0x00),因此,null 的類(lèi)型標(biāo)簽是 0,typeof null 也因此返回 "object"。
The history of “typeof null”:https://2ality.com/2013/10/typeof-null.html
原始值也就是原始類(lèi)型的值(數(shù)據(jù))。
A primitive value is data that is not an object and has no methods.
原始值是一種沒(méi)有任何方法的非對(duì)象數(shù)據(jù)。
也就是說(shuō),string、number 和 boolean 等原始類(lèi)型的值本身是沒(méi)有任何屬性和方法的。
這個(gè)時(shí)候嗅覺(jué)敏銳的小伙伴是不是已經(jīng)察覺(jué)到有什么不對(duì)勁了?
是孜然!我加了孜然!(手動(dòng)狗頭并劃掉)
? 這里有一個(gè)非常有意思的點(diǎn),但是在討論這個(gè)問(wèn)題之前,先讓我們認(rèn)識(shí)下包裝對(duì)象。
除了 null 和 undefined 外的原始類(lèi)型都有其相應(yīng)的包裝對(duì)象:
String(字符串)
Number(數(shù)字)
Boolean(布爾)
BigInt(大整數(shù),ES6)
Symbol(標(biāo)志?ES6)
對(duì)象是引用類(lèi)型。
首先,包裝對(duì)象本身是一個(gè)對(duì)象,也是函數(shù)。
String instanceof Object; // true String instanceof Function; // true
實(shí)例 (Instance)
其中 String、Number 和 Boolean 均支持使用 new 運(yùn)算符來(lái)創(chuàng)建對(duì)應(yīng)的包裝對(duì)象實(shí)例。
? 例如 String 的聲明(節(jié)選):
interface StringConstructor { new(value?: any): String; (value?: any): string; readonly prototype: String; } declare var String: StringConstructor;
? 使用 new 運(yùn)算符得到的數(shù)據(jù)是對(duì)象(Object):
// 字符串 typeof 'pp'; // "string" typeof new String('pp'); // "object" new String() instanceof Object; // true // 數(shù)字 typeof 123; // "number" typeof new Number(123); // "object" new Number() instanceof Object; // true // 布爾 typeof true; // "boolean" typeof new Boolean(true); // "object" new Boolean() instanceof Object; // true
? 我們可以調(diào)用包裝對(duì)象實(shí)例的 valueOf() 函數(shù)來(lái)獲取其原始值:
// 字符串 let s = new String('pp'); s.valueOf(); // "pp" typeof s.valueOf(); // "string" // 數(shù)字 let n = new Number(123); n.valueOf(); // 123 typeof n.valueOf(); // "number" // 布爾 let b = new Boolean(true); b.valueOf(); // true typeof b.valueOf(); // "boolean"
“異類(lèi)” (Attention)
而 BigInt 和 Symbol 都屬于“不完整的類(lèi)”,不支持 new 運(yùn)算符。
? 例如 BigInt 的聲明(節(jié)選):
interface BigIntConstructor { (value?: any): bigint; readonly prototype: BigInt; } declare var BigInt: BigIntConstructor;
可以看到 BigInt 的聲明中沒(méi)有 new 運(yùn)算符相關(guān)函數(shù)。
包裝對(duì)象也可以作為普通函數(shù)來(lái)使用。
其中 String()、Number() 和 Boolean() 函數(shù)都可以用來(lái)對(duì)任意類(lèi)型的數(shù)據(jù)進(jìn)行顯式類(lèi)型轉(zhuǎn)換。
另外 Object() 函數(shù)也可用于顯式類(lèi)型轉(zhuǎn)換,但本文不再展開(kāi)。
String
? 示例代碼:
typeof String(); // "string" String(); // "" String('pp'); // "pp" String(123); // "123" String(true); // "true" String(false); // "false" String(null); // "null" String(undefined); // "undefined" String([]); // "" String({}); // "[object Object]"
? 小貼士 1
當(dāng)我們使用 String() 函數(shù)來(lái)轉(zhuǎn)換對(duì)象時(shí),JavaScript 會(huì)先訪問(wèn)對(duì)象上的 toString() 函數(shù),如果沒(méi)有實(shí)現(xiàn),則會(huì)順著原型鏈向上查找。
? 舉個(gè)栗子:執(zhí)行 String({ toString() { return 'pp'; } }) 返回的結(jié)果是 "pp",并非 "[object Object]"。
所以 String() 函數(shù)并不能夠用來(lái)判斷一個(gè)值是否為對(duì)象(會(huì)翻車(chē))。
? 小貼士 2
常用的判斷對(duì)象的方式為 Object.prototype.toString({}) === '[object Object]'。
? 舉個(gè)栗子:執(zhí)行 Object.prototype.toString({ toString() { return 'pp'; } }) 返回的是 "[object Object]"。
Number
? 示例代碼:
typeof Number(); // "number" Number(); // 0 Number(''); // 0 Number('pp'); // NaN Number(123); // 123 Number(true); // 1 Number(false); // 0 Number(null); // 0 Number(undefined); // NaN Number([]); // 0 Number({}); // NaN
? 小貼士
對(duì)于 Number() 函數(shù)來(lái)說(shuō),可能最實(shí)用的轉(zhuǎn)換就是將 true 和 false 轉(zhuǎn)換為 1 和 0 吧。
Boolean
? 示例代碼:
typeof Boolean(); // "boolean" Boolean(); // false Boolean(''); // false Boolean('pp'); // true Boolean(0); // false Boolean(1); // true Boolean(null); // false Boolean(undefined); // false Boolean([]); // true Boolean({}); // true
? 小貼士
某些情況下,我們會(huì)在數(shù)據(jù)中使用 0 和 1 來(lái)表示真假狀態(tài),此時(shí)就可以使用 Boolean() 進(jìn)行狀態(tài)的判斷。
BigInt
BigInt() 函數(shù)用于將整數(shù)轉(zhuǎn)換為大整數(shù)。
該函數(shù)接受一個(gè)整數(shù)作為參數(shù),傳入?yún)?shù)若為浮點(diǎn)數(shù)或任何非數(shù)字類(lèi)型數(shù)據(jù)都會(huì)報(bào)錯(cuò)。
? 示例代碼:
BigInt(123); // 123n BigInt(123n); // 123n typeof 123n; // "bigint" typeof BigInt(123); // "bigint"
BigInt & Number
需要注意的是,BigInt 和 Number 是不嚴(yán)格相等(寬松相等)的。
? 示例代碼:
123n === 123; // false 123n == 123; // true
Symbol
Symbol() 函數(shù)用于創(chuàng)建一個(gè) symbol 類(lèi)型的值。
該函數(shù)接受一個(gè)字符串作為描述符(參數(shù)),如果傳入其他類(lèi)型的值則會(huì)被轉(zhuǎn)換為字符串(除了 undefined)。
注意,每一個(gè) symbol 值都是獨(dú)一無(wú)二的,即使它們的描述符都是一樣的。
且 symbol 類(lèi)型的數(shù)據(jù)只能通過(guò) Symbol() 函數(shù)來(lái)創(chuàng)建。
? 示例代碼:
// 后面的返回值是 Devtools 模擬出來(lái)的,并非實(shí)際值 Symbol('pp'); // Symbol(pp) Symbol(123); // Symbol(123) Symbol(null); // Symbol(null) Symbol({}); // Symbol([object Object]) // 類(lèi)型 typeof Symbol('pp'); // "symbol" Symbol('pp') === Symbol('pp'); // false // 描述符 Symbol('pp').description; // "pp" Symbol(123).description; // "123" Symbol({}).description; // "[object Object]" Symbol().description; // undefined Symbol(undefined).description; // undefined
? 有意思的來(lái)了~
本文前面有提到:「原始值是一種沒(méi)有任何方法的非對(duì)象數(shù)據(jù)?!?/p>
我們都知道對(duì)象(Object)上可以有屬性和方法。
但是字符串不是對(duì)象,所以你不能給字符串增加屬性。
? 做個(gè)小實(shí)驗(yàn):
let a = 'chenpipi'; console.log(a.length); // 8 // 嘗試增加新的屬性 a.name = '吳彥祖'; console.log(a.name); // undefined // 嘗試修改已有的屬性 typeof a.slice; // "function" a.slice = null; typeof a.slice; // "function"
? 渣皮小劇場(chǎng)
此時(shí)一位頭鐵的小伙伴使用了反駁技能。
渣皮你別在這忽悠人了,我平時(shí)寫(xiě) Bug 哦不寫(xiě)代碼的時(shí)候明明可以調(diào)用到字符串、數(shù)字和布爾值上的方法!
? 比如下面這段代碼,能夠正常執(zhí)行并得到符合預(yù)期的結(jié)果:
// 字符串 let s = 'chenpipi'; s.toUpperCase(); // "CHENPIPI" 'ChenPiPi'.slice(4); // "PiPi" // 數(shù)字 let n = 123; n.toString(); // "123" (123.45).toFixed(2); // "123.5" // 布爾值 let b = true; b.toString(); // "true" false.toString(); // "false"
? 無(wú)用小知識(shí)
有沒(méi)有發(fā)現(xiàn),數(shù)字的字面量后面不能直接調(diào)用函數(shù)?例如執(zhí)行 123.toString() 會(huì)報(bào) SyntaxError(語(yǔ)法錯(cuò)誤)。
這是因?yàn)閿?shù)字(浮點(diǎn)數(shù))本身會(huì)用到小數(shù)點(diǎn) .,而調(diào)用函數(shù)也需要用小數(shù)點(diǎn),這時(shí)就出現(xiàn)了歧義(字符串和布爾值就沒(méi)有這種煩惱)。
對(duì)于這種情況,我們可以使用括號(hào) () 將數(shù)字包裹起來(lái),如 (123).toString();或者使用兩個(gè)連續(xù)的小數(shù)點(diǎn) .. 來(lái)調(diào)用函數(shù),如 123..toString()。
? 奇了怪了
那么既然字符串不是對(duì)象,那么為什么字符串會(huì)有屬性和方法呢?
轉(zhuǎn)念一想,數(shù)字就是數(shù)字,數(shù)字身上怎么會(huì)有方法呢?
這確實(shí)不符合邏輯,但是這又與實(shí)際相矛盾。
咋回事呢???
答案揭曉~
? 暗中操作
以字符串(string)為例,當(dāng)我們?cè)诖a中讀取字符串的屬性或者方法時(shí), JavaScript 會(huì)靜默地執(zhí)行下面的操作:
將字符串通過(guò) new String() 的方式來(lái)創(chuàng)建一個(gè)臨時(shí)的包裝對(duì)象實(shí)例;
通過(guò)創(chuàng)建的對(duì)象來(lái)執(zhí)行我們的代碼邏輯(讀取屬性或執(zhí)行函數(shù));
臨時(shí)對(duì)象不再使用,可以被銷(xiāo)毀。
? 如下面的栗子:
let a = 'chenpipi'; console.log(a); // "chenpipi" // ------------------------------ let b1 = a.length; console.log(b1); // 8 // 上面的代碼相當(dāng)于: let b2 = (new String(a)).length; console.log(b2); // 8 // ------------------------------ let c1 = a.toUpperCase(); console.log(c1); // "CHENPIPI" // 上面的代碼相當(dāng)于: let c2 = (new String(a)).toUpperCase(); console.log(c2); // "CHENPIPI"
數(shù)字(number)和布爾值(boolean)同理,但數(shù)字通過(guò) new Number() 來(lái)創(chuàng)建臨時(shí)對(duì)象,而布爾值則通過(guò) new Boolean() 來(lái)創(chuàng)建。
? 除了上面的例子,最有力的證明,就是他們的構(gòu)造函數(shù):
'chenpipi'.constructor === String; // true (12345).constructor === Number; // true true.constructor === Boolean; // true
這一切都是 JavaScript 在暗中完成的,且過(guò)程中產(chǎn)生的臨時(shí)對(duì)象都是一次性的(用完就丟)。
? 原來(lái)如此
蕪湖,這么一來(lái)就說(shuō)得通了!
這也就能解釋為什么我們能夠訪問(wèn)字符串上的屬性和方法,卻不能增加或修改屬性。
那是因?yàn)槲覀儗?shí)際操作的目標(biāo)其實(shí)是 JavaScript 創(chuàng)建的臨時(shí)對(duì)象,而并非字符串本身!
所以我們的增加或修改操作實(shí)際上是生效了的,只不過(guò)是在臨時(shí)對(duì)象上生效了!
? 就像這樣:
// 代碼中: let a = 'chenpipi'; a.name = '吳彥祖'; console.log(a.name); // undefined // 相當(dāng)于: let a = 'chenpipi'; (new String(a)).name = '吳彥祖'; console.log(a.name); // undefined // 相當(dāng)于: let a = 'chenpipi'; let temp = new String(a); temp.name = '吳彥祖'; console.log(a.name); // undefined
最后我們來(lái)總結(jié)一下:
多數(shù)原始類(lèi)型都有相應(yīng)的包裝對(duì)象;
有些包裝對(duì)象可以被 new,有些不行;
包裝對(duì)象一般被用來(lái)進(jìn)行顯式的類(lèi)型轉(zhuǎn)換;
對(duì)象上有屬性和方法;
原始值上沒(méi)有屬性和方法;
原始值上也不能有屬性和方法;
但我們可以像操作對(duì)象一樣來(lái)操作原始值;
這是因?yàn)?JavaScript 在執(zhí)行代碼的時(shí)候偷偷搞小動(dòng)作;
JavaScript 會(huì)用臨時(shí)的包裝對(duì)象來(lái)替原始值執(zhí)行操作。
以上是“JavaScript原始值與包裝對(duì)象的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。