参考
《JS 高级程序设计》
原始值与引用值
- 所谓原始值,就是 Undefined、Null、Boolean、Number、String 和 Symbol。对于原始值的操作,也是操作的实际的值。
- 所谓引用值,就是保存在内存中的对象。因为 JS 不能直接操作对象的内存地址,所以实际操作的是对象的引用。
- 引用值是具体引用类型的实例,它们有自己的属性和方法,很像类,但是不是类
区别
动态属性
- 引用值可以添加,修改和删除其属性和方法。但原始值不可以
// 引用值可以操作它的属性
let person = new Object();
person.name = "Nicholas";
console.log(person.name); // "Nicholas"
// 原始值不可以
let name = "Nicholas";
name.age = 27;
console.log(name.age); // undefined
相同类型的原始值和引用值行为区别
let name1 = "Nicholas";
let name2 = new String("Nicholas");
name1.age = 27;
name2.age = 27;
console.log(name1.age); // undefined
console.log(name2.age); // 26
console.log(typeof name1); // string
console.log(typeof name2); // object
复制值
- 原始值在复制的时候是值传递(传递的是“克隆体”,可以随意修改而不相互影响),而引用类型是引用传递(指针传递,都是指向相同的地址)
let obj1 = new Object();
let obj2 = obj1;
obj1.name = "Nicholas";
console.log(obj2.name); // "Nicholas"
// 可以看到 obj1 和 obj2 指向的都是相同的对象,所以它们显示的内容也相同
函数参数为值传递
- 不管是原始类型还是引用类型,在作为函数参数的时候都是按值传递,在 JS 中函数参数是一个局部变量,都是 arguments 类数组对象的一个值
// 原始值按值传递,这一点是非常好理解的
// 但是引用值也按值传递是怎么回事呢
function setName(obj) {
obj.name = "Nicholas";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
// 到这里,很多人会以为,这是引用传递
// 因为明明函数内的修改,作用到了全局作用域中
// 我们再看一个例子
function setName(obj) {
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
let person = new Object();
setName(person);
console.log(person.name); // "Nicholas"
// 可以看到 obj 的引用并没有被修改,这个在函数内部被重写的对象,在函数结束时就被销毁了
确定引用值
我们知道 typeof 可以适合判断一个变量是否为原始类型,但是像 Array,Object 等都是对象(引用类型),我们需要具体知道它是什么对象的实例。
这个时候 instanceof 就是用来确定对象是什么类型的,顾名思义,a instanceof b 就是问,a 是不是 b 的实例
console.log(person instanceof Object); // 变量 person 是 Object 吗?
console.log(colors instanceof Array); // 变量 colors 是 Array 吗?
console.log(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗?
【注】在 JS 中所有的引用类型都是 Object 的实例