当面试官问:JS中原始类型有哪些?

这个问题基本回答:

原始类型有:string/number/boolean/null/undefined/symbol,总共六类

其中包含了ES6新增的symbol,以及ES10新增的BigInt

回答出上面的答案,这个问题其实你仅仅只是回答了最浅层的答案,下面我们来看下由这个问题引申出来的扩展。

扩展1 :null 是对象么?

null 不是对象。虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。

js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息

  • 000:对象
  • 010:浮点数
  • 100:字符串
  • 110:布尔
  • 1:整数

扩展2:原始类型和对象类型的异同?

在 JS 中,除了原始类型那么其他的都是对象类型了。

对象类型和原始类型的区别:存储方式的不同。原始类型存储的是值,对象类型存储的是地址(指针)。当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址(指针)。

扩展3:函数参数是对象的时候,会发生什么?

这个地方大家可能一下想不起来这里要考察的是什么?

我们先来看个例子:

function test(person) {
  person.age = 26
  person = {
    name: 'yyy',
    age: 30
  }

  return person
}
const p1 = {
  name: 'yck',
  age: 25
}
const p2 = test(p1)
console.log(p1) // 输出什么?
console.log(p2) // 输出什么?

上面输出什么呢?

 首先,函数传参是传递对象指针的副本,

到函数内部修改参数的属性这步,p1的值也被修改了,

但是我们重新为person分配了一个对象时就出现了分歧,

所以最后person拥有了一个新的指针,也就和p1没有任何关系了,导致最终两个的值是不一样的。

扩展4:如何正确的判断类型?

判断类型的方法有:typeof/instanceof/Object.prototype.toString.call() ,那这些方法的优缺点又是什么呢?

  • typeof:typeof 不能准确的判断类型

typeof 对于原始类型来说,除了 null 都可以显示正确的类型,typeof null 结果是 object

typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型

  • instanceof

如果想要判断一个对象的正确类型,可以使用instanceof,instanceof判断的原理是通过原型链来判断:即判断实例对象的__proto__ 和 构造函数的prototype是不是统一引用。

( instanceof用法: x instanceof Function 判断x是否是函数,也就是左边变量的隐式原型是否等于右边的显式原型)

如果想用instanceof来判断原始类型的话,我们可以利用hasInstance自己实现:

class PrimitiveString {
  static [Symbol.hasInstance](x) {
    return typeof x === 'string'
  }
}
// 使用
console.log('test' instanceof PrimitiveString) // true
  • Object.prototype.toString.call()

这个方法可以准确的判断原始类型和对象类型的正确类型,这里着重给大家介绍一下这个方法的原理,便于大家理解。

首先,我们先明确一下toString这个方法

let test = {name: 'test'}
console.log(test.toString())

上面代码,大家第一印象可能是输出 {name: 'test'} 的字符串形式,但其实输出的是

这是为什么呢?

这里就不得不说到我们的原型和原型链了。接下来我们来分析一下:

首先,test 这个变量本身是没有toString这个方法的,所以,当我们调用toString方法的时候,就会沿着原型链向上查找,所以找到的是test原型链上的toString方法,也就是:

 这个原型链上的方法又是哪里来的呢?

实例对象的隐式原型等于构造函数的显式原型:即

我们知道,JS的大原型其实是Object构造函数的prototype指向的那个Object,JS的世界的来源就是它。

 这个toString才是原汁原味的toString,它的返回一直是

一个字符串个格式的[object xxxx] xxxx代表着调用这个方法的“数据”的数据类型。

 到这里大家都明白了我们的toString 返回的正确格式到底是什么,又是来自于哪里,但有些同学可能就会问,那为什么有些数据类型的toString方法返回的是变量的字符串形式呢?

举例:

上面定义了一个函数变量,为什么toString返回的是变量的字符串形式呢?

原因:JS其实重写了某些数据类型的toString方法 

有些同学可能会产生疑虑,怎么确定调用的toString方法是重写的那个,还是原汁原味的那个呢?

可以跟着我继续看下去

上面我们说过了,JS的大千世界,最后的大原型其实是Object构造函数的prototype指向的那个Object。

所以,为了验证我们的Function的toString方法到底来自哪里,我们把Function上的toString方法删除,看看会发生什么?

(这里说明一下,变量func 本身是没有toString方法的,所以,就会沿着原型链上面去找,也就是Function)

看到这里想必大家都明白了

我们把Function上的toString方法删掉之后,就会沿着原型链上继续查找toString方法,继而也就找到了我们原汁原味的那个toString方法,因此最后输出的结果是'[object Function]'了

因此,Function类型上其实是重写了toString方法的。

那为什么Function会继续往上查找呢?

这里我们再来回顾一下原型链接:实例对象的隐式原型(__proto__)等于构造函数的显式原型(prototype),也就是

所以呢,当我们把Function.prototype上的toString方法删掉之后,就会去找Function.prototype.__proto__上的toString方法,那也就是我们的Obejct.prototype上的toString方法了。

(其实我们最终的原型链的顶端是null,也就是Object.prototype.__proto__,当查找方法的时候,会一直沿着原型链上查找,直到查找到为止,如果一直没有查找到,那就会在Object.prototype.__proto__结束,返回null)

到这里大家应该就知道了,我们为什么使用Obejct.proptoype.toString这个方法来判断类型了,因为,它是JS世界的来源,是我们原汁原味的方法。

这里大家可能又会问,我们在判断的时候总不能先把类型上的toString方法删掉,再判断吧?

那肯定是不行的,所以,我们的 call 方法就用上了

采用call方法去改变Object.prototype.toString方法this的指向。

最终我们就得到了 Object.prototype.toString.call()方法来判断我们的类型了,这也是最为准确的方法,建议大家使用这个方法

扩展5:原始类型和对象类型在内存中的存储方式? 

  • 对于原始类型,名字和值都存在栈内存中。
  • 对于对象类型,名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值。

总结:

  • 原始类型包括:string/number/boolean/null/undefined/symbol/BigInt
  • null不是对象
  • 原始类型和对象类型最主要的区别在于存储方式的不同:原始类型存储的是值,对象类型存储的是地址
  • 使用Object.prototype.toString.call()判断类型是最准确的

提问:

  1. 判断数据类型的方法都有哪些?
  2. instanceof 可以判断基本类型吗?
  3. typeof 函数 会返回什么?

大家可以思考下,再遇到上面的问题,你回答的思路是什么呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值