文章講隱式強制類型轉換的路線是從基礎講到應用, 這條學習曲線比較平滑. 具體的路線爲:正則表達式
基本數據類型引出包裝類型的概念 ==> valueOf 和 toString 方法 ==> ToPrimitive 抽象操做和 [[DefaultValue]] 操做 ==> 各類類型之間類型轉換的規律 ==> 常見引發隱式類型轉換的狀況 ==> 多個實例練習以檢驗方法的有用性.數組
討論基本包裝類型的前提是瞭解基本數據類型(也能夠稱爲原始類型, 簡單數據類型等)。而後經過基本數據類型調用方法的行爲, 引出基本包裝類型的概念和做用.函數
已經知道 JavaScript 中共有6種基本數據類型,分別是:string,number,boolean,null,undefined,symbol.學習
基本類型既不是對象, 也沒有方法可供調用. 然而常常見到基本類型的變量調用方法的狀況, 類如:ui
let index = 'ABCDE'.indexOf('CD');
console.log(index); // 2
複製代碼
上例中 'ABCDE'
是一個字符串的基本類型, 這個字符串直接調用了indexOf
方法, 將該方法的返回值保存在變量 index
中, 而後在第二行輸出. 能夠看到這個例子可以運行而且結果正確.spa
既然上面說原始類型沒有方法可供調用, 那麼字符串 'ABCDE'
在調用 indexOf
方法時爲何沒有出錯呢? 並且從運行結果來看, indexOf
這個方法確實被調用了, 那麼是誰調用了這個方法而且返回了正確的結果呢? 答案就是基本包裝類型. 下面引入基本包裝類型的相關內容.prototype
關鍵詞: 包裝, 基本包裝類型, 基本包裝類型的對象code
上一節討論到其實調用方法的並非字符串自己 , 而是基本包裝類型. 下面就應該具體討論基本包裝類型的相關內容了.對象
爲了基本類型能夠正常調用方法, 後臺會爲這個基本類型的值自動建立一個對應的的對象, 這個對象的類型就稱爲基本包裝類型, 而後用這個對象去調用方法, 在調用完方法以後, 這個對象就會被銷燬( 用完就銷燬 )了. 這個從基本數據類型生成基本包裝類型對象的過程稱爲包裝( box ).繼承
簡單來講, 在基本類型調用方法的時候, 方法的真正調用者其實不是咱們直接定義的基本類型, 而是後臺給咱們建立的基本包裝類型的 對象 . 並且這個對象是臨時的、不會一直存在的、用完就會被銷燬的.
這個對象還有一個特色, 他是與基本類型的值相對應的. 然而:
並非每一種基本類型都有對應的包裝類型
上節中提到的六種基本類型中的四種有其對應的包裝類型, 分別是:
string(字符串) -> String, number(數值) -> Number,boolean(布爾) -> Boolean,symbol(符號) -> Symbol
其中符號 ->
表示對應
. 同時: 注意首字母的大寫( 對象名的首字母習俗默認大寫 ).
注意: 本文只討論 string,number,boolean
三種基本類型及其對應的包裝類型.
還須要瞭解的是, 不只僅是系統能夠隱式的建立包裝對象, 用戶也能夠手動、顯式的建立一個基本包裝類型的對象, 方法爲: 用關鍵字 new
加上對應的包裝類型的構造函數, 參數傳入基本類型的值便可, 例如:
let n = new Number(22); // n 是 數值22對應的包裝對象
let str = new String('example'); // str 是字符串 'example' 對應的包裝對象
let flag = new Boolean(false); // flag 是布爾值 false 對應的包裝對象
複製代碼
至此, 再看第一節的這個例子, 能夠再次想象系統建立包裝對象的大體過程:
let index = 'ABCDE'.indexOf('CD');
console.log(index); // 2
複製代碼
能夠發現第一行代碼中後臺發生了包裝行爲: 後臺根據字符串類型的 'ABCDE'
包裝出了一個對象. 即: 在調用 indexOf
方法時, 後臺發現想調用這個方法的是一個基本類型的值, 可是這個值沒有這個方法可供調用, 因而爲它生成了對應的包裝對象( 操做 1 ), 而後經過這個包裝對象來調用了 indexOf
方法( 操做 2 ), 然後將方法的返回值賦給了變量 index
. 最後, 把這個包裝對象銷燬( 操做 3 ).
根據以上思路,能夠大體模擬出上述過程的對應的代碼:
// step 1. 建立 'ABCDE' 對應的基本包裝類型的對象:
let temp = new String('ABCDE');
// step 2. 用包裝類型的對象 temp 調用 indexOf 方法, 並將返回值賦給 index 變量:
let index = temp.indexOf('CD');
// step 3. 將 temp 對象銷燬
temp = null;
複製代碼
當一個基本數據類型想要調用方法時, 後臺會爲它生成一個臨時的包裝對象, 利用這個對象去調用方法, 再將方法執行的結果返回, 隨後這個臨時對象被銷燬.
從上面的內容瞭解到 包裝(box) 是根據一個基本類型的值生成一個對應類型的對象的過程, 與這個過程大體相反, 存在一種根據包裝對象生成基本類型值的過程, 可稱爲 拆包裝 (unbox). 這個過程一樣便可以由後臺隱式的完成, 也能夠手動的調用方法 valueOf
來作.
下一節開始討論 valueOf
這個方法, 同時引出另一個一樣重要的方法 toString
.
valueOf
和 toString
valueOf
方法基本包裝類型的拆包裝操做用到了包裝對象中的 valueOf
函數, 這個函數能夠將一個對象轉換成一個基本類型的值.
對於 Boolean, Number 和 String
三者的基本包裝對象來講, 調用 valueOf
的返回值是各自對應的基本數據類型的值:
let n = new Number(22); // 包裝基本數值數據 22
// 拆包裝出來的結果是對應的基本數據類型的值
console.log(n.valueOf() === 22); // true
let str = new String('example'); // 包裝基本字符串數據 'example'
// 拆包裝出來的結果是對應的基本數據類型的值
console.log(str.valueOf() === 'example'); // true,
let flag = new Boolean(false); // 包裝基本布爾數據 false
// 拆包裝出來的結果是對應的基本數據類型的值
console.log(flag.valueOf() === false); // true
複製代碼
有時會發生後臺隱式拆包裝的狀況, 包裝類型的對象會在後臺調用 valueOf
方法, 例如:
let a = new Number(1);
let b = a + 1; //---> 這一行發生了隱式拆包裝操做: let b = a.valueOf() + 1;
console.log(b); // b 爲 2
console.log(typeof a); // object
console.log(typeof b); // number, b 的類型爲 基本數據類型, 而不是包裝類型
複製代碼
甚至一行代碼中會發生包裝和解包裝兩種操做, 例如:
let num = 3.14159;
console.log(num.valueOf()); // 3.14159
複製代碼
在上面代碼塊的第二行代碼中變量 num
要調用函數 valueOf
, 此時 num
會被先包裝爲 基本包裝類型的對象,而這個對象在調用 valueOf
方法時就發生瞭解包裝的操做.
valueOf
方法不只僅是基本包裝類型有 valueOf
方法, 許多 JavaScript 內建(build-in)對象都有該函數, 爲使執行的結果與對象自己相符合, 大多對象都重寫了這個方法. 下面看一些其餘對象的 valueOf
方法的行爲有什麼特色.
如下列出經常使用內置對象的 valueOf
方法的返回值:
對象 | 返回值 |
---|---|
Boolean, Number 和 String 三者 | 各自相對應的基本類型的值 |
Array,Function,Object 三者 | 其自己 |
Date | 當前時間距 1970.01.01 午夜的毫秒數 |
Math 和 Error | 沒有 valueOf 方法 |
下面是實驗結果:
// 數組調用 valueOf, 返回數組自己
let array = [1, 'hello', false];
console.log(array.valueOf() === array); // true
// 函數調用 valueOf, 返回函數自己
function foo(){}
console.log(foo.valueOf() === foo); // true
// 對象調用 valueOf, 返回對象自己
let obj = {
name: 'doug',
age : 22
};
console.log(obj.valueOf() === obj); // true
// 當前時間距1970年1月1日午夜的毫秒數
console.log(new Date().valueOf()); // 1551684737052
複製代碼
總結: valueOf
方法能夠將一個對象轉換爲基本數據類型, 並非每一個對象都有此方法(例如: Math 和 Error 對象). 對於布爾、數值和字符串三者的基本包裝類型來講,調用此函數返回其對應的基本類型的值; 對象調用此函數的返回值是其自己 ( 因爲數組和函數本質上也是對象, 因此也返回其自身 ) .
提到 valueOf
方法就不得不想起另一個對於類型轉換十分重要的方法 toString
, 下節將會討論它.
toString()
每一個內置的對象都有此方法,是從 Object
對象繼承而來的. 爲使執行的結果與對象自己相符合, 大多數內置對象都重寫了該函數. 常見的對象調用 toString
方法的返回值以下:
對於用戶建立的對象, 返回'[object object]'. (存在一個例外, 見最後部分)
對於 Math 對象, 返回 "[object Math]":
// 自定義的對象
console.log({name: 'doug'}.toString()); // '[object Object]'
// Math 對象
console.log(Math.toString()); // '[object Math]'
複製代碼
valueOf
方法獲得的結果相同) .// 布爾值的包裝類型的對象
console.log(new Boolean(false).toString()); // 'false'
// 數值包裝類型對象的對象
console.log(new Number(3.14159).toString()); // '3.14159'
// 字符串包裝類型對象的對象
console.log(new String('str').toString()); // 'str'
複製代碼
// 數組
console.log([1, 'hello', false].toString()); // '1,hello,false'
複製代碼
// 函數
function foo(){console.log('hello foo');}
console.log(foo.toString());
// 'function foo(){console.log('hello foo');}'
複製代碼
// 正則對象
console.log(new RegExp("a+b+c").toString()); // "/a+b+c/"
// 日期對象
console.log(new Date().toString());
// Mon Mar 04 2019 17:07:54 GMT+0800 (中國標準時間)
// Error 對象
console.log(new Error('fatal error').toString());
// 'Error: fatal error'
複製代碼
並不是每一個對象都有 toString()
方法, 例如經過 Object.create
函數傳入null
爲參數建立出來的對象, 因爲它的 prototype
爲 null
, 因此沒有 toString
和 valueOf
方法
夲節討論了兩個重要的函數 valueOf
和 toString
, 前者返回調用者的基本類型的值, 後者能夠將一個對象轉化爲字符串.
這兩個函數將在強制類型轉換過程當中起到重要的做用.
注: 包裝和拆包裝過程也有其餘名稱, 例如封裝(wrap)和解封(unwrap), 只是同一個過程的不一樣說法.