# JS 数据类型面试题答案解析

关于答案解析

  • JS 数据类型 相关面试题答案解析过程是根据自身项目实践以及查阅官方文档等最终的得出结论。
  • 仅供学习参考,评论区感谢补充和纠错 !

# 1、JS 有哪些数据类型,如何判断这些数据类型 ?(腾讯、阿里、滴滴、货拉拉、百度)

答案解析


最新的 ECMAScript 标准定义了 8 种数据类型(7 种基本数据类型、1 种引用数据类型)

分类 数据类型
7 种基本数据类型 string(字符串)、number(数字)、boolean(布尔)、undefined(未定义)、null(空)、Symbol(符号)、BigInt(数值类型,表示任意精度的整数)
1 种引用数据类型 Object 对象:Array(数组)、Function(函数)、Date(时间)等

JS 检测数据类型的 4 种方式

  • typeof
  • instanceof
  • constructor
  • Object.prototype.toString.call()

这四种方法各有自己的优缺点,我们可以根据自己的需要去选择。

① 数据类型检测方法一:typeof

详细解读


语法

typeof 变量;
//或
typeof 变量;

例子

typeof 1; //'number'
typeof ""; //'string'
typeof true; //'boolean'
typeof undefined; // 'undefined'
typeof null; //'object'
typeof [1, 2, 3]; //'object'
typeof {}; //'object'
typeof function () {}; //'function'
typeof Symbol(); //'symbol'
  • typeof 的返回值类型为字符串类型
  • typeof 判断基本数据类型时,除了 null 的输出结果为'object' 其它类型都能正确判断
  • typeof 判断引用数据类型时,除了判断函数会输出'function' 其它都输出'object'

注意点:

null 的数据类型是 object (null 是对一个空对象的引用,是一个占位符)

  • typeof 并不能区分引用数据类型(Array 数组、Date 时间)等
  • 所以我们可以使用 instanceof 来判断引用数据类型

② 数据类型检测方法二:instanceof

详细解读


语法:

obj1 instanceof obj2; // 判断obj1是否为obj2的实例
  • instanceof 用来判断两个对象是否属于实例关系,通过这种关系来判断对象是否属于某一类型。(但不能判断对象具体属于哪种类型)。
  • instanceof 可以准确判断引用数据类型,它的原理是:检测构造函数的 prototype 属性是否在某个实例对象的原型链上。
  • instanceof 返回值为布尔值
点击查看源代码
<script>
  class People {
    constructor(name, age) {
      this.name = name;
      this.age = age;
    }
    eat() {
      console.log(`${this.name}正在吃饭`);
    }
  }
  //Student类继承People类
  class Student extends People {
    constructor(name, age, id) {
      super(name, age);
      this.id = id;
    }
    study() {
      console.log(`${this.name}正在学习`);
    }
  }
  const s = new Student("小明", 15, "0001");
  console.log(s.__proto__ === Student.prototype);
  console.log(Student.prototype.__proto__ === People.prototype); //true
  console.log(People.prototype.__proto__ === Object.prototype); //true
  console.log(Object.prototype.__proto__); //null
  console.log(s instanceof Student); //只要在原型链上,都为true
  console.log(s instanceof People); //只要在原型链上,都为true
  console.log(s instanceof Object); //只要在原型链上,都为true
</script>

image-20220523162449860

③ 数据类型检测方法三:constructor(构造函数)

详细解读


语法:

"".constructor === String; // true
var num = 1;
num.constructor === Number; // true
true.constructor === Boolean; // true
[].constructor === Array; // true
var obj = {};
obj.constructor === Object; // true

当一个函数 F 被定义时,JS 引擎会为 F 添加 prototype 原型,然后在 prototype 上添加了一个 constructor 属性,并让其指向 F 的引用

<script>
  function F() {}
  const f = new F(); // 实例
  console.log(F.prototype.constructor); // function F(){}
  console.log(F.prototype.constructor === F); // 构造器指向F
  console.log(f.constructor === F);
</script>

当执行 const f = new F() 时,F 被当成了构造函数,f 是 F 的实例对象,此时 F 原型上的 constructor 传递到了 f 上,因此f.__proto__.constructor===F简写成f.constructor === F

