JavaScript面试题(一)

本文详细介绍了JavaScript的八种数据类型,包括原始数据类型(如Undefined、Null、Boolean等)和引用数据类型(如Object、Array),以及它们在栈和堆中的存储区别。此外,文章还讨论了数据类型检测的方法(如typeof、instanceof、constructor),以及如何获取安全的undefined值和Object.is()与比较操作符的区别。

JavaScript 有哪些数据类型,它们的区别?

JavaScript共有八种数据类型,分别是UndefinedNullBooleanNumberStringObjectSymbolBigInt
其中SymbolBigIntES6 中新增的数据类型:

  • Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
  • BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数, 使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围, 要创建BigInt,只需在整数的末尾追加n即可。

这些数据可以分为原始数据类型引用数据类型

  • 栈:原始数据类型(UndefinedNullBooleanNumberString
  • 堆:引用数据类型(对象数组函数

两种类型的区别在于存储位置的不同:

  • 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
  • 引用数据类型存储在堆(heap)中的对象,占据空间大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:

  • 在数据结构中,栈中数据的存取方式为先进后出。
  • 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。

在操作系统中,内存被分为栈区和堆区:

  • 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  • 堆区内存一般由开发着分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收。

数据类型检测的方式有哪些

  • typeof
    console.log(typeof 2);              // number
    console.log(typeof true);           // boolean
    console.log(typeof "");             // string
    console.log(typeof "fr");           // string
    console.log(typeof []);             // object
    console.log(typeof function () {}); // function
    console.log(typeof {});             // object
    console.log(typeof undefined);      // undefined
    console.log(typeof null);           // object
    console.log(typeof Symbol());       // symbol
    console.log(typeof 10n);            // bigint
    console.log(typeof new Set());      // object
    console.log(typeof new WeakSet());  // object
    console.log(typeof new Map());      // object
    console.log(typeof new WeakMap());  // object

其中数组对象null 都会被判断为 object,其他判断都正确。

  • instanceof
    instanceof 可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。
    console.log(2 instanceof Number)                   // false
    console.log(Number(2) instanceof Number)           // false
    console.log(new Number(2) instanceof Number)       // true
    console.log(true instanceof Boolean)               // false
    console.log('fr' instanceof String)                // false
    console.log([] instanceof Array)                   // true
    console.log(function () { } instanceof Function)   // true
    console.log({} instanceof Object)                  // true
    console.log(Symbol() instanceof Symbol)            // false
    console.log(10n instanceof BigInt)                 // false
    console.log(new Set() instanceof Object)           // true
    console.log(new WeakSet() instanceof Object)       // true
    console.log(new Map() instanceof Object)           // true
    console.log(new WeakMap() instanceof Object)       // true
    console.log(undefined instanceof Object)           // false
    console.log(null instanceof Object)                // false

可以看到,instanceof 只能正确判断引用数据类型,而不能判断基本数据类型。
instanceof 运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。

  • constructor
    console.log((2).constructor === Number)                   // true
    console.log((new Number(2)).constructor === Number)       // true
    console.log((true).constructor === Boolean)               // true
    console.log(('fr').constructor === String)                // true
    console.log(([]).constructor === Array)                   // true
    console.log((function () { }).constructor === Function)   // true
    console.log(({}).constructor === Object)                  // true
    console.log((Symbol()).constructor === Symbol)            // true
    console.log((10n).constructor === BigInt)                 // true
    console.log((new Set()).constructor === Object)           // false
    console.log((new WeakSet()).constructor === Object)       // false
    console.log((new Map()).constructor === Object)           // false
    console.log((new WeakMap()).constructor === Object)       // false
    console.log((undefined).constructor === Object)           // Uncaught TypeError: Cannot read properties of undefined (reading 'constructor')
    console.log((null).constructor === Object)                // Uncaught TypeError: Cannot read properties of undefined (reading 'constructor')

constructor 有两个作用,一是判断数据的类型,二是对象实例通过 constrcutor 对象访问它的构造函数。
需要注意,如果创建一个对象来改变它的原型,constructor 就不能用来判断数据类型了:

    function Fn() { }

    Fn.prototype = new Array();

    var f = new Fn()

    console.log(f.constructor === Fn)       // false
    console.log(f.constructor === Array)    // true

  • Object.prototype.toString.call()

Object.prototype.toString.call() 使用 Object 对象的原型方法 toString 来判断数据类型:

    let a = Object.prototype.toString;

    console.log(a.call(2))                   // [object Number]
    console.log(a.call(new Number(2)))       // [object Number]
    console.log(a.call(true))                // [object Boolean]
    console.log(a.call('fr'))                // [object String]
    console.log(a.call([]))                  // [object Array]
    console.log(a.call(function () { }))     // [object Function]
    console.log(a.call({}))                  // [object Object]
    console.log(a.call(Symbol()))            // [object Symbol]
    console.log(a.call(10n))                 // [object BigInt]
    console.log(a.call(new Set()))           // [object Set]
    console.log(a.call(new WeakSet()))       // [object WeakSet]
    console.log(a.call(new Map()))           // [object Map]
    console.log(a.call(new WeakMap()))       // [object WeakMap]
    console.log(a.call(undefined))           // [object Undefined]
    console.log(a.call(null))                // [object Null]

同样是检测对象 obj 调用 toString 方法,obj.toString()的结果和 Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为 toStringObject 的原型方法,而 Arrayfunction 等类型作为 Object 的实例,都重写了toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法(function类型返回内容为函数体的字符串,Array 类型返回元素组成的字符串…),而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString() 不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。

nullundefined 区别

首先 UndefinedNull 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefinednull
undefined 代表的含义是未定义,null 代表的含义是空对象。
一般变量声明了但还没有定义的时候会返回 undefinednull 主要用于赋值给一些可能会返回对象的变量,作为初始化。
undefinedJavaScript 中不是一个保留字,这意味着可以使用 undefined 来作为一个变量名,但是这样的做法是非常危险的,它会影响对 undefined 值的判断。我们可以通过一些方法获得安全的 undefined 值,比如说 void 0
当对这两种类型使用 typeof 进行判断时,Null 类型化会返回 "object",这是一个历史遗留的问题。当使用双等号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false

intanceof 操作符的实现原理及实现

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

    function myInstanceof(left, right) {
      let proto = Object.getPrototypeOf(left)
      let prototype = right.prototype;

      while (true) {
        if (!proto) return false;
        if (proto === prototype) return true;
        proto = Object.getPrototypeOf(proto)
      }
    }

如何获取安全的 undefined 值?

因为 undefined 是一个标识符,不是保留字,所以可以被当作变量来使用和赋值,但是这样会影响 undefined 的正常判断。
表达式 void没有返回值,因此返回结果是 undefinedvoid 并不改变表达式的结果,只是让表达式不返回值。因此可以用 void 0 来获得 undefined

Object.is() 与比较操作符 ===== 的区别?

使用双等号(==)进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。
使用三等号(===)进行相等判断时,如果两边的类型不一致时,不会做强制类型准换,直接返回 false
使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0+0 不再相等,两个 NaN 是相等的。

    console.log(NaN == NaN)             // false
    console.log(NaN === NaN)            // false
    console.log(Object.is(NaN, NaN))    // true
    console.log(-0 == +0)               // true
    console.log(-0 === +0)              // true
    console.log(Object.is(-0, +0))      // false
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值