数据类型
-
JavaScript有哪些数据类型?
7种基本数据类型:Undefined, Null, Boolean, String, Number, Bigint, Symbol;
1种复杂数据类型:Object.
注意Bigint是新增的数据类型,可以以任意精度表示整数,可以安全地存储和操作大整数;Number则是基于IEEE754标准的双精度64位二进制格式值。
-
原始值和引用值是什么?有什么区别?
变量有按值访问和按引用访问两种方式。
当声明基本数据类型的变量并赋值时,变量里存储的值都是原始值,即我们访问的是变量的实际值。
但是声明对象并对对象进行操作时,我们访问的是对象的引用值。因为JavaScript不允许访问对象的内存位置,所以我们只能访问对象的引用值而不是对象本身。
区别:
-
保存位置不同:原始值保存在全局作用域的栈内存中,引用值实际上是一个指针,指向存储在堆内存的对象中。
-
传递方式不同:当进行变量复制的操作时,原始值会被复制一份副本赋给新变量,而引用值则是复制一个指向同一对象的指针再赋给新变量,内存中的对象不变。
函数的参数传递规则等同于变量复制,即参数传递是值传递的方式。
因此对于引用值的值传递,就是传递一份复制的指针;而引用传递是直接传递原指针。
-
undefined和null的区别是什么?
- 意义不同:
undefined是未初始化变量,null是对象空指针。
当变量被声明却没有初始化时,会默认其值为undefined,而无需显式声明。
但是当对象被声明,而当下还没有合适的变量值时,我们会选择给对象赋值为null。
- 对未声明的变量和未初始化变量调用
typeof时,得到的结果都是undefined,
但是对空指针对象调用typeof时,得到的结果是object.
-
不同类型与布尔值之间的转换规则是什么?
要弄清楚除了布尔型的false以外,其他数据类型还有哪些值是falsy的(假值):
| 数据类型 | falsy值 |
|---|---|
| String | “”(空字符串) |
| Number | 0、NaN |
| Undefined | undefined |
| Object | null |
控制流语句会自动进行其他数据类型和布尔型之间的转换。
-
不同类型与数值类型之间的转换规则是什么?
首先要知道,不同类型转换为数值类型有三种方式:Number()转型函数、parseInt()、parseFloat().
前者可以处理任何数据类型:
| 数据类型 | 数值 |
|---|---|
| Null | 0 |
| Undefined | NaN |
| Boolean | true --> 1, false --> 0 |
| String | ①空字符串 --> 0;②纯数值字符(有效整数、浮点数、十六进制数) --> 十进制数值; ③其他字符串 --> NaN |
| Object | 先调用valueOf(),不是NaN --> 直接返回;是NaN --> 调用toString(),根据String的转换规则处理 |
后两个方法是针对String类型的:
从第一个非空格字符开始,依次检测每一个字符,直到遇见字符串结尾或者非数值字符。
如“blue1234”会返回1234.
二者区别:
-
parse方法遇到空字符串会返回NaN,而Number()会返回0; -
parse方法可以解析十六进制。
-
什么是
NaN?如何判断?
NaN,即“Not a Number”,不是数字,但是一个特殊的数值。
原本要返回一个数值但是操作失败时,返回值会是NaN。
当我们需要判断某个值是否是NaN时,可以使用isNaN()方法:
首先我们需要知道,NaN是一个数值,因此要判断某个值是否是NaN时,第一步是进行数据类型的转换,第二步才是判断是否为NaN。
如果一个值可以转换为数值,则isNaN()会返回false,反之则true。
-
基本数据类型中的
Symbol有什么用?
Symbol值是唯一、不可变的值。
用处:
- 可以作为对象独一无二的属性名
对象的属性名可以是字符串或者Symbol,凡是Symbol的属性名,都不会与其他属性产生冲突。
- 可以用于定义对象非私有但仅用于内部的方法
本质上还是定义对象的唯一属性名,但是因为Symbol值作为对象的属性名时不会被常规方法遍历得到,所以可以用于定义仅用于内部的方法。
注意点:
Symbol值没有包装类,直接使用Symbol()声明Symbol()的参数仅用于对Symbol值的描述,不会影响Symbol值的唯一性- 如果需要复用
Symbol值,需要在全局符号注册表中用Symbol.for()注册
-
隐式的类型转换有哪些情况?
-
四则运算符中的隐式转换:
-
加法操作中如果有一个操作数是字符串,就会把另一个操作数的类型转换为字符串
-
其他操作中只要有一个操作数是数值,就会把另一个操作数转换为数值
-
等于
(==)和不等于(!=)操作符中的隐式转换:
等于和不等于操作符会在比较前进行类型转换(全等和不全等操作符不会强转类型)。
-
有布尔,转数值
-
字符和数值,转数值
-
null和undefined,不能转,但是null == undefined -
NaN不用转,和谁比都是不相等 -
有一个是对象,调用
valueOf()比较原始值两个都是对象,比较是否是同一个对象
数据类型判断
-
类型判断有哪几种方式?
typeof操作符
typeof通常用于基本数据类型的判断,因为原始值使用typeof来判断类型是很方便的,但是引用值使用typeof时,只会得到function或object的结果,并不能很直接的判断出引用值的类型。
typeof操作符有几个注意点:
(1) 不能判断null类型。如果对null使用typeof,结果会是object,这是因为null的意义就是空指针对象。
(2) typeof操作符是未声明变量的唯一有效操作,返回结果是undefined。
instanceof操作符
instanceof通常用于判断原型和实例之间的关系,即通过原型链的方式来判断对象的类型。
instanceof的原理是检测构造函数的prototype属性是否出现在实例对象的原型链上,具体原理见第二道题。
Object.prototype.toString.call()
能判断的方式最完整。
console.log(Object.prototype.toString.call([])); // [object Array]
但是要注意,这个方式没办法判断出实例对象和原型的关系。
/*
父类SuperType 子类SubType
let instance = new SubType();
*/
console.log(Object.prototype.toString.call(instance)); // [object Object] 打印结果与父类无关
console.log(instance instanceof SuperType); // true
isXXX的API
判断特定类型的API,比如isArray()、isNaN()等。
-
instanceof操作符的原理是什么?
首先要知道instanceof的语法是:object instanceof constructor,即 instanceof检测的是实例对象和构造函数二者的关系,再进一步讲,它检测构造函数的prototype属性是否出现在实例对象的原型链上。
那么 instanceof是怎么知道constructor的原型对象有没有在object的原型链上出现过的呢?
// 模拟instanceof的查找过程
function instance_of(obj, constructor){
// 实例对象的原型链
let obj_proto = obj.__proto__;
// 构造函数的原型对象
let prototype = constructor.prototype;
// 循环遍历obj的原型链
while(obj_proto !== null){
// 说明constructor的原型对象有出现在obj的原型链上,返回true
if(obj_proto === prototype) return true;
obj_proto = obj_proto.__proto__;
}
// obj_proto为null时仍未返回true,则说明找不到
return false;
}