从上面的整个过程来看,构造函数 F 就是新对象 f(实例)的类型。所以如果某个实例的 constructor 指向某个构造函数,那这个构造函数就是这个实例的类型。

注意

  • constructor 是不稳定的,因为开发者可以重写 prototype,重写后,原有的 constructor 引用会丢失,需要我们重新指定 constructor 的引用
  • 在没有重新指定时,constructor 会默认为 Object

为什么重写 prototype 后,constructor 的默认值会为 Object 呢?

<script>
  function F() {}
  F.prototype = {};
  console.log(F.prototype.constructor);
  // 结果为 Object() { [native code] }
</script>

当 F.prototype={ }时,{ }是 new Object 的字面量(Object 的实例),所以 Object 原型上的 constructor 会被传递给{ },Object 的 constructor 指向的就是 Object 本身。

④ 、数据类型检测方法四:Object.prototype.toString.call()

详细解读

toString()是 Object 的原型方法,调用该方法,默认返回当前对象的 [object type]。其中 type 就是对象的类型。

  • Object对象,直接调用toString() 就能返回 [object Object]
  • 其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息
Object.prototype.toString.call(""); // [object String]
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call(true); // [object Boolean]
Object.prototype.toString.call(Symbol()); // [object Symbol]
Object.prototype.toString.call(undefined); // [object Undefined]
Object.prototype.toString.call(new Function()); // [object Function]
Object.prototype.toString.call(new Date()); // [object Date]
Object.prototype.toString.call([]); // [object Array]
Object.prototype.toString.call({}); // [object Object]
Object.prototype.toString.call(document); // [object HTMLDocument]
Object.prototype.toString.call(window); // [object Window]

# 2、null 和 undefined 的区别 ?

答案解析

  • undefined(未定义):当一个变量被定义(声明)但并没有赋值时,他的初始值就是 undefined。
  • null(空):表示对一个空对象的引用。
    • 当一个变量定好之后,未来是用来保存对象的引用时,我们可以给他赋初始值为 null。
    • 当一个对象使用完,需要对其进行释放内存时,可以将其值设置 null (js 会自动垃圾回收)

相同点

  • undefined 和 null 都是基本数据类型,保存栈中。
  • undefined 和 null 转换为 boolean 布尔值都为 false

不同点:

两者转换为数字类型时,其值不一样

Number(undefined); //NaN
Number(null); //0

特殊点:

undefined == null; //true

# 3、基本数据类型和引用数据类型的区别?

答案解析

比较 基本数据类型 引用数据类型
数据存放位置 基本数据类型存放在中,数据大小确定,内存空间大小可以分配 引用数据类型存放在中,每个空间大小不一样,要根据情况进行特定的配置
变量存储内容 变量中存储的是值本身 变量存储的是地址
变量用来赋值时 把变量的复制一份去赋值 把变量的内存地址复制一份去赋值
存储内容大小 存储值较小 存储值较大

栈和堆的的介绍

详细解读

当我们创建数据时就会占用内存,在内存中主要开辟两类空间:堆内存栈内存

比较 栈(线程) 堆(进程,线程共享)
大小固定 创建时,确定大小(值大小固定),故可能会溢出 大小不固定,可随时增加
存储类型 存储基本数据类型及引用类型数据的堆地址 存储引用类型数据
如何访问 按值访问 按引用(堆内存地址)访问
特点 空间小,运行效率高 空间大,运行效率相对较低
存放规则 按顺序存放,先进后出 无序存储,可根据引用(地址)直接获取

实例:

基本类型和引用类型赋值的区别

<script>
  var a = 10;
  var b = a; //10
  a = 20;
  console.log(a, b); //20 10
</script>
  • a 变量是基本数据类型,他在赋值是把 a 的值复制一份给到 b
  • 所以 b = 10 ,最后我们修改 a 的值,并不会响影到 b

image-20220513164139171

<script>
  var obj = {
    name: "清心",
    age: 34,
  };
  var obj2 = obj; //是把obj中地址赋值给obj2,两者指向的是同一个堆内存中地址
  obj.name = "小丽"; //操作是堆内存中的数据
  console.log(obj === obj2); //true
  console.log(obj.name, obj2.name); //小丽 小丽
</script>

栈内存中存着变量 obj 对堆内存中的地址,而堆内存中存着对应地址的数据内容

image-20220513164726666

