如何理解javascript的隐式转换

138 阅读2分钟

在理解隐式转换前,需要先提到两个概念:

  • 装箱转换:正是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类。
  • 拆箱转换:在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换。

装箱转换

装箱转换的3种方式

  1. new的形式
  2. 使用call方法
  3. 使用Object函数
    var symObj = (function(){return this;}).call(Symbol("a"));
    
    var symObj = Object(Symbol("a"))
    
    console.log(typeof symObj);  //object 
    console.log(symObj instanceof Symbol); //true  
    console.log(symObj.constructor == Symbol); //true
  • 如何准确识别对象的基本类型? 每一类装箱对象皆有私有的 Class 属性,这些属性可以用 Object.prototype.toString 获取:
console.log(Object.prototype.toString.call(symObj)); //[object Symbol]

在 JavaScript 中,没有任何方法可以更改私有的 Class 属性,因Object.prototype.toString 是可以准确识别对象对应的基本类型的方法,它比 instanceof 更加准确。但需要注意的是,call 本身会产生装箱操作,所以需要配合 typeof 来区分基本类型还是对象类型。

拆箱转换

  • 拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。

  • 如何理解隐式转换(拆箱转换)的 valueOf,toString调用顺序

    let ss = "";
    let ssObj= (function () {
        return this;
    }).call(ss);

    console.log(typeof ssObj);  // object
    console.log(ssObj instanceof String);  // true
    console.log(ssObj.constructor === String);  // true
    // 可以看出 ssObj 是String装箱后的对象

    ssObj.valueOf = () => {console.log("valueOf"); return {}};
    ssObj.toString = () => {console.log("toString"); return {}};
    ssObj + ss;
    // valueOf
    // toString
    // Uncaught TypeError
    // 由此可见,操作符号“+” 的 toPrimitive的默认是Numer,与原数据类型无关
  • 到 String 的拆箱转换会优先调用 toString。我们把刚才的运算从 ssObj + ss 换成 String(ssObj),那么你会看到调用顺序就变了。
console.log(String(ssObj))  

// toString 
// valueOf 
// Uncaught TypeError
  • 在 ES6 之后,还允许对象通过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。
ssObj[Symbol.toPrimitive] =
    () => {
        console.log("toPrimitive"); 
        return "hello";
    } 
console.log(ssObj + "") 
// toPrimitive 
// hello

其他注意

  • js“类型”中,typeof 的运算结果,与运行时类型的规定不一致的地方:

image.png

参考文献

  1. 此文章为3月Day1学习笔记,内容来源于极客时间《重学前端》

最后

文中若有我没说清楚或错误的地方

欢迎v我yxdiet,备注下是掘金的小伙伴!

反手赞一个叭!对我是莫大的鼓励 💗