数据类型
有哪些
- 基础数据类型:存储在栈内存,被引用或拷贝时,会创建一个完全相等的变量
- undefined
- Null
- Boolean
- String
- Number
- Symbol(ES6 引入):符号类型,表示独一无二的值
- BigInt(ES10 引入):大整数类型,可以安全地存储和操作大整数。
- 引用数据类型:存储在堆内存,存储的是地址,多个引用指向同一个地址
- Object(以下类型继承自 Object)
- Array
- RegExp
- Date
- Math
- Function
- Object(以下类型继承自 Object)
数据类型的检测
typeof
可判断基础数据类型(null 除外),不能正确判断引用数据类型(function 除外)
typeof 42; // "number"
typeof "blubber"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof { a: 1 }; // "object"
typeof null; // "object"
typeof [1, 2, 3]; // "object"
typeof function () {}; // "function"
typeof Symbol(); // "symbol"
typeof BigInt(9007199254740991); // "bigint"
instanceof
返回对象是否是构造函数生成的对象 可判断复杂引用数据类型,不能正确判断基础数据类型
let Car = function () {};
let benz = new Car();
Car instanceof Function; // true
Car instanceof Object; // true
benz instanceof Car; // true
let car = new String("Mercedes Benz");
car instanceof String; // true
let str = "Covid-19";
str instanceof String; // false
instanceof 的工作原理更详细的解释:
它首先会检查右侧的对象(函数)是否有 prototype 属性。如果没有,它会抛出一个类型错误。
如果有,它接着会查看左侧对象的原型链,看是否能找到右侧对象的 prototype。这个过程会一直持续到原型链的末端,也就是 null。
如果在原型链上找到了右侧对象的 prototype,那么 instanceof 就会返回 true。如果直到 null 都没有找到,那么就会返回 false。
这就是 instanceof 运算符的工作原理。需要注意的是,所有的对象最终都会继承自 Object,所以几乎所有的对象都可以通过 instanceof Object 检测出来。
Object.prototype.toString
Object.prototype.toString({}); // "[object Object]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call(1); // "[object Number]"
Object.prototype.toString.call("1"); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(function () {}); // "[object Function]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(/123/g); // "[object RegExp]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(document); // "[object HTMLDocument]"
Object.prototype.toString.call(window); // "[object Window]"
function getType(obj) {
let type = typeof obj;
// 先进行 typeof 判断,如果是基础数据类型,直接返回
if (type !== "object") {
return type;
}
// 如果是引用数据类型,使用正则返回结果
return Object.prototype.toString
.call(obj)
.replace(/^\[object (\S+)]\]$/, "$1");
}
// test
getType([]); // "Array"
getType("123"); // "string"
数据类型的转换
强制类型转换
- Number()
- 布尔值:true 和 false 分别被转换为 1 和 0
- 数字:返回自身
- null:返回 0
- undefined:返回 NaN
- 字符串
- 只包含数字:转换为十进制
- 有效的浮点格式:转换为浮点数值
- 空字符串:转换为 0
- 不是以上格式:返回 NaN
- parseInt()
- parseFloat()
- toString()
- String()
- Boolean()
- 除了 undefined、null、false、''、0、NaN 转换是 false
- 其他都是 true
toString()
和 String()
都可以用来将其他数据类型转换为字符串,但它们的行为方式有一些不同。
toString()
是包装对象的一个方法,所有的对象都继承了这个方法。当你调用这个方法时,它会返回一个表示该对象的字符串。对于内置类型(如Number
,Boolean
,Array
等),它们的toString()
方法已经被重新定义,以返回更有意义的字符串。例如,对于数组,toString()
会返回一个由数组中的所有元素组成的字符串,元素之间用逗号分隔。但是,如果你尝试在null
或undefined
上调用toString()
,会抛出一个错误,因为它们没有这个方法。javascript(123).toString(); // "123" true.toString(); // "true" [1, 2, 3].toString(); // "1,2,3" ({ a: 1, b: 2 }).toString(); // "[object Object]" null.toString(); // 抛出错误 undefined.toString(); // 抛出错误
String()
是一个函数,可以将任何类型的值转换为字符串。如果值有toString()
方法,String()
会调用这个方法并返回结果。否则,它会直接返回一个表示该值的字符串。对于null
和undefined
,String()
会返回"null"
和"undefined"
。javascriptString(123); // "123" String(true); // "true" String([1, 2, 3]); // "1,2,3" String({ a: 1, b: 2 }); // "[object Object]" String(null); // "null" String(undefined); // "undefined"
总的来说,toString()
是一个方法,通常在对象上调用,而 String()
是一个函数,可以接受任何类型的参数。在处理 null
和 undefined
时,String()
会更安全,因为它不会抛出错误。
隐式类型转换
逻辑运算符:&&、||、!
运算符:+、-、*、/
关系操作符:>、<、<=、>=
条件运算符:if/while
相等运算符:==
- 数字和字符串比较:字符串会被转换为数字,然后进行比较。例如:
5 == '5'
,字符串'5'
被转换为数字5
,所以结果为true
。 - 布尔值和其他类型比较:布尔值会被转换为数字(
true
转换为1
,false
转换为0
),然后进行比较。例如:1 == true
,布尔值true
被转换为数字1
,所以结果为true
。 - 对象和非对象比较:当一个对象与一个非对象(数字或字符串)比较时,对象会尝试转换为原始类型(通过
valueOf
或toString
方法),然后进行比较。例如:[1] == 1
,数组[1]
会先转换为字符串'1'
,然后字符串'1'
转换为数字1
,所以结果为true
。 null
和undefined
比较:当null
和undefined
进行比较时,结果为true
。例如:null == undefined
的结果为true
。NaN
比较:NaN
与任何值比较,包括其自身,结果都是false
。
- 数字和字符串比较:字符串会被转换为数字,然后进行比较。例如:
这些转换规则可能会导致一些出乎意料的结果,因此在可能的情况下,推荐使用 ===
(严格等于)进行比较,因为 ===
不会进行类型转换。
数据类型的比较
在 JavaScript 中,数据类型的比较可以分为两种:严格比较(使用 ===
和 !==
运算符)和抽象比较(使用 ==
和 !=
运算符)。
严格比较(
===
和!==
)严格比较运算符会比较两个值的类型和值。如果两个值的类型或值不同,那么这两个值就被认为是不相等的。
javascript123 === 123; // true "123" === 123; // false true === 1; // false null === undefined; // false NaN === NaN; // false
注意,
NaN
是唯一一个不等于自身的值。抽象比较(
==
和!=
)抽象比较运算符会在比较两个值之前尝试进行类型转换。如果两个值的类型不同,JavaScript 会尝试将一个或两个值转换为一个共同的类型,然后比较它们的值。
javascript123 == "123"; // true true == 1; // true null == undefined; // true "0" == false; // true NaN == NaN; // false
注意,尽管
null
和undefined
在抽象比较中被认为是相等的,但它们不会被转换为其他任何值。此外,NaN
在任何比较中都不等于任何值,包括它自己。
在编程中,通常推荐使用严格比较运算符,因为抽象比较的规则可能会导致一些非直观的结果。严格比较运算符提供了更明确和可预测的比较结果。
数据类型的包装对象
在 JavaScript 中,原始数据类型(Primitive types)包括 Undefined、Null、Boolean、Number、String、Symbol 和 BigInt。这些原始数据类型的值是直接包含在变量中的简单数据段。然而,JavaScript 为我们提供了一种方法,可以在这些原始数据类型上调用方法。这就是通过所谓的包装对象(Wrapper objects)实现的。
包装对象是一种特殊的对象,它们关联了一个原始类型的值。JavaScript 有三种内置的包装对象:String、Number 和 Boolean。当你在一个原始数据类型上调用一个方法时,JavaScript 会暂时把它转换(包装)成一个对象,这样就可以调用方法了。这个过程是自动的,你不需要显式创建一个包装对象。
以下是一些例子:
String:当你对一个字符串调用方法时,JavaScript 会创建一个临时的 String 对象,调用方法,然后丢弃这个临时对象。你可以调用的方法包括
charAt()
、replace()
、split()
等。javascriptlet str = "hello world"; console.log(str.charAt(0)); // 'h'
在这个例子中,
str
是一个字符串,不是一个对象,所以直接在str
上调用charAt()
方法实际上是 JavaScript 内部自动将str
包装成了一个临时的 String 对象,然后在这个临时对象上调用了charAt()
方法。Number:同样,当你对一个数字调用方法时,JavaScript 会创建一个临时的 Number 对象。
javascriptlet num = 12345.6789; console.log(num.toFixed(2)); // '12345.68'
在这个例子中,
num.toFixed(2)
实际上是 JavaScript 内部自动将num
包装成了一个临时的 Number 对象,然后在这个临时对象上调用了toFixed()
方法。Boolean:Boolean 包装对象同样存在,但在实际编程中使用的不多。
需要注意的是,虽然 JavaScript 会自动创建这些包装对象,但直接使用 new
关键字手动创建包装对象是不推荐的,因为这可能会导致一些意想不到的结果。
let strObj = new String("hello world");
console.log(typeof strObj); // 'object', not 'string'
在这个例子中,strObj
是一个对象,而不是一个字符串。这可能会导致一些问题,因为在很多情况下,我们希望的是一个字符串,而不是一个 String 对象。