JavaScript引用数据类型(对象类型)和原始(基本)数据类型特点比较
为讲解JavaScript引用数据类型(对象类型)和原始(基本)数据类型特点比较,需要先回顾JavaScript数据类型有哪些?
一)原始(primitive:原始、基本)数据类型,也称为原始值(primitive value),包括:
1.布尔值(Boolean),其字面值只有两个,分别是true和false。
2.null,Null类型只有一个唯一的字面值null, null 值的特殊关键字。JavaScript 是大小写敏感的,因此 null 与 Null、NULL或变体完全不同。
3.undefined,Undefined类型只有一个唯一的字面值undefined,undefined 表示变量未赋值时的属性。而undefined是JavaScript中的一个全局变量,即挂载在window对象上的一个变量,并不是关键字。
4.数字(Number),整数或浮点数,例如: 42 或者 3.14159。
5.任意精度的整数(BigInt),可以安全地存储和操作大整数。ES10(也称为ES2019)引入了一个新的原始数据类型BigInt,用于表示任意精度的整数。在之前的版本中,JavaScript只能表示有限范围的整数,超出范围的整数会被转换为特殊的Infinity值。
6.字符串(String),字符串是一串表示文本值的字符序列,例如:"Howdy"。
7.代表(Symbol,在 ECMAScript 6 中新添加的类型)。一种实例是唯一且不可改变的数据类型。
二)对象(object)类型,是一种非原始(non-primitive)数据类型,也称为引用值(reference value),用于更复杂的数据结构。JavaScript中的对象类型包括:
1、普通对象(Plain objects):是指用户自定义的对象类型。这些对象通常是通过使用构造函数或类来定义的。在JavaScript中,有多种方式可以创建对象。在ES6及以后的版本中,JavaScript引入了类(class),这是一种创建和定义对象的新方法。
2、数组(Arrays):例如let arr = [1, 2, 3]; 数组对象有一些特殊的属性和方法,如length、push、pop等。
3、函数(Functions):函数也是对象,它们可以有属性和方法。例如:
function sayHello() {
console.log('Hello');
}
sayHello.myProperty = 'This is a property of the function.';
4、日期(Dates):例如let date = new Date();
5、正则表达式(RegExp,Regular expressions):例如let regex = /ab+c/;
6、其他内置对象,如Math,Set,Map等。
JavaScript原始(基本)数据类型和引用数据类型(对象类型)特点差别:
☆原始(基本)数据类型只能表示一个简单的值,不能添加、修改和删除属性。引用数据类型可以包含多个键值对,形成复杂的数据结构,可以动态地添加、修改和删除属性。
原始(基本)数据类型只能表示一个简单的值,比如字符串、数字、布尔值等。它们不能添加、修改和删除属性。
例如:
let num = 5;
num = 10; // 修改变量的值
console.log(num); // 10
let str = "Hello";
str.length = 5; // 添加属性
console.log(str.length); // undefined
在这个例子中,我们可以修改原始数据类型的变量的值,但不能添加属性。即使我们尝试添加属性,也不会对原始数据类型的值产生影响。因为原始数据类型的值是不可变的,它们没有属性可以修改或删除。
引用数据类型可以包含多个键值对,形成复杂的数据结构,比如对象和数组。你可以动态地添加、修改和删除属性或元素。
例如:
let person = {
name: "John",
age: 30,
hobbies: ["reading", "coding"]
};
person.name = "Jane"; // 修改属性值
person.gender = "female"; // 添加新属性
delete person.age; // 删除属性
console.log(person); // { name: "Jane", hobbies: ["reading", "coding"], gender: "female" }
在这个例子中,我们有一个person对象,它包含了name、age和hobbies等属性。我们可以通过修改属性值、添加新属性或删除属性来动态地改变对象的结构。
☆ 原始(基本)数据类型的值是不可变的,引用数据类型的值是可变的。
原始(基本)数据类型的值是不可变的,意味着你不能直接修改它们的值。当你尝试修改原始数据类型的值时,实际上是创建了一个新的值,并将其赋给变量。
例如:
let num = 5;
num = 10;
console.log(num); // 10
在这个例子中,我们将num的值从5修改为10,但实际上是创建了一个新的值10,并将其赋给num变量。
引用数据类型的值是可变的,意味着你可以修改它们的属性或元素。这是因为引用数据类型的值实际上是存储在内存中的对象,变量只是指向这个对象的引用。当你修改引用数据类型的值时,实际上是在修改对象本身,而不是修改变量的值。
例如:
let obj = { name: "John" };
obj.name = "Jane";
console.log(obj); // { name: "Jane" }
在这个例子中,我们修改了obj对象的name属性的值,这是因为obj是一个引用数据类型,它指向的对象是可变的。
这种不可变性的特性使得原始数据类型在处理简单的数据时更加方便和可靠,而引用数据类型则更适合处理复杂的数据结构和对象。
☆ 变量赋值方面,原始(基本)数据类型的变量赋值是值的复制,而引用数据类型(对象类型)的变量赋值是引用的复制。
当执行到变量赋值语句时,JavaScript引擎会将赋值操作的右侧表达式计算出一个值,并将该值赋给变量。赋值操作可以使用赋值运算符 =,也可以使用其他赋值运算符(如 +=、-= 等)。
变量赋值是一个按值传递的过程。对于基本数据类型(如数字、字符串、布尔值等),赋操作会将值复制给变量。而对于引用数据类型(如对象、数组等),赋值操作会将引用(指向对象的内存地址)复制给变量,而不是复制对象本身。这意味着,当你修改一个引用类型的变量时,实际上是修改了引用所指向的对象。
对于原始类型,变量赋值是通过将一个值复制给另一个变量来完成的。这意味着当你将一个原始数据类型的变量赋值给另一个变量时,实际上是将原始值复制到了新的变量中。这两个变量是完全独立的,修改其中一个变量的值不会影响另一个变量。
例如:
let a = 5;
let b = a;
b = 10;
console.log(a); // 输出 5
console.log(b); // 输出 10
当一个原始变量把值赋给另一个原始变量时,只是把栈中的内容复制给另一个原始变量,此时这两个变量互不影响——其实在内存中是两个地址,是互相独立的存在,当一个变量值改变时,另一个变量不会因此而发生任何变化。图解如下:
对于引用数据类型,变量赋值是通过将引用复制给另一个变量来完成的。引用是指向存储在内存中的对象的地址。当你将一个引用数据类型的变量赋值给另一个变量时,实际上是将引用复制到了新的变量中。这两个变量指向同一个对象,修改其中一个变量的属性会影响另一个变量。
例如:
let obj1 = { name: 'Alice' };
let obj2 = obj1;
obj2.name = 'Bob';
console.log(obj1.name); // 输出 'Bob'
console.log(obj2.name); // 输出 'Bob'
需要注意的是,当你修改引用数据类型的属性时,实际上是修改了对象本身,而不是变量。因此,所有指向该对象的变量都会反映出这个修改。图解如下:
☆ 参数传递方面,在 JavaScript中,参数传递方式是按值传递——传递的是副本。具体说来:1)当将一个原始(基本)数据类型(如数字、字符串、布尔值等)作为参数传递给函数时,实际上是将该值的一个副本传递给函数——将实参值复制给形参,实参和形参相互独立互不干扰。函数内部对该副本的修改不会影响到原始的值。2)当将一个引用数据类型(对象类型)(如对象、数组等)作为参数传递给函数时,传递的是该对象的引用(地址)的副本——将实参引用的地址值复制给形参,形参和实参指向同一个对象的地址,改变形参所指向的对象的属性将影响实参所指向的对象。需要注意,在引用类型的参数传递中,并不会改变形参的值(即引用的地址),而是通过形参改变它所指向的对象的属性。
当传递原始数据类型时,实际上是将原始值的副本传递给了函数或其他变量。这意味着函数或其他变量操作的是原始值的副本,而不是原始值本身。
这种传递方式使得函数内部对原始值的修改不会影响到函数外部的变量,因为函数内部操作的是原始值的副本,而不是原始值本身。
例如:
function changeValue(num) {
num = 10;
}
let x = 5;
changeValue(x);
console.log(x); // 输出 5
当传递引用数据类型时,实际上是将引用的地址传递给了函数。这意味着函数可以通过引用来访问和修改原始对象。引用是指向存储在内存中的对象的地址,所以传递引用时,传递的是指向对象的指针,而不是对象本身的实际值。
这种传递方式使得我们可以在函数内部修改原始对象,并且这些修改会在函数外部可见。因为函数和外部变量都指向同一个对象,所以对对象的修改会影响到所有引用该对象的变量。
例如:
function changeName(obj) {
obj.name = 'Bob';
}
let person = { name: 'Alice' };
changeName(person);
console.log(person.name); // 输出 'Bob'
总结之,传递引用时,传递的是引用的地址,而不是实际的值。传递原始数据类型时,传递的是实际的值的副本。这种差异导致了在函数内部对引用数据类型的修改会影响到函数外部的变量,而对原始数据类型的修改不会影响到函数外部的变量。
☆ 数据比较方面,原始(基本)数据类型的比较是对值的比较,引用数据类型的比较是对内存地址的比较。
对于原始(基本)数据类型的比较,是对值的比较。例如:
let num1 = 5;
let num2 = 5;
console.log(num1 === num2); // true
因为num1和num2的值相同,所以比较结果为true。
引用数据类型的比较是对内存地址的比较,而不是对值的比较。这意味着当你比较两个引用数据类型的变量时,实际上是在比较它们是否指向同一个内存地址。例如:
let obj1 = { name: "John" };
let obj2 = { name: "John" };
console.log(obj1 === obj2); // false
尽管obj1和obj2的属性值相同,但它们指向的是不同的内存地址,所以比较结果为false。
OK!