基本类型
javascript有七种基本类型
| Undefined | Null | Boolean | String | Number | Symbol | Object |
|---|---|---|---|---|---|---|
| undefined | null | true/false | "a" | 1 | Symbol() | {},[],function(){}, |
原始(Primitive)类型
除了Object以外, 前面6中都是原始类型.
| boolean | null | undefined | number | string | symbol |
|---|---|---|---|---|---|
| true | null | undefined | 1 | "1” | Symbol(1) |
以上原始类型都是值. 没有函数可以调用, 只是一个值.
注意:
- 不能使用
instance去判断
"1" instanceof String // false
const a = Symbol(1)
a instanceof Symbol // false
- 隐式转码
'1'.toString() // "1"
这个可以使用, 是因为’1’被转换成了String 类型了.
typeof null输出object
typeof null // object
因为js最开始为了性能, 使用了低位存储类型.
用 000代表对象, null 就是000
红宝书的解释: null表示一个空对象指针,所以给typeof传null会返回object
对象(Object)类型
问: 什么是对象类型?
答: 除了原始类型的都是对象类型
问: 对象类型和原始值类型的区别?
答: 对象类型: 存储指针. 指向一个内存地址.
值类型: 存储的值
例子:
function test(person) {
person.age = 26
// 重新指向了新的地址
person = {
name: 'yyy',
age: 30
}
return person
}
const p1 = {
name: 'yck',
age: 25
}
const p2 = test(p1)
console.log(p1) //
// {
// name: 'yck',
// age: 26
// }
console.log(p2) // -> ?
// {
// name: 'yyy',
// age: 30
// }
值类型在对象类型中的亲戚—临时对象
JavaScript 语言设计上试图模糊对象和基本类型之间的关系,
所以下面四种基本类型, 在对象中都有对应的类, 在进行运算的时候提供了装箱操作. 具体的会在类型转换中解释.
- Number;
- String;
- Boolean;
- Symbol。
"1".toString() //
这里"1" 先进行了String("1")这个装箱操作, 产生了临时对象, 要不然值类型没有toString()这个方法
如何判断类型?
typeof 和 instanceof有什么不同?
typeof
typeof可以判断原始值类型, 除了 null
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
但是对象类型, 除了function都是object
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
instanceof
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 的行为.
也就是说 instanceof 不是百分百可信的, 而且原型链是可以被改变的.
Object.prototype.toString.call()
javascript中没有任何属性可以修改私有Class属性.
Object.prototype.toString.call(1) // [object Number]
[Symbol.toStringTag](https://www.notion.so/Symbol-toStringTag-151c492cbab24956a01ae5abe6aa03ab) 可以定义class的名字
类型转换
js中的类型转换只有三种情况
- 转换为
boolean - 转换成
number - 转换成
string
转换成Boolean :
| 原始值 | 例子 | 结果 |
|---|---|---|
| number | Boolean(1) | 0, -0, NaN 为 false, 其余都是 true |
| string | Boolean("") | 除了"" 都是true |
| undefined, null | Boolean(undefined), Boolean(null) | false |
| 引用类型 | Boolean({}) | true |
转换成String:
| 原始值 | 例子 | 结果 |
|---|---|---|
| number | String(5), | 5 ⇒ “5” |
| Boolean, 函数, Symbol | String(true), String(function() {}), String(Symbol()) | true ⇒ “true”, function(){} ⇒ “function(){}”, Symbol ⇒ “Symbol” |
| 数组 | String([1,2,3]) | [1,2,3] ⇒ “1,2,3” |
| 对象 | String({}) | "[object Object]” |
转换成Number:
| 原始值 | 例子 | 结果 |
|---|---|---|
| string | Number("1"), Number(”a”) | “1”⇒1, “a”⇒NaN |
| 数组 | Number([1,2,3]), Number([]), Number([1]) | [] ⇒ 0, [1] ⇒ 1, [1,2] ⇒ NaN |
| Null | Number(null) | 0 |
| 引用类型-除了数组 | Number({}) | NaN |
| Symbol | Number(Symbol()) | 报错: Uncaught TypeError: Cannot convert a Symbol value to a number |
条件判断 if:
除了 undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象.
装箱操作
- Number;
- String;
- Boolean;
- Symbol。
上面四个基本类型转换成对象
- 显式装箱
let temp = 1;
typeof temp // number
temp = new Number(1); // 原始数据类型 => 包装类型
typeof temp // object
// Symbol()函数无法通过new调用,我们可以使用Object()函数来显式装箱
let symbolObject = Object(Symbol('abc'));
typeof symbolObject // object
- 隐式装箱
"a".toString()
"1234".substring(1)
let s1 = "1234";
let s2 = s1.substring(1); // s2 === 234
// 对上述第二行代码实际执行过程的理解
let temp = new String(s1);
let s2 = temp.substring();
temp = null
// 证明第二行代码确实创建了临时变量
String.prototype.proof = () => {
console.log("proof函数被调用!");
}
'123'.proof(); // proof函数被调用!
拆箱转换
对象(Object) ⇒ 原始(Primitive)类型
内部调用[[ToPrimitive]]函数基本处理:
- 如果有Symbol.toPrimitive()方法, 优先调用;
- 调用valueOf(), 如果转换为原始类型, 则返回;
- 调用toString(), 如果转换为原始类型, 则返回;
- 如果都没有返回原始类型, 则报错;
正常流程:
valueOf ⇒ toString ⇒ TypeError
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o * 2
// valueOf
// toString
// TypeError
转到String:
toString ⇒ valueOf ⇒ TypeError
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
String(o)
// toString
// valueOf
// TypeError
ToPrimitive:
toPrimitive ⇒ 结果
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}
console.log(o + "")
// toPrimitive
// hello
四则运算中的类型转换
加法
- 运算中有字符串, 另外一方也换转成字符串
- 如果不是字符串或者数字, 则会转成字符串和数字
1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"
'a' + + 'b' // -> "aNaN"
除了加法
其他运算符, 只要其中一方是数字, 则都转成数字.
4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaN
比较运算符
- 对象, 通过toPrimitive拆箱转换成原始值比较
- 是字符串, 通过
unicode索引来比较
let a = {
valueOf() {
return 0
},
toString() {
return '1'
}
}
a > -1 // true