数据类型
基本类型
基本类型有七种: null,undefined,Boolean,Number,String,Symbol, Bigint
- BigInt 是 ES10 新增的数据类型
- Symbol 代表独一无二的值,最大的用法是用来定义对象的唯一属性名。
- BigInt 可以表示任意大小的整数。
其中 JS 的数字类型是浮点类型的,没有整型。并且浮点类型基于 IEEE 754标准实现,在使用中会遇到某些 Bug。NaN 也属于 number 类型,并且 NaN 不等于自身。 对于基本类型来说,如果使用字面量的方式,那么这个变量只是个字面量,只有在必要的时候才会转换为对应的类型。
复杂类型
Object, Array, Function, Map、Set、WeakMap、WeakSet, Date, RegExp, Error, Math, Json 等
自动装箱和拆箱
自动装箱(Autoboxing)和拆箱(Unboxing)是 JavaScript 在处理基本类型和包装类型之间进行隐式转换的过程。
-
自动装箱(Autoboxing):
-
自动装箱是指将基本类型值隐式转换为对应的包装类型对象。
-
当我们使用基本类型的值调用其对应包装类型的属性或方法时,JavaScript 引擎会临时将基本类型值转换为对应的包装类型对象,以便能够调用对象上的方法和属性。
-
例如,在访问字符串基本类型的属性时,JavaScript 引擎会临时创建一个 String 包装类型对象:
let str = "Hello";
console.log(str.length); // 输出 5,临时创建 String 包装类型对象
-
-
拆箱(Unboxing):
-
拆箱是指将包装类型对象隐式转换为对应的基本类型值。
-
当我们使用包装类型对象参与运算或需要基本类型值时,JavaScript 引擎会自动从包装类型对象中提取对应的基本类型值。
-
例如,在进行相等比较时,JavaScript 引擎会自动拆箱并比较两个基本类型值是否相等:
let numObj = new Number(42);
console.log(numObj === 42); // 输出 true,自动拆箱
-
自动装箱和拆箱在 JavaScript 中是隐式进行的,这使得基本类型和包装类型之间的转换更加方便。它使得我们可以在需要时使用基本类型的特性,同时也可以使用包装类型提供的方法和属性。但需要注意的是,在涉及到性能要求较高的情况下,显式地进行类型转换可能更加高效和可控。
FAQ?
null是对象吗?为什么?
null不是对象。
解释: 虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object 。
'1'.toString()为什么可以调用?
其实在这个语句运行的过程中做了这样几件事情:
var s = new Object('1');
s.toString();
s = null;
- 第一步: 创建Object类实例。注意为什么不是String ? 由于Symbol和BigInt的出现,对它们调用new都会报错,目前ES6规范也不建议用new来创建基本类型的包装类。
- 第二步: 调用实例方法。
- 第三步: 执行完方法立即销毁这个实例。
整个过程体现了基本包装类型的性质,而基本包装类型恰恰属于基本数据类型,包括Boolean, Number和String。
0.1+0.2为什么不等于0.3?如何让其相等
0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成0.30000000000000004
我们都知道计算机时是通过二进制来进行计算的,即 0 和 1
就拿 0.1 + 0.2 来说,0.1表示为0.0001100110011001...,而0.2表示为0.0011001100110011... 而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100... 转成10进制就近似表示为 0.30000000000000004 简单来说就是,浮点数转成二进制时丢失了精度,因此在二进制计算完再转回十进制时可能会和理论结果不同
- ES6提供的Number.EPSILON方法
function isEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(isEqual(0.1 + 0.2, 0.3)); // true
Number.EPSILON 的实质是一个可以接受的最小误差范围,一般来说为 Math.pow(2, -52)
- 乘以一个10的幂次方
把需要计算的数字乘以10的n次方,让数值都变为整数,计算完后再除以10的n次方 ,这样就不会出现浮点数精度丢失问题
(0.1*10 + 0.2*10) / 10 == 0.3 //true
typeof 和 instanceof 区别
- typeof 适用于变量和基本数据类型,返回字符串。
- instanceof 适用于对象,主要用于检测对象是否是指定类的实例,返回布尔值。
- instanceof 在检查原型链时很有用,但不适用于基本数据类型。
- typeof 对于 null 返回 "object",这是一个历史遗留问题,需要小心处理。
- instanceof能否判断基本数据类型?
能。比如下面这种方式:
class PrimitiveNumber {
static [Symbol.hasInstance](x) {
return typeof x === 'number'
}
}
console.log(111 instanceof PrimitiveNumber) // true
其实就是自定义instanceof行为的一种方式,这里将原有的instanceof方法重定义,换成了typeof,因此能够判断基本数据类型。
- 能不能手动实现一下instanceof的功能
function myInstanceof(left, right) {
//基本数据类型直接返回false
if(typeof left !== 'object' || left === null) return false;
//getProtypeOf是Object对象自带的一个方法,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left);
while(true) {
//查找到尽头,还没找到
if(proto == null) return false;
//找到相同的原型对象
if(proto == right.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
Object.is 和 === 区别
Object.is 主要用于解决 === 在处理特殊值时的一些不直观的情况。
console.log(+0 === -0); // 输出 true
console.log(Object.is(+0, -0)); // 输出 false
console.log(NaN === NaN); // 输出 false
console.log(Object.is(NaN, NaN)); // 输出 true
[] == ![]结果是什么?为什么?
== 中,左右两边都需要转换为数字然后进行比较。
[]转换为数字为0。
![] 首先是转换为布尔值,由于[]作为一个引用类型转换为布尔值为true,
因此![]为false,进而在转换成数字,变为0。
0 == 0 , 结果为true
对象转原始类型是根据什么流程运行的?
对象转原始类型,会调用内置的[ToPrimitive]函数,对于该函数而言,其逻辑如下:
如果Symbol.toPrimitive()方法,优先调用再返回 调用valueOf(),如果转换为原始类型,则返回 调用toString(),如果转换为原始类型,则返回 如果都没有返回原始类型,会报错
var obj = {
value: 3,
valueOf() {
return 4;
},
toString() {
return '5'
},
[Symbol.toPrimitive]() {
return 6
}
}
console.log(obj + 1); // 输出7
如何让if(a == 1 && a == 2)条件成立?
var a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
console.log(a == 1 && a == 2);//true
什么是 JavaScript 中的包装类型?
在 JavaScript 中,包装类型(Wrapper types)是一种将基本类型(primitive types)包装成对象的特殊机制。JavaScript 提供了三种包装类型:String
、Number
和 Boolean
。
当我们使用基本类型的字面量创建字符串、数字或布尔值时,JavaScript 引擎会自动创建一个相应的包装对象,以便能够调用对象的方法和属性。这种自动包装过程被称为自动装箱(Autoboxing)。
以下是每个包装类型的示例:
- String 包装类型:用于包装字符串值。
let str = "Hello";
console.log(typeof str); // 输出 "string"
let strObj = new String("Hello");
console.log(typeof strObj); // 输出 "object"
- Number 包装类型:用于包装数字值。
let num = 42;
console.log(typeof num); // 输出 "number"
let numObj = new Number(42);
console.log(typeof numObj); // 输出 "object"
- Boolean 包装类型:用于包装布尔值。
let bool = true;
console.log(typeof bool); // 输出 "boolean"
let boolObj = new Boolean(true);
console.log(typeof boolObj); // 输出 "object"
这些包装类型具有与其对应的基本类型相同的值,但它们还提供了许多附加的方法和属性,例如字符串对象的 length
属性和 charAt()
方法,数字对象的 toFixed()
方法等。
需要注意的是,虽然可以使用包装类型创建对象,但在大多数情况下,使用基本类型的字面量更为常见和方便。JavaScript 引擎会自动执行自动装箱和拆箱(Unboxing)的过程, 以便在需要时自动将基本类型转换为包装对象,或将包装对象转换为基本类型。
Object.is() 与比较操作符 “===”、“==” 的区别?
-
Object.is() 方法:
Object.is()
是一个静态方法,用于确定两个值是否严格相等。- 它基本上执行与严格相等操作符(
===
)相同的比较,但有两个特殊情况:- 对于
NaN
的比较,Object.is(NaN, NaN)
返回true
,而严格相等操作符NaN === NaN
返回false
。 - 对于正零和负零的比较,
Object.is(0, -0)
返回false
,而严格相等操作符0 === -0
返回true
。
- 对于
-
严格相等操作符(
===
):===
是一种严格相等比较操作符,用于比较两个值是否严格相等。- 它要求进行比较的两个值既具有相同的类型,又具有相同的值才会返回
true
,否则返回false
。 - 严格相等操作符不会进行类型转换,因此在比较时会考虑类型和值的完全一致性。
-
相等操作符(
==
):==
是一种相等比较操作符,用于比较两个值是否相等。- 相等操作符在进行比较时会进行隐式类型转换,根据规则将两个值转换为相同类型后再进行比较。
- 相等操作符的行为相对复杂,因为它会进行类型转换,可能会导致一些意外的结果。因此,在大多数情况下,推荐使用严格相等操作符(
===
)来进行更明确和可预测的比较。
总结:
Object.is()
用于确定两个值是否严格相等,并具有特殊处理NaN
和零的情况。- 严格相等操作符(
===
)用于比较两个值的类型和值是否完全一致。 - 相等操作符(
==
)用于比较两个值是否相等,会进行类型转换,可能导致意外结果,不推荐在大多数情况下使用。
intanceof 操作符的实现原理及实现
instanceof
操作符用于检查一个对象是否属于某个特定构造函数的实例。它的实现原理是通过检查对象的原型链来确定对象是否继承自指定构造函数的原型。
实现 instanceof
操作符的一种常见方式是使用对象的 __proto__
属性,它是每个对象都具有的一个内部属性,指向其原型。具体的实现步骤如下:
-
获取对象的原型链:通过访问对象的
__proto__
属性,可以获取对象的原型链,即一个对象连接到其原型对象,然后连接到上一级原型对象,一直到顶层的 Object.prototype。 -
检查原型链中是否存在指定构造函数的原型:遍历对象的原型链,逐级与指定构造函数的原型进行比较。如果找到了匹配的构造函数原型,就返回
true
,表示对象是该构造函数的实例;如果遍历完整个原型链仍未找到匹配的构造函数原型,就返回false
,表示对象不是该构造函数的实例。
下面是一个简单的 instanceof
的实现示例:
function myInstanceOf(obj, constructor) {
// 获取对象的原型
let prototype = obj.__proto__;
// 遍历原型链
while (prototype !== null) {
// 检查原型是否匹配指定构造函数的原型
if (prototype === constructor.prototype) {
return true;
}
// 将原型设置为原型的原型,继续向上层原型查找
prototype = prototype.__proto__;
}
return false;
}
使用示例:
function Person(name) {
this.name = name;
}
const person = new Person('Alice');
console.log(myInstanceOf(person, Person)); // 输出 true
console.log(myInstanceOf(person, Object)); // 输出 true(Person 继承自 Object)
console.log(myInstanceOf(person, Array)); // 输出 false
需要注意的是,这只是一个简化的实现,实际的 instanceof
操作符在处理一些特殊情况时可能会更加复杂。此外,不建议频繁自行实现 instanceof
,而是使用 JavaScript 提供的原生操作符来进行类型检查。
void 0
、undefined
和 null
区别
- void 0:
void 0
是一种常见的用法,用于获取 undefined 值。void
是一个一元运算符,它对表达式进行求值并返回 undefined。void 0
的结果始终是 undefined。例如:
console.log(void 0); // 输出 undefined
- undefined:undefined 是一个预定义的全局变量,它表示一个未被赋值的变量。当一个变量被声明但未被赋值时,它的默认值是 undefined。也就是说,如果一个变量没有被显式赋予一个值,那么它的值将是 undefined。例如:
let x;
console.log(x); // 输出 undefined
函数中没有显式返回值的情况下,函数的返回值也将是 undefined。
- null:null 是一个表示空值或缺失值的特殊关键字。它表示一个被明确赋予了空值的变量或对象属性。例如:
let y = null;
console.log(y); // 输出 null
null 是一个表示空对象引用的值。当我们想要明确指示一个变量或属性没有对象引用时,可以将其赋值为 null。
数据类型检测的方式有哪些?
- typeof 操作符:typeof 操作符可以返回一个值的数据类型的字符串表示。例如:
typeof "Hello" // 返回 "string"
typeof 42 // 返回 "number"
typeof true // 返回 "boolean"
typeof undefined // 返回 "undefined"
typeof null // 返回 "object"(这是一个历史遗留问题,null被错误地识别为"object")
typeof {} // 返回 "object"
typeof [] // 返回 "object"
typeof function() {} // 返回 "function"
- instanceof 操作符:instanceof 操作符用于检查一个对象是否属于某个特定的构造函数。例如:
"Hello" instanceof String // 返回 false(原始字符串并非 String 对象)
new String("Hello") instanceof String // 返回 true(String 对象的实例)
[] instanceof Array // 返回 true
{} instanceof Object // 返回 true
- Array.isArray():Array.isArray() 方法用于检查一个值是否为数组类型。例如:
Array.isArray([]) // 返回 true
Array.isArray({}) // 返回 false
- Object.prototype.toString():通过调用 Object.prototype.toString() 方法可以获取一个值的内部属性 [[Class]] 的字符串表示,从而进行类型检测。例如:
Object.prototype.toString.call("Hello") // 返回 "[object String]"
Object.prototype.toString.call(42) // 返回 "[object Number]"
Object.prototype.toString.call(true) // 返回 "[object Boolean]"
Object.prototype.toString.call(undefined) // 返回 "[object Undefined]"
Object.prototype.toString.call(null) // 返回 "[object Null]"
Object.prototype.toString.call({}) // 返回 "[object Object]"
Object.prototype.toString.call([]) // 返回 "[object Array]"
Object.prototype.toString.call(function() {}) // 返回 "[object Function]"
对于某些对象,可以使用对象的 constructor
属性来检查其构造函数。 constructor
属性是一个指向创建对象的构造函数的引用。通过比较对象的 constructor
属性与预期的构造函数,可以进行类型检测。
例如,可以使用 constructor
属性来检查对象是否是特定构造函数的实例:
const obj = {};
console.log(obj.constructor === Object); // 返回 true,obj 是通过 Object 构造函数创建的
const arr = [];
console.log(arr.constructor === Array); // 返回 true,arr 是通过 Array 构造函数创建的
const fn = function() {};
console.log(fn.constructor === Function); // 返回 true,fn 是通过 Function 构造函数创建的
需要注意的是,某些情况下,constructor
属性可能会被修改或重写,因此不是一种完全可靠的类型检测方法。此外,对于字面量创建的对象(如 {}
和 []
),它们的 constructor
属性将指向 Object
和 Array
构造函数,而不是直接返回预期的构造函数。因此,使用 constructor
属性进行类型检测时需要谨慎处理。
void 0
、undefined
和 null
区别
- void 0:
void 0
是一种常见的用法,用于获取 undefined 值。void
是一个一元运算符,它对表达式进行求值并返回 undefined。void 0
的结果始终是 undefined。例如:
console.log(void 0); // 输出 undefined
- undefined:undefined 是一个预定义的全局变量,它表示一个未被赋值的变量。当一个变量被声明但未被赋值时,它的默认值是 undefined。也就是说,如果一个变量没有被显式赋予一个值,那么它的值将是 undefined。例如:
let x;
console.log(x); // 输出 undefined
函数中没有显式返回值的情况下,函数的返回值也将是 undefined。
- null:null 是一个表示空值或缺失值的特殊关键字。它表示一个被明确赋予了空值的变量或对象属性。例如:
let y = null;
console.log(y); // 输出 null
null 是一个表示空对象引用的值。当我们想要明确指示一个变量或属性没有对象引用时,可以将其赋值为 null。
typeof 是否能正确判断类型?
- 对于原始类型,除了null以外,typeof操作符都能正确判断类型。
- 对于复杂类型,除了函数以外,都会显示object。
- 建议使用instanceof来判断复杂类型。
instanceof能否判断基本数据类型?
能。比如下面这种方式:
class PrimitiveNumber {
static [Symbol.hasInstance](x) {
return typeof x === 'number'
}
}
console.log(111 instanceof PrimitiveNumber) // true
其实就是自定义instanceof行为的一种方式,这里将原有的instanceof方法重定义,换成了typeof,因此能够判断基本数据类型。
NaN 是什么,用 typeof 会输出什么?
NaN:Not a Number,表示非数字
typeof NaN === 'number'
如何判断一个对象是不是空对象?
// 方法1
Object.keys(obj).length === 0
// 方法2
JSON.stringify(obj) === '{}'
数据类型检测的方式有哪些?
typeof instanceof constructor Object.prototype.toString.call()
JavaScript中的错误有哪几种类型?
- Error 最基本的错误类型,其他类型都集成于该类型。
- EvalError 在使用eval()时,如果出现错误,会抛出该错误。
- RangeError 当数值超出范围时,会抛出该错误。
- ReferenceError 当引用变量时,变量未定义,会抛出该错误。
- SyntaxError 当语法错误时,会抛出该错误。
- TypeError 当类型不匹配时,会抛出该错误。
- URIError 当使用encodeURI()或encodeURIComponent()时,如果参数非法,会抛出该错误。
JS对象类型,基本对象类型以及引用对象类型的区别
分为基本对象类型和引用对象类型 基本数据类型:按值访问,可操作保存在变量中的实际的值。基本类型值指的是简单 的数据段。基本数据类型有这六种:undefined、null、string、number、boolean、 symbol。 引用类型:当复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加 属性时,操作的是实际的对象。引用类型值指那些可能为多个值构成的对象。 引用类型有这几种:Object、Array、RegExp、Date、Function、特殊的基本包装类型 (String、Number、Boolean)以及单体内置对象(Global、Math)。
为什么会有 BigInt 的提案?
BigInt(大整数)的提案是为了解决 JavaScript 中数字范围的限制。在 JavaScript 中,数字类型是基于 IEEE 754 标准 的双精度浮点数,表示范围为 -2^53 到 2^53。这意味着 JavaScript 中的数字有一个有限的精度,不能准确表示超过这个范围的整数。
在某些情况下,需要处理超出双精度浮点数范围的大整数,例如在密码学、大数据计算、科学计算等领域。为了解决这个问题,JavaScript 提案委员会提出了 BigInt 的概念和相应的提案。
BigInt 是一种新的数据类型,用于表示任意精度的整数。它可以表示超过双精度浮点数范围的整数,可以执行大整数运算,如加法、减法、乘法和除法,而不会丢失精度。BigInt 的数据范围只受系统内存的限制。
BigInt 提案的目标是提供一种标准化的方式来处理大整数,使 JavaScript 可以在处理超出双精度浮点数范围的整数时更加可靠和灵活。它提供了一组操作符和方法,用于在 BigInt 上执行各种数值运算,以及将 BigInt 与其他数字类型进行转换。
需要注意的是,BigInt 是一种特殊的数据类型,与常规的数字类型(如整数和浮点数)不同,因此在使用和操作 BigInt 时需要特别注意其语法和规则。
如何判断一个对象是空对象
在 JavaScript 中,可以使用不同的方法来判断一个对象是否为空对象。下面列举几种常用的方法:
- 使用 Object.keys() 方法:该方法返回对象中可枚举属性的数组。如果对象没有可枚举属性,那么返回的数组将为空数组。因此,可以通过检查返回的数组长度是否为0来判断对象是否为空对象。
function isEmptyObject(obj) {
return Object.keys(obj).length === 0;
}
- 使用 for...in 循环:通过遍历对象的属性,如果对象具有可枚举的属性,则判断对象不是空对象。
function isEmptyObject(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
}
- 使用 JSON.stringify() 方法:将对象转换为 JSON 字符串,然后检查字符串是否为空字符串来判断对象是否为空对象。
function isEmptyObject(obj) {
return JSON.stringify(obj) === '{}';
}
请注意,这些方法都是基于对象没有可枚举属性的前提下来判断对象是否为空对象。如果对象的原型链上有属性或者通过 Object.defineProperty() 方法定义了不可枚举的属性,上述方法可能会得出错误的结果。
不同数据类型的值的比较,是怎么转换的,有什么规则
TODO
JS中Map和Object的区别
TODO