js知识点(一)内置类型、Typeof、类型转换

内置类型

JS 中分为七种内置类型又分为两大类型:基本类型和对象(Object)。
基本类型有六种: null,undefined,boolean,number,string, symbol(ES6引入的原始数据类型)。

number

JS 的数字类型是浮点类型,并且浮点类型基于 IEEE 754标准实现,在使用中会遇到某些 Bug。


var num1 = 0.1; 
var num2= 0.2; 
 
// 0.1 + 0.2 不等于 0.3
console.log(num1 + num2); // 0.30000000000000004

浮点数不如整数精准,所以一般不要使用浮点数进行计算

NaN 是 not a number 的简写,即非数字。它是一个特殊的值,这个数值用于表示一个本来要返回数值的操作数,结果未返回数值的情况。

NaN 有两个不同寻常的特点:

  • 任何涉及 NaN 的操作都会返回 NaN
  • NaN 值与任何值都不相等,包括本身。

Symbol

1.Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

2.注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

3.由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
例子:

//例1
let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
//例2
let s = Symbol();

let obj = {
  [s]: function (arg) { ... }
};

obj[s](123);
//例3
let obj = {
  [s](arg) { ... }
};
//注意,Symbol 值作为对象属性名时,不能用点运算符。
const mySymbol = Symbol();
const a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"

Typeof

typeof 对于基本类型,除了 null 都可以显示正确的类型

typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof b // b 没有声明,但是还会显示 undefined

typeof 对于对象,除了函数都会显示 object

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

对于 null 来说,虽然它是基本类型,但是会显示 object,这是一个存在很久了的 Bug

typeof null // 'object'

如果我们想获得一个变量的正确类型,可以通过 Object.prototype.toString.call(xx)。这样我们就可以获得类似 [object Type] 的字符串。

let a
// 我们也可以这样判断 undefined
a === undefined
// 但是 undefined 不是保留字,能够在低版本浏览器被赋值
let undefined = 1
// 这样判断就会出错
// 所以可以用下面的方式来判断,并且代码量更少
// 因为 void 后面随便跟上一个组成表达式
// 返回就是 undefined
a === void 0

类型转换

转Boolean

在条件判断时,除了 undefined, null, false, NaN, ‘’, 0, -0,其他所有值都转为 true,包括所有对象。

对象转基本类型

1.对象在转换基本类型时,会调用 valueOf 和 toString。并且这两个方法你是可以重写的。
2.调用哪个方法,我们可以看看以下例子:

以alert(obj)为例,我们知道,alert(obj)会优先调用obj的toString方法,其内部实现机制是这样的:

  • alert调用该引用的GetValue方法,返回它所指向的对象
  • alert该值的基础上调用toString方法(这个跟对象的toString方法不同)
  • toString在该值上调用toPrimitive方法,传递hint参数为string(是toPrimitive方法的一个参数)
  • toPrimitive调用对象的内部方法[[DefaultValue]],并传递hint参数为string
  • [[DefaultValue]]调用对象(obj)的toString方法,并把this指向该对象
  • toString的结果是一个基本数据类型,返回该值 结果是一个字符串类型,将其返回该alert alert展现这个值

我们注意到,最开始的GetValue和toString其实是alert调用的方法,对象转化的核心方法其实是toPrimitive,这个方法获取一个任意值,并返回一个基础数据类型,如果传入的参数不是一个基础数据类型,将会调用对象内部的 [[DefaultValue]]方法获取其default value(默认值)。

[[DefaultValue]]是每个对象都有的一个内部方法,这个方法可以接收一个形参hint,这个形参的值要么是string,要么是number,如果为空,则默认为number,除非该对象是Date,Date的该方法默认传入的是string。之后会决定是调用对象的toString方法还是调用对象的valueOf方法。如果hint是number,则优先调用valueOf方法,否则优先调用toString方法。

具体过程如下(我们把优先调用的方法叫做方法一):

  1. 如果方法一存在,而且可以被调用,则调用它获取结果,否则跳到第3步
  2. 如果1的结果是基本类型,返回它
  3. 如果方法二存在,而且可以被调用,则调用它获取结果,否则跳到第5步
  4. 如果第三步的结果是基础数据类型,返回它
  5. 抛出TypeError异常

