原來 JS 還存在這樣的拆箱轉換

在讀 Winter 大佬的《重學前端》欄目時,重溫了 JS 的「拆箱轉換」。「裝箱轉換」與「拆箱轉換」之前都是瞭解的,今天來看,本身所謂的瞭解也真是隻知其一;不知其二。在閱讀 Winter 老師寫的內容後,對「拆箱轉換」這個知識點仍是不甚清楚,所以我再去深刻地瞭解一番,參考資料詳見文末的「參考連接」。javascript

被咱們忽略的表象

首先,咱們來看一下例子:前端

const a = {
    name: 'a',
    toString () {
        console.log(this);
        console.log('toString');
        return { name: 'toString' };
    },
    valueOf () {
        console.log(this);
        console.log('valueOf');
        return { name: 'valueOf' };
    }
};

a * 2;
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// Uncaught TypeError: Cannot convert object to primitive value

a + "";
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// Uncaught TypeError: Cannot convert object to primitive 

alert(a);
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// Uncaught TypeError: Cannot convert object to primitive value
複製代碼

能夠看到,toStringvalueOf 的執行順序並不固定,而是根據某個條件來決定的,那麼是根據什麼呢?那就是在拆箱轉換時,調用了對象的 ToPrimitive 內部函數時,其會根據執行上下文,自動傳入一個轉換類型參數,暫時給它命名爲 hintjava

ToPrimitive

在 JavaScript 標準中,規定了 ToPrimitive 函數,它是對象類型到基本類型轉換的實現者(即,拆箱轉換);但這是一個內部算法,是編程語言在內部執行時遵循的一套規則。git

對象到 String 和 Number 的轉換都遵循「先拆箱再轉換」的規則。經過拆箱轉換,把對象變成基本類型,再從基本類型轉換爲對應的 String 或者 Number。github

可是對於不一樣的操做,拆箱轉換的內部實現也有所區別,正如上面的例子所示。算法

「拆箱轉換」的調用規則及順序以下:編程

  1. 檢查對象中是否有用戶顯式定義的 [Symbol.toPrimitive] 方法,若是有,直接調用;
  2. 若是沒有,則執行原內部函數 ToPrimitive,而後判斷傳入的 hint 值,若是其值爲 string,順序調用對象的 toStringvalueOf 方法(其中 toString 方法必定會執行,若是其返回一個基本類型值,則返回、終止運算,不然繼續調用 valueOf 方法);
  3. 若是判斷傳入的 hint 值不爲 string,則就可能爲 number 或者 default 了,均會順序調用對象的 valueOftoString 方法(其中 valueOf 方法必定會執行,若是其返回一個基本類型值,則返回、終止運算,不然繼續調用 toString 方法);

來看一下第一種狀況:微信

const b = {
    [Symbol.toPrimitive] (hint) {
        console.log(`hint: ${hint}`);
        return {};
    },
    toString () {
        console.log('toString');
        return 1;
    },
    valueOf () {
        console.log('valueOf');
        return 2;
    }
};

alert(b); // hint: string 
b + ''; // hint: default
b + 500; // hint: default
+b; // hint: number
b * 1; // hint: number
複製代碼

第2、三種狀況:less

const c = {
    toString () {
        console.log('toString');
        return 1;
    },
    valueOf () {
        console.log('valueOf');
        return 2;
    }
};

alert(c); // 打印出 toString 並 alert 出 1
c + '';  // 前後打印出 valueOf,"2"
c + 500; // 前後打印出 valueOf,502
+c; // 前後打印出 valueOf,2
c * 1; // 前後打印出 valueOf,2
複製代碼

那麼關於 hint 可取的三種值,都有什麼含義?又什麼狀況對應什麼值?編程語言

肯定 hint 取值

string
當在但願是字符串操做,也即發生對象到字符串的轉換時,傳入內部函數 ToPrimitive 的參數值即爲 string

// output
alert(obj);

// using object as a property key
anotherObj[obj] = 123;
複製代碼

number
當在但願是數值操做,也即發生對象到數值的轉換時,傳入內部函數 ToPrimitive 的參數值即爲 number

// explicit conversion
let num = Number(obj);

// maths (except binary plus)
let n = +obj; // unary plus
let delta = date1 - date2;

// less/greater comparison
let greater = user1 > user2;
複製代碼

default
當在一些不肯定須要將對象轉換成什麼基礎類型的場景下,傳入內部函數 ToPrimitive 的參數值即爲 default

// binary plus
let total = car1 + car2;

// obj == string/number/symbol
if (user == 1) { ... };
複製代碼

結語

若是親愛的讀者們在本文中發現了什麼錯誤,或者有什麼不一樣的意見,還請留言,一塊兒討論,一塊兒將隱藏的、晦澀的點提出來,而後解決掉。

參考連接


微信公衆號 以爲本文不錯的話,分享一下給小夥伴吧~

相關文章
相關標籤/搜索