当 obj2=obj 时,其实是把 obj 中保存的地址赋值给了 obj2,所以本质上 obj===obj2 比较时,比较的是地址,地址始终没有变,所以两者是全等的。

我们在操 obj.name = '小丽' 时,操作的是堆内存中的数据,所以 obj.name 和 obj2.name 的值是一样的。

# 4、typeof(NaN)返回结果?

答案解析

  • NaN 不是数字的数字类型,所以 typeof(NaN) 返回结果就是 number
  • NaN === NaN 结果为 false,他自己和他自己都不相等
  • 如何判断一个变量的值是 NaN ?
var a = "我" - "你";
isNaN(a) && typeof a === "number";

# 5、以下两种方式的区别?及 typeof 得到的结果(字节)

答案解析


const str1 = "abc";
const str2 = new String("abc");

str1 是基本数据类型

  • 存储在在栈内存中,用 typeof 检测的结果为 string。
  • 当我们把 str1 赋值给别一个变量时,是把 str1 中的值复制一份来赋值。

str2 是引用数据类型

  • 存储在堆内存中,不过变量 str2 中存的是指向堆内存中的地址,用 typeof 检测 str2 结果为 Object。
  • 当我们把 str2 赋值给另一个变量时,是把 str2 中存入的地址复制一分给到了变量。

# 6、typeof 能判断那些类型 ?

答案解析

typeof 能判断的类型有:string、number、boolean、undefined、symbol、function

  • typeof 判断基本数据类型时,除了 null 的输出结果为'object' 其它类型都能正确判断
  • typeof 判断引用数据类型时,除了判断函数会输出'function' 其它都输出'object'

# 7、typeof(null) 为什么返回的是'object' ?

答案解析

  • typeof(null) = object 是 JS 在诞生设计之初留下的历史遗留 BUG 问题
  • 在 JS 中进行数据底层存储的时候是用二进制存储的,这是一定的,而且它的前三位是代表存储的数据类型,而 000 是代表 object 类型也就是引用类型的数据。
  • 而 null 正好全是 0,所以它巧妙的符合 object 类型的存储格式,所以在 typeof 检测的时候,它才会输出 object。

# 8、== 和 === 的区别 ?

答案解析

== 在比较类型不同的变量时,如果两侧的数据类型不同,则会按以下规则进行相应的隐式类型类型转换

  • 对象 --> 字符串 --> 数值
  • 布尔值 --> 数值

转换后,再比较两边的值是否相等,值相等返回 true,不等返回 false;

=== 在比较时,会比较值和类型两个。只要两边值的类型不相等就返回 false

var x = 2;
var y = "2";
(x == y)(
  // 返回 true,因为 x 和 y 的值相同
  x === y
);
// 返回 false,因为 x 的类型是“数字”,y 的类型是“字符串”

不过要注意以下几个特殊情况的比较

NaN === NaN; // false NaN和任何数据都不相等,包括自身
[] == []; // false 比较的是地址
{} == {}; // false 比较的是地址
undefined == null; // true; 特殊情况,记下

对象转字符串,得到的是'[object Object]'

考题 1:以下输出结果

console.log([] == false); // true
  • [] 转换成字符串是'' ,然后'' 转换成数值是 0
  • false 转换成数值是 0 所以最后比较的值是 0==0 ,结果为 true

考题 2:以下输出结果

if ([]) {
  alert("能弹出吗?"); // 可以弹出弹窗
}
  • if 语句中的表达式或值都会被转成 boolean
  • [] 转成布尔值是 true,所以可以弹出。

# 9、什么是变量提升 ?(腾讯、网易、小米)

答案解析

变量提升:是指使用 var 关键字声明的变量会自动提升到当前作用域的最前面。不过只会提升声明,不会提升其初始化。

<script>
  console.log(a); // undefined
  var a = 10;
  function fn() {
    console.log(b); // undefined
    var b = 1;
  }
  fn();
</script>

变量只有被声明后,才能使用

  • 我们在 var a=10; 之前 console.log(a); 之所以不会报错,就是因为变 a 的声明被提前到了当前作用域的最顶部
  • 不过只提升了声明,并没会提升初始化,所以打印结果为 undefined; (变量声明初始化,其默认值是 undefined)

上面代码可以看成如下:

