JS基本类型和引用类型

888 阅读3分钟

JS中某种意义上来说只包含两种类型的变量,即基本类型引用类型。
这两种类型的变量解析方式完全不同,对于基本类型(Undefined、Null、Boolean、Number、String)是直接操作保存在变量中的值。而对于复杂类型也就是对象,如果是直接复制这个对象那么操作的是对象的引用,如果为对象添加属性那么操作的是实际的对象。

动态属性

定义两种类型变量的方式类似,都是创建变量并赋值,但是保存到变量中后,操作方式却完全不同。
可以通过如下代码,体会区别:

// 引用类型
var foo = {}
foo.name = 123
console.log(foo.name) // 123
// 基本类型
var bar = 'bar'
bar.name = 123
console.log(bar.name) // undefind

如上代码可以看到只能给引用类型动态的添加属性。

复制变量

我们可以通过复制变量这个操作来看看为什么基本类型和引用类型使用上会有区别。
基本类型:

var num1 = 123
var num2 = a
console.log(num1,num2) // 123, 123

num1 = 5
console.log(num1,num2) // 5, 123

993539-20161124221816643-1579754944.png

当num2复制num1后,栈内存中会生成一个新的变量num2,num1和num2的值相等,但是是毫不相关的变量,以后的操作都不会互相影响。
复杂类型:

obj1 = {}
obj2 = obj1

obj1.a = 123
console.log(obj1, obj2) // {a: 123}, {a: 123}

// 赋予obj2新的对象
obj2 = {}
obj2.a = 666
console.log(obj1, obj2) // {a: 123}, {a: 666}

993539-20161124223028768-834204270.png
··
复杂类型复制时第一步和简单类型相似,都会在栈内存中生成一个新的obj2变量,但是这个变量仅仅是指向堆内存中的一个指针。由图上可知obj2复制obj1后他们指向了同一个对象,所以给这个对象赋值时,对于obj2和obj1是没有区别的,但是当obj2等于一个新的对象时,obj1和obj2指向了不同的对象,他们再无关系。

传递参数

js中的参数传递都是复制变量,也就是把函数内部的值传递给函数内部的参数,所以这个过程一样遵循上面的规律,我们来看一下例子:

// 基本类型的参数传递
function addOne (num) {
	num = num + 1;
  return num
}
var count = 10
addOne(count) // 11
console.log(count) // 10
// 引用类型的参数传递
function setAge (obj) {
	obj.age = 23
}
var person = {}
setAge(person)
console.log(person.age) // 23

以上两个例子说明了函数的传参就是简单的复制变量,基本类型复制后就没有联系,所以原来的变量没有变化,而引用类型复制后指向同一个对象,所以添加属性,两个变量指向的同一个对象就发生了变化。

检测类型

前面我们说过typeof可以检测变量类型,但是对于引用类型具体指向时作用不大,所以js为我们提供了instanceof这个操作符,它能判断变量是否是给定的引用类型,整个判断过程会沿着原型链向下,直到指向Object,这是因为所有的引用类型的值都是Object的实例,所以在检测一个引用类型的值是不是Object的实例时,instanceof总是返回true。反过来说instanceof判断基本类型值总是返回false。