原始(Primitive)类型
涉及面试题:原始类型有哪几种?null 是对象嘛?
在 JS 中,存在着 6 种原始值,分别是:
boolean null undefined number string symbol
首先原始类型存储的都是值,是没有函数可以调用的,比如 undefined.toString()。
此时你肯定会有疑问,这不对呀,明明 '1'.toString() 是可以使用的。其实在这种情况下,'1' 已经不是原始类型了,而是被强制转换成了 String 类型也就是对象类型,所以可以调用 toString 函数。
除了会在必要的情况下强转类型以外,原始类型还有一些坑。
其中 JS 的 number 类型是浮点类型的,在使用中会遇到某些 Bug,比如 0.1 + 0.2 !== 0.3,但是这一块的内容会在进阶部分讲到。string 类型是不可变的,无论你在 string 类型上调用何种方法,都不会对值有改变。
另外对于 null 来说,很多人会认为他是个对象类型,其实这是错误的。虽然 typeof null 会输出 object,但是这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。
对象(Object)类型
涉及面试题:对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?
在 JS 中,除了原始类型那么其他的都是对象类型了。对象类型和原始类型不同的是,原始类型存储的是值,对象类型存储的是地址(指针)。当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,但是我们需要找到这个空间,这个空间会拥有一个地址(指针)。
const a = []
对于常量 a 来说,假设内存地址(指针)为 #001,那么在地址 #001 的位置存放了值 [],常量 a存放了地址(指针) #001,再看以下代码
const a = []const b = ab.push(1)
当我们将变量赋值给另外一个变量时,复制的是原本变量的地址(指针),也就是说当前变量 b 存放的地址(指针)也是 #001,当我们进行数据修改的时候,就会修改存放在地址(指针) #001 上的值,也就导致了两个变量的值都发生了改变。
接下来我们来看函数参数是对象的情况
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) // -> ?{ name: 'yck', age: 26}console.log(p2) // -> ?{ name: 'yyy', age: 30}
对于以上代码,你是否能正确的写出结果呢?接下来让我为你解析一番:
- 首先,函数传参是传递对象指针的副本
- 到函数内部修改参数的属性这步,我相信大家都知道,当前 p1 的值也被修改了
- 但是当我们重新为 person 分配了一个对象时就出现了分歧,请看下图
所以最后 person 拥有了一个新的地址(指针),也就和 p1 没有任何关系了,导致了最终两个变量的值是不相同的。
typeof vs instanceof
涉及面试题:判断类型的方法有哪些,分别是什么原理?
- typeof
对于基本类型,typeof可以做出准确的判断(除了null)
对于引用类型,typeof返回的是object,因为所有对象的原型链最终指向Object,Object是所有对象的祖宗
- instanceof
instanceof判断A是否是B的实例,表达式为:A instanceof B。所以instanceof只能判断两个对象是否属于实例关系,不能具体判断一个对象实例属于哪种类型。undefined、null用instanceof 会报错。
- constructor
当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向自身。
4.Object.prototype.toString()
toString()是Object的原型方法,调用该方法,默认返回当前对象的[[Class]] 。这是一个内部属性,其格式为[object Xxx],其中Xxx就是对象的类型。
ps:详细版本(www.jianshu.com/p/f0780b452…)
涉及面试题:typeof 是否能正确判断类型?instanceof 能正确判断对象的原理是什么?
typeof 对于原始类型来说,除了 null 都可以显示正确的类型
typeof 1 // 'number'typeof '1' // 'string'typeof undefined // 'undefined'typeof true // 'boolean'typeof Symbol() // 'symbol'
typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型
typeof [] // 'object'typeof {} // 'object'typeof console.log // 'function'
如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof,因为内部机制是通过原型链来判断的,在后面的章节中我们也会自己去实现一个 instanceof。
const Person = function() {}const p1 = new Person()p1 instanceof Person // truevar str = 'hello world'str instanceof String // falsevar str1 = new String('hello world')str1 instanceof String // true
对于原始类型来说,你想直接通过 instanceof 来判断类型是不行的,当然我们还是有办法让 instanceof 判断原始类型的
class PrimitiveString { static Symbol.hasInstance { return typeof x === 'string' }}console.log('hello world' instanceof PrimitiveString) // true
你可能不知道 Symbol.hasInstance 是什么东西,其实就是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof 'hello world' === 'string',所以结果自然是 true 了。这其实也侧面反映了一个问题, instanceof 也不是百分之百可信的。
类型转换
涉及面试题:该知识点常在笔试题中见到,熟悉了转换规则就不惧怕此类题目了。
首先我们要知道,在 JS 中类型转换只有三种情况,分别是:
- 转换为布尔值
- 转换为数字
- 转换为字符串
我们先来看一个类型转换表格,然后再进入正题