type
- Boolean
- String
- Number
- Null
- Undefined
- Symbol(ES6 新定义)
正常情况下:
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof b // b 没有声明,但是还会显示 undefined
特殊情况下:
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
typeof null // 'object'
PS:为什么会出现这种情况呢?因为在 JS 的最初版本中,使用的是 32 位系统,为了性能考虑使用低位存储了变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。
如果我们想获得一个变量的正确类型,可以通过 Object.prototype.toString.call(xx)。这样我们就可以获得类似 [Object Type] 的字符串。
let a = {};
Object.prototype.toString.call(a)
"[object Object]"
let b = '11';
Object.prototype.toString.call(b)
"[object String]"
用于实例和构造函数的对应。例如判断一个变量是否是数组,使用typeof无法判断,但可以使用[1, 2] instanceof Array来判断。因为,[1, 2]是数组,它的构造函数就是Array。
function instanceof(left, right) {
// 获得类型的原型
let prototype = right.prototype
// 获得对象的原型
left = left.__proto__
// 判断对象的类型是否等于类型的原型
while (true) {
if (left === null)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
值类型与引用类型
- Object
- Function
- Date
- Array
- 等等
var obj = {
a: 1,
b: [1,2,3]
}
var a = obj.a
var b = obj.b
a = 2
b.push(4)
console.log(obj, a, b)
// a:1
b:(4) [1, 2, 3, 4]
虽然obj本身是个引用类型的变量(对象),但是内部的a和b一个是值类型一个是引用类型,a的赋值不会改变obj.a,但是b的操作却会反映到obj对象上。
JS 中这种设计的原因是:按值传递的类型,复制一份存入栈内存,这类类型一般不占用太多内存,而且按值传递保证了其访问速度。按共享传递的类型,是复制其引用,而不是整个复制其值(C 语言中的指针),保证过大的对象等不会因为不停复制内容而造成内存的浪费。
在条件判断时,除了 undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象。
对象在转换基本类型时,首先会检查有无设置 Symbol.toPrimitive 该方法优先级最高,然后调用 valueOf,然后调用 toString。三个方法都可以重写。
let a = {
valueOf() {
return 0;
},
toString() {
return '1';
},
[Symbol.toPrimitive]() {
return 2;
}
}
1 + a // => 3
'1' + a // => '12'
- 加法运算:其中一方是字符串类型,就会把另一个也转为字符串类型
- 非加法运算:只要其中一方是数字,那么另一方就转为数字
- 加法运算会触发三种类型转换:将值转换为原始值,转换为数字,转换为字符串。
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' // -> "aNaN"
// 因为 + 'b' -> NaN
// 你也许在一些代码中看到过 + '1' -> 1
//!的优先级较==高,先运算==右侧的操作数:[]是对象,会转换成true,然后再转成false(加!的一定是转换成boolean)
// [] 转成 true,然后取反变成 false
[] == false
// 根据第 8 条得出
[] == ToNumber(false)
[] == 0
// 根据第 10 条得出
ToPrimitive([]) == 0
// [].toString() -> ''
'' == 0
// 根据第 6 条得出
0 == 0 // -> true
- 如果是对象,就通过 toPrimitive 转换对象
- 如果是字符串,就通过 unicode 字符索引来比较
相等和不相等——先转换再比较
全等和不全等——仅比较而不转换
类型转换:这两个操作符都会先转换操作数(强制转型),然后再比较它们的相等性。
规则:
1. 一个是布尔,比较之前先转换成数值——false转0,true转1
2. 一个是字符串,一个是数值 ,比较之前先将字符串转成数值
3. 一个是对象,另一个不是,则调用对象的valueof方法,用得到的基本类型值按照前面的规则进行比较
比较时遵循:
1. null和undefined相等
2. 一个是NaN,相等操作符则返回false。不相等操作符则返回true。
3. 两个都是对象,则比较是否是同一个对象。如果两个操作数指向同一个对象,
则相等操作符返回true。否则返回false
null==undefined//true
"NaN"==NaN//false
5==NaN//false
NaN==NaN//false
NaN!=NaN//true
false==0//true
true==1//true
true==2//true
undefined==0//false
null==0//false
"5"==5//true
除了在比较之前不转换操作数外,全等和不全等与相等和不相等没有什么区别
"55"===55//false
null===undefined//false