<script>
  var a;
  console.log(a); // undefined
  a = 10;
  function fn() {
    var b;
    console.log(b); // undefined
    b = 1;
  }
  fn();
</script>

补充

  • 函数声明也会被提升,函数和变量相比,会被优先提升。
  • 这意味着函数会被提升到更靠前的位置,如果出现了重名的变量和函数,声明提升时会以函数为主。
<script>
  console.log(num); // function num(){console.log('函数');}  函数被优先提升
  var num = 1;
  console.log(num); // 1 在从上往下执行时num变量赋值为 1
  function num() {
    console.log("函数");
  }
  console.log(num()); // 报错,因为变量num被重新赋值为1,不会再有函数了
</script>

# 10、const、let、var 区别(叠纸、字节、阿里)

答案解析

const、let、var 三者的区别,我们可以从以下 5 个点来展开对比

  • 变量提升和暂时性死区: var 存在变量提升,let 和 const 不存在变量提升,所以 let 和 const 会存在暂时性死区
  • 块级作用域: var 不存在块级作用域,let 和 const 存在块级作用域
  • 重复声明: var 允许重复声明变量,let 和 const 在同一作用域下不允许重复声明变量
  • 修改变量: var 和 let 声明的变量可以修改,const 是不可以的。
  • 使用:const 用来声明常量,引用类型值。其它情况推荐用 let ,避免用 var

① 变量提升

详细解读

  • var 声明的变量存在变量提升,即变量可以在声明之前被调用。
  • let 和 const 不存在变量提升,即它们所声明的变量一定要在声明后使用,否则会报错
<script>
  console.log(a); // undefined
  var a = 1;
</script>
<script>
  console.log(b); // Cannot access 'b' before initialization
  let b = 2;
  console.log(c); // Cannot access 'c' before initialization
  const c = 3;
</script>

② 块级作用域

详细解析

var 不存在块级作用域,let 和 const 存在块级作用域

{
  var a = 1;
}
console.log(a); // 1
{
  let b = 2;
}
console.log(b); // Uncaught ReferenceError: b is not defined
{
  const c = 3;
}
console.log(c); // Uncaught ReferenceError: c is not defined

③ 重复声明

详细解读

var 允许重复声明变量,let 和 const 在同一作用域下不允许重复声明变量

<script>
  var a = 1;
  var a = 2;
</script>
<script>
  let b = 1;
  let b = 2; // Uncaught SyntaxError: Identifier 'b' has already been declared
</script>
<script>
  const c = 1;
  const c = 2; // Uncaught SyntaxError: Identifier 'c' has already been declared
</script>

④ 修改变量

详细解读

  • var 和 let 声明的变量,可以修改
  • const 声明的是一个只读的常量。一旦声明,常量的值就不能改变
<script>
  var a = 1;
  a = 2;
  let b = 2;
  b = 3;
  console.log(a, b); // 2 3
</script>
<script>
  const c = 3;
  c = 3;
  console.log(c); // ncaught TypeError: Assignment to constant variable.
</script>

⑤ 使用

详细解读

  • 能用 const 的情况尽量使用 const,比如声明的变量是用来保存 对象、数组等引用类型时,用 const
  • 其他情况下大多数使用 let,比如 for 循环,避免使用 var

提示:var 在全局作用域中声明的变量,相当于 window 对象的属性。

# 11、const 定义的值一定是不能改变的吗?

答案解析

  • const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个栈内存地址所保存的数据不得改动。
  • 对于简单类型的数据(数值、字符串、布尔值)值就保存在变量指向的那个栈内存地址,因此等同于常量。
  • 引用类型的数据(主要是对象和数组)变量指向的栈内存地址,保存的只是一个指向实际数据的指针
  • const 只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

所以如果是 const 声明的是一个引用类型的变量,其引用类型的结构是可以发生改变的。

# 12、const 声明了数组,还能 push 元素吗,为什么 ?

答案解析


可以

  • 因为 const 声明的变量保存的只是栈内存中的地址,只是一个指向实际数据的指针。指针指向堆内存中保存的数据。
  • const 只能保证栈内存中的地址不变,但是堆内存中的数据如何改变是没有办法控制的。
  • push 方法相当于是改变了堆内存中的数据结构。
上次更新时间: 6/8/2023, 9:23:17 PM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X