JS类型系统
JS的类型系统包括原生对象、宿主对象和浏览器扩展对象
原生对象
原生对象分为原始类型和对象类型,原始类型分为空值和包装对象,对象类型分为构造器对象和单体内置对象
原始类型
空值:JS有两个空值,分别是undefined和null,逻辑上,undefined表示原始类型的空值,null表示对象类型的空值
包装对象:String、Number、Boolean三种
对象类型
构造器对象:Object、Function、Date、Array、Error和RegExp。
注意: 如果使用new构造器函数定义包装对象,那么包装对象也是构造器对象,如new String('hello')
单体内置对象:Math、JSON、全局对象和arguments四种,它们不需要声明可以直接使用
宿主对象
宿主对象指在宿主环境下特有的对象,常见的宿主环境如浏览器、操作系统等,浏览器环境中的宿主对象有window、document、navigator等。
浏览器扩展对象
浏览器扩展对象是浏览器自己实现的对象,比如Debug对象,早期IE浏览器的ActiveXObject对象。
理解包装对象
上面提到的字符串String、数字Number、布尔值Boolean之所以称作包装对象,是因为在一定条件下会它们可以自动转化为对象。
包装对象是特殊的引用类型,当读取字符串、数值和布尔值的属性或方法时,创建的临时对象就是包装对象
以字符串为例,通过new String()的方式将其转换为包装对象,对象会继承字符串的属性和方法,当属性或方法引用结束,这个新建对象就会销毁。
var s = 'hello'
var s2 = s.slice(3)
// 过程如下: 创建实例 调用方法 销毁实例
var s = new String('hello')
var s2 = s.slice(3)
s = null
包装对象是引用类型,所以执行流离开当前作用域之前对象一直保存在内存中。
var s = new String('hello')
s.name = 'wmui'
alert(s.name) // wmui
包装对象的创建方式有两种:Object方式和构造函数方式
Object方式
var s = new Object('hello')
var b = new Object(true)
var n = new Object(1)
构造函数方式
var s = new String('hello')
var b = new Boolean(true)
var n = new Number(1)
用构造函数创建包装对象和调用转型函数的结果是不一样的,转型函数返回的是基本类型值,构造函数返回引用类型值
var s = 'hello'
var s2 = String('hello')
var s3 = new String('hello')
console.log(typeof s, typeof s2, typeof s3) // string string object
相等运算符将原始值和包装对象视为相等,因为包装对象调用其valueOf()
方法后,返回的原始值就是自身。但它们不是恒等的,因为它们的数据类型不同
var s = new String('hello')
var s2 = 'hello'
console.log(s == s2) // true
console.log(s === s2) // false
总结
重点就一句话,包装对象是特殊的引用类型
原始值和引用值
编程语言中,能够表示并操作的值的类型叫做数据类型,JS的数据类型分为原始类型(也叫基本类型或简单类型)和引用类型(也叫复杂类型)。原始类型包括String、Number、Boolean、Null、Undefined
五种,引用类型就是Object
。原始类型的值称作原始值,引用类型的值称作引用值。
原始值
原始值也叫简单值,是最简单的数据形式,所以它们是不可以再细化的。原始值的最大特点就是不可更改。
var s = 'hello'
s.toUpperCase()
alert(s) // hello
引用值
引用值由JS对象组成(可以是多种不同类型的对象),和原始不同得是它的值是可以修改的,也正因为如此致使它在内存中占据的空间大小是未知的。
var o = {name: 'wmui'}
o.name = 'hello'
o.age = 10
console.log(o.name, o.age) // hello 10
存储方式
原始值由于其占据的内存空间是固定可计算的,为提升变量的查询速度将其存储在栈(stack)中。
引用值由于其大小可变,放在栈中会降低变量查询速度,所以它是在堆(heap)中存储的,存储在变量处的是一个指向堆中对象的指针。
访问方式
原始值引用它们时会在栈中创建一个新的值,所以改变原始值不会影响引用它们的值。
var s = 'hello'
var s2 = s
s = 'world'
console.log(s, s2) //world hello
引用值是一个指向对象的指针,当对象改变时,所有指向该对象的引用值也会改变。
var o = {name: 'wmui'}
var o2 = o
var o3 = o2
o.name = 'hello'
console.log(o.name, o2.name, o3.name) // hello hello hello
动态属性
原始值不能添加属性和方法。引用值可以添加、修改、删除属性和方法。
var s = 'hello'
s.name = 'wmui'
alert(s.name) // undefined
var o = {name: 'wmui'}
o.age = 10
alert(o.age) // 10
总结
原始值在栈中存储,引用值在堆中存储
原始值不能动态添加属性,引用值可以动态添加属性
原始值引用时会创建一个副本,引用值创建一个指向对象的指针