概念
- ECMA 变量可以包含两种不同类型的数据:原始值和引用值
- 原始值(primitive value) 就是最简单的数据
- 引用值(reference value) 是由多个值构成的对象
- 在把一个值赋给变量时,JavaScript 引擎必须确定这个值的类型
- 保存原始值的变量是按值(by value)访问的,因为我们操作的就是存储在变量中的实际值
- 引用值是保存在内存中的对象。JavaScript 不允许直接访问内存位置,因此也就不能直接操作对象所在的内存空间。在操作对象是,实际上操作的是对该对象的引用(reference)而非实际的对象本身。因此,保存引用值的变量是按引用(by reference)访问的
动态属性
- 原始值和引用值的定义方式很类似,都是创建一个变量,然后给他赋一个值。不过在变量保存了这个值后,可以对这个值做什么,则很不同。引用值可以随时添加、修改和删除其属性和方法
const person = new Object();
person.name = 'Nicholas';
console.log(person.name); // 'Nicholas'
let name = 'Nicholas';
name.age = 27;
console.log(name.age); // undefined
- 在给对象一个新属性后即可访问,直到对象被销毁或属性被显示删除
- 原始值不能有属性,尽管尝试给它添加属性不会报错
- 只有引用值可以动态添加后面可以使用的属性
const name1 = 'Nicholas';
const name2 = new String('Nicholas');
name1.age = 27;
name2.age = 26;
console.log(name1.age); // undefined
console.log(name2.age); // 26
console.log(typeof name1); // string
console.log(typeof name2); // object
- 注意:原始类型的初始化只能使用原始字面量形式声明,如果使用的是
new
关键字,则 JavaScript 会创建一个Object
类型的实例,但其行为类似原始值
复制值
-
除了储存方式不同,原始值和引用值在通过变量赋值时也有所不同
-
原始值
-
复制前后的两个值是完全独立的,互不干扰
-
let num1 = 5; let num2 = num1; num1 = 10; console.log(num2); // 5
-
-
引用值
-
复制出来的值实际上是一个指针,它指向储存在堆内存中的对象
-
复制过后,两个变量实际上指向同一个对象,因此一个变量上面的变化会在另一个变量上反映出来
-
const obj1 = new Object(); const obj2 = obj1; obj1.name = 'Nicholas'; console.log(obj2.name); // 'Nicholas'
-
传递参数
- 所有函数的参数都是按值传输的,函数外的值传递到函数内,相当于从一个变量复制到另一个变量一样
- 在传递原始值时我们非常容易理解,因为本身原始值复制过后都是独立的,复制过后,函数内外操作都不会彼此影响
- 而在传递引用值时,函数内外设置属性时,双方都会反映这个变化,这并非是因为函数传参是按引用传输,而是因为按值传输后的两个值都指向同一堆内存
let num = 1;
function fn1(num) {
num += 3;
}
fn1(num);
console.log(num); // 1
let obj = new Object();
function fn2(obj) {
obj.name = 'Nicholas';
obj = new Object();
obj.name = 'Greg';
}
fn2(obj);
// 这里就很能说明传输是按值传输,否则 log 的结果应该是 Greg
console.log(obj.name); // 'Nicholas'
确定类型
typeof
操作符最适合用来判断一个变量是否为原始类型,它是判断一个变量是否为字符串、数值、布尔值或undefined
的最好方法,如果值是对象或null
,则返回object
typeof
虽然对原始值很有用,但他对引用值的用处不大。一般我们不关心一个值是不是对象,而是想知道他是什么类型的对象,这时,我们可以使用instanceof
操作符result = variable instanceof constructor
- 如果变量是给定引用类型(由其原型链决定)的实例,则
instanceof
操作符返回true
const person = {};
console.log(person instanceof Object); // true
const colors = [];
console.log(colors instanceof Array); // true
const pattern = /\d/;
console.log(pattern instanceof RegExp); // true
- 当然,所有引用值都是
Object
的实例,因此通过instanceof
操作符检测任何引用值和 Object 构造函数都会返回true
,相对的,如果用于检测原始值,则始终返回false
,因为原始值不是对象 - 注意:
typeof
在用于检测函数时会返回function
【ECMA-262 规定,任何实现内部call
方法的对象都应该返回function
】,当在 Safari(直到Safari5) 和 Chrome(直到Chrome7) 检测正则表达式时,会返回function
,其原因是因为上述浏览器正则实现了这个方法