也就是说,toPrimitive方法返回的一定是一个基本数据类型,否则就抛出类型错误异常

var obj = {
  toString () {
    console.log('toString')
    return 'string'
  },
  valueOf () {
    console.log('valueOf')
    return 'value'
  }
}

alert(obj) // string
console.log(1 + obj) // 1value

如果重写了 toString 方法,而没有重写 valueOf 方法,则会调用 toString 方法

var obj = {
  toString () {
    return 'string'
  }
}

console.log(1 + obj) // 1string

3.调用上述两个方法的时候,需要 return 原始类型的值 (primitive value)
如果在调用 valueOf 的时候,返回的不是原始类型的值,就会去调用 toString

var obj = {
  toString () {
    console.log('toString')
    return 'string'
  },
  valueOf () {
    console.log('valueOf')
    return {}
  }
}

console.log(1 + obj)

// 依次打印出
valueOf
toString
1string

如果返回还不是原始的值,就会报错

var obj = {
  toString () {
    console.log('toString')
    return {}
  },
  valueOf () {
    console.log('valueOf')
    return {}
  }
}

console.log(1 + obj)

// 报错。无法将一个对象转换为原始类型的值
Uncaught TypeError: Cannot convert object to primitive value

4.如果有 Symbol.toPrimitive 属性的话,会优先调用,它的优先级最高

var obj = {
  toString () {
    console.log('toString')
    return {}
  },
  valueOf () {
    console.log('valueOf')
    return {}
  },
  [Symbol.toPrimitive] () {
    console.log('primitive')
    return 'primi'
  }
}

console.log(1 + obj) // 1primi

同样只能 return 原始类型的值,否则会报和上面所说一样的错。
5.对于加操作的困惑

var obj = {
  toString () {
    console.log('toString')
    return '1'
  },
  valueOf () {
    console.log('valueOf')
    return 2
  }
}

console.log(1 + obj)
console.log('1' + obj)

// 依次输出
valueOf
3
valueOf
12

按理来说’1’ + obj的’+'是倾向于转为转换为字符串类型的(也就是调用 toString);但是,它最后却是调用 valueOf。

要理解该过程,就该理解加操作的计算过程:

  1. 计算加号左边,获取其值
  2. 计算加号右边,获取其值
  3. 两边同时调用toPrimitive方法,hint参数为空
  4. 如果任意一个类型为string,跳到第7步
  5. 将两个值转化为数字(调用ToNumber)
  6. 返回两个值之和
  7. 将两个值转化为字符串(调用ToString)
  8. 返回两个值的组合

可以看到,hint参数为空,而如果对象不是Date对象,则hint默认为number,即调用valueOf方法

四则运算符

只有当加法运算时,其中一方是字符串类型,就会把另一个也转为字符串类型。其他运算只要其中一方是数字,那么另一方就转为数字。并且加法运算会触发三种类型转换:将值转换为原始值,转换为数字,转换为字符串。

1 + '1' // '11'
2 * '2' // 4
[1, 2] + [2, 1] // '1,22,1'
// [1, 2].toString() -> '1,2'
// [2, 1].toString() -> '2,1'
// '1,2' + '2,1' = '1,22,1'

对于加号需要注意这个表达式 ‘a’ + + ‘b’

'a' + + 'b' // -> "aNaN"
// 因为 + 'b' -> NaN
// 你也许在一些代码中看到过 + '1' -> 1

== 操作符

在这里插入图片描述

上图中的 toPrimitive 就是对象转基本类型。
这里来解析一道题目 [] == ![] // -> true,下面是这个表达式为何为 true 的步骤

// [] 转成 true,然后取反变成 false
[] == false
// 根据第 8 条得出
[] == ToNumber(false)
[] == 0
// 根据第 10 条得出
ToPrimitive([]) == 0
// [].toString() -> ''
'' == 0
// 根据第 6 条得出
0 == 0 // -> true

比较运算符

  1. 如果是对象,就通过 toPrimitive 转换对象
  2. 如果是字符串,就通过 unicode 字符索引来比较

文章部分内容引用自:

https://yuchengkai.cn/docs/frontend
http://www.cnblogs.com/strayling/p/3752795.html
https://segmentfault.com/a/1190000013407859

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值