Javascript细节总结2

作者: littleyu | 来源:发表于2019-01-03 14:08 被阅读13次

    1.JS 分为哪两大类型

    i. 原始(Primitive)类型
    在 JS 中,存在着 6 种原始值,分别是:

    • boolean
    • null
    • undefined
    • number
    • string
    • symbol

    首先原始类型存储的都是值,是没有函数可以调用的,比如 undefined.toString()

    此时你肯定会有疑问,这不对呀,明明 '1'.toString() 是可以使用的。其实在这种情况下,'1' 已经不是原始类型了,而是被强制转换成了 String 类型也就是对象类型,所以可以调用 toString 函数。

    除了会在必要的情况下强转类型以外,原始类型还有一些坑。

    其中 JS 的 number 类型是浮点类型的,在使用中会遇到某些 Bug,比如 0.1 + 0.2 !== 0.3string 类型是不可变的,无论你在 string 类型上调用何种方法,都不会对值有改变。

    另外对于 null 来说,很多人会认为他是个对象类型,其实这是错误的。虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。

    ii. 对象(Object)类型

    在 JS 中,除了原始类型那么其他的都是对象类型了。对象类型和原始类型不同的是,原始类型存储的是值,对象类型存储的是地址。当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址。

    2.typeof vs instanceof

    typeof 对于原始类型来说,除了 null 都可以显示正确的类型

    typeof 1 // 'number'
    typeof '1' // 'string'
    typeof undefined // 'undefined'
    typeof true // 'boolean'
    typeof Symbol() // 'symbol'
    

    typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型

    typeof [] // 'object'
    typeof {} // 'object'
    typeof console.log // 'function'
    

    如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof,因为内部机制是通过原型链来判断的

    const Person = function() {}
    const p1 = new Person()
    p1 instanceof Person // true
    
    var str = 'hello world'
    str instanceof String // false
    
    var str1 = new String('hello world')
    str1 instanceof String // true
    

    对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的,当然我们还是有办法让 instanceof 判断原始类型的

    class PrimitiveString {
      static [Symbol.hasInstance](x) {
        return typeof x === 'string'
      }
    }
    console.log('hello world' instanceof PrimitiveString) // true
    

    你可能不知道 Symbol.hasInstance 是什么东西,其实就是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof 'hello world' === 'string',所以结果自然是 true 了。这其实也侧面反映了一个问题, instanceof 也不是百分之百可信的。

    3.类型转换

    首先我们要知道,在 JS 中类型转换只有三种情况,分别是:

    • 转换为布尔值
    • 转换为数字
    • 转换为字符串

    i. 转Boolean
    在条件判断时,除了 undefinednullfalseNaN'', 0-0,其他所有值都转为 true,包括所有对象。

    ii. 对象转原始类型
    对象在转换类型的时候,会调用内置的 [[ToPrimitive]] 函数,对于该函数来说,算法逻辑一般来说如下:

    • 如果已经是原始类型了,那就不需要转换了
    • 调用 x.valueOf(),如果转换为基础类型,就返回转换的值
    • 调用 x.toString(),如果转换为基础类型,就返回转换的值
    • 如果都没有返回原始类型,就会报错

    当然你也可以重写 Symbol.toPrimitive ,该方法在转原始类型时调用优先级最高。

    let a = {
      valueOf() {
        return 0
      },
      toString() {
        return '1'
      },
      [Symbol.toPrimitive]() {
        return 2
      }
    }
    1 + a // => 3
    

    iii. 四则运算符
    加法运算符不同于其他几个运算符,它有以下几个特点:

    • 运算中其中一方为字符串,那么就会把另一方也转换为字符串
    • 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
    1 + '1' // '11'
    true + true // 2
    4 + [1,2,3] // "41,2,3"
    

    如果你对于答案有疑问的话,请看解析:

    • 对于第一行代码来说,触发特点一,所以将数字 1 转换为字符串,得到结果 '11'
    • 对于第二行代码来说,触发特点二,所以将 true 转为数字 1
    • 对于第三行代码来说,触发特点二,所以将数组通过 toString 转为字符串 '1,2,3',得到结果 '41,2,3'

    另外对于加法还需要注意这个表达式 'a' + + 'b'

    'a' + + 'b' // -> "aNaN"
    

    因为 + 'b' 等于 NaN,所以结果为 "aNaN",你可能也会在一些代码中看到过 + '1' 的形式来快速获取 number 类型。

    那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

    4 * '3' // 12
    4 * [] // 0
    4 * [1, 2] // NaN
    

    iiii. 比较运算符

    • 如果是对象,就通过 toPrimitive 转换对象
    • 如果是字符串,就通过 unicode 字符索引来比较
    let a = {
      valueOf() {
        return 0
      },
      toString() {
        return '1'
      }
    }
    a > -1 // true
    

    在以上代码中,因为 a 是对象,所以会通过 valueOf 转换为原始类型再比较值。

    相关文章

      网友评论

        本文标题:Javascript细节总结2

        本文链接:https://www.haomeiwen.com/subject/ejzzlqtx.html