# JavaScript教程 - 7 对象
在前面讲到 JS 中的数据类型主要分为两类:原始数据类型(原始类型) 和 引用数据类型(对象类型)。
原始数据类型有:
- 数值(number)
- 字符串(string)
- 布尔值(boolean)
- 大整数(bigint)
- 空值(null)
- 未定义(undefined)
- 符号(symbol)
但是这些数据类型只能表示简单的数据,如果想表示一个人的信息,例如包括姓名、性别、年龄等,用基本数据类型只能定义三个变量表示,没办法从整体通过一个数据类型来表示。
现在来学习一个新的数据类型,对象类型,对象数据类型可以由多个各种数据类型组合而成,甚至其中还可以包含对象类型。
# 7.1 对象的基本操作
下面就来创建对象,添加属性、访问对象中的属性,以及修改属性、删除属性。
# 1 创建对象
举个栗子:
// 创建一个对象,赋值给person变量
let person = new Object();
// 给对象添加属性
person.name = '逗比';
person.gender = '男'
person.age = 18;
console.log(person); // 打印person变量
2
3
4
5
6
7
8
9
- 使用
new Object()
创建一个对象; - 对象中添加的一个个选项叫属性,我们可以随意添加属性,并赋值,格式为:
对象.属性=值
;上面给person
对象添加了三个属性,并分别赋值。
执行结果:
{name: '逗比', gender: '男', age: 18}
创建对象还有简单的写法:
let person = Object(); // 可以省略new
console.log(person); // {}
2
- 打印没有添加属性的对象,结果为
{}
。
创建对象还可以通过字面量 {}
的方式来创建,举个栗子:
// 创建一个对象,赋值给person变量
let person = {};
person.name = '逗比';
console.log(person); // 逗比
2
3
4
5
6
这种方式可以在创建对象的时候,直接添加属性:
// 创建一个对象,赋值给person变量
let person = { name: '逗比', gender: '男', age: 18 };
console.log(person); // {name: '逗比', gender: '男', age: 18}
2
3
4
# 2 访问属性
访问对象中的属性,直接通过 对象.属性
访问:
// 创建一个对象,赋值给person变量
let person = new Object();
// 给对象添加属性
person.name = '逗比';
person.gender = '男'
person.age = 18;
console.log(person.name); // 逗比
console.log(person.gender); // 男
console.log(person.age); // 18
2
3
4
5
6
7
8
9
10
11
# 3 修改属性值
修改属性值和添加属性是一样的:
// 创建一个对象,赋值给person变量
let person = new Object();
person.name = '逗比'; // 没有该属性的时候就是添加属性
person.name = '牛比'; // 修改属性值
console.log(person); // {name: '牛比'}
2
3
4
5
6
7
# 4 删除属性
可以使用 delete 删除对象中的属性。
举个栗子:
// 创建一个对象,赋值给person变量
let person = new Object();
person.name = "逗比"; // 添加属性
delete person.name;
console.log(person); // {}
console.log(person.name); // undefined
2
3
4
5
6
7
8
delete person.name;
删除 person 对象中的 name 属性,删除属性后,再次访问,结果为undefined
。
# 7.2 对象属性
# 1 属性名
对象中的属性名通常就是一个字符串,所以属性名可以是任何值,包括特殊字符,但是如果属性名太特别,不能直接通过 对象.属性
的格式进行赋值和访问,需要使用 对象['属性']
这样的格式。
举个栗子:
let person = {};
// 给对象添加属性
person.name = "逗比";
person["gender"] = "男";
person["年龄"] = 18;
person["^*(&%)"] = "乱七八糟";
console.log(person); // {name: '逗比', gender: '男', 年龄: 18, ^*(&%): '乱七八糟'}
// 获取属性
console.log(person.name); // 逗比
console.log(person['name']); // 逗比
console.log(person['年龄']); // 18
console.log(person['^*(&%)']); // 乱七八糟
2
3
4
5
6
7
8
9
10
11
12
13
14
- 上面通过
person['属性']
可以给 person 对象添加属性。 - 虽然属性名没有限制,但是强烈建议属性名和标识符一样,遵守命令规则,别浪!
通过 对象['属性']
这样的方式操作属性,要比 对象.属性
更为灵活,可以完成 对象.属性
无法实现的功能。
举个栗子:
let person = {};
let str = "name";
person[str] = "逗比"; // 等价于person['name'] = "逗比";
// 获取属性
console.log(person.name); // 逗比
2
3
4
5
6
7
对象['属性']
中的属性可以传递一个变量,这个是对象.属性
无法实现的。你不能person.str='逗比'
,这个添加的是str
属性。
属性名除了可以使用字符串,还可以使用符号来作为属性名。
举个栗子:
let person = {};
// 定义符号
let symbol1 = Symbol();
let symbol2 = Symbol();
// 给对象添加属性
person[symbol1] = '通过symbol1添加的属性';
console.log(person[symbol1]); // 打印: 通过symbol1添加的属性
console.log(person[symbol2]); // undefined
2
3
4
5
6
7
8
9
10
11
- 在上面通过符号作为对象的属性,需要注意,通过哪个符号添加的属性,只能通过哪个符号获取。
使用符号作为属性名不太常用,只有特殊的场景下才会使用,一般在不希望属性被外界访问的时候用到。
在使用字面量进行对象初始化的时候,也可以使用特殊字符和符号属性,写法如下:
// 创建一个符号
let mySymbol = Symbol();
// 创建一个对象,赋值给person变量
let person = {
name: '逗比',
gender: '男',
['age']: 18,
[mySymbol]: '符号通过symbol1添加的属性'
};
console.log(person); // {name: '逗比', gender: '男', age: 18, Symbol(): '符号通过symbol1添加的属性'}
2
3
4
5
6
7
8
9
10
11
12
# 2 属性是否存在
如何判断一个对象中是否有指定的属性呢?可以通过 in
关键字来判断。
举个栗子:
let person = {};
person['name'] = "逗比";
// 查看是否有指定的属性
console.log('name' in person); // true,有name属性
console.log('age' in person); // false,没有age属性
2
3
4
5
6
7
- 使用
in
关键字来判断,有就返回 true,没有返回 false。
# 3 对象类型的属性
对象的属性还可以是对象,属性是可以相互嵌套的,可以
举个栗子:
let person = {};
person.name = "逗比";
person.birthday = {}; // 生日定义为对象类型
person.birthday.year = 2025; // 通过多层访问属性,就不停的'点'就行了
person.birthday.month = 12;
person.birthday.day = 10;
// 访问属性
console.log(person.name); // 逗比
console.log(person.birthday); // {year: 2025, month: 12, day: 10}
console.log(person.birthday.year); // 2025
console.log(person.birthday.month); // 12
2
3
4
5
6
7
8
9
10
11
12
13
- 对象属性多层嵌套其实就是获取到属性后,继续按照访问对象的访问方式继续操作就可以了。
- 如果有更多层,也是按照这样的讨论继续访问就可以了。
其实上面的初始化太复杂了,像下面这些写就方便很多:
let person = {
name: '逗比',
birthday: {
year: 2025,
month: 12,
day: 10
}
};
2
3
4
5
6
7
8
# 4 枚举属性
有时候别人传递给我们一个对象,我们不知道其中有什么属性,那么可以遍历一下对象中的属性,看一下有哪些属性。
方法如下:
// 创建一个符号
let mySymbol = Symbol();
// 创建一个对象,赋值给person变量
let person = {
name: "逗比",
gender: "男",
["age"]: 18,
[mySymbol]: "符号通过symbol1添加的属性",
};
// 遍历对象中的属性
for (let propName in person) {
console.log("属性:", propName, ", 值:", person[propName]);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 在上面的代码中使用
for-in
遍历对象中的属性,对象有几个属性,for 循环就会执行几次;但是并不是所有的属性都会被枚举,使用符号创建的属性,是无法被枚举的。 - 每一次执行,就会将属性名赋值给 propName(这个名称是自定义的),获取到属性后,通过
person[propName]
获取到属性的值。
执行结果:
属性: name , 值:逗比 属性: gender , 值:男 属性: age , 值:18
# 7.3 变量内存结构
# 1 基本数据类型变量
先看一下下面的代码:
let a = 2;
let b = a;
a = 5;
console.log(b); // 2
2
3
4
- 在上面的代码中,修改 a 的值,并不会影响 b 的值。
let a = 2;
会在栈中为 a 分配内存空间,并赋值为 2;let b = a;
会在栈中位 b 分配内存空间,并复制 a 的值给 b;a = 5;
会修改 a 的内存空间的值为 5,并不会修改 b 的值。
如下图:
在 JS 中基本类型(undefined
、null
、boolean
、number
、 symbol
)的变量在内存中是直接存储在栈中的,存储的是值本身。但是字符串比较特别,一般是存储在堆中的,因为字符串可能比较长,会占用比较大的内存空间。
# 2 引用类型变量
在看一下下面的代码:
let a = {name:'doubi', age:18}
let b = a;
a.name = 'niubi';
console.log(b.name); // niubi
2
3
4
5
- 上面的代码修改了 a 对象的值,为什么 b 对象的值也发生了变化呢?
这是因为对象类型的数据是在堆中分配的。
内存示意如下:
let a = {name:'doubi', age:18}
,在栈中和堆中分配内存空间,对象是在堆中存储的,栈中只是存储的堆中的对象的内存地址,字符串不是直接存储的,也是存储字符串的指针。
let b = a;
,将 a 赋值给 b 时,实际是将 a 存储的地址复制给 b,这样 b 也存储的是对象的地址,指向的也是同一个对象。
当执行 a.name = 'niubi';
时,修改的是堆中对象的信息:
所以此时修改 a ,通过 b 来访问,是一样的。
再举个例子:
let a = {name:'doubi', age:18}
let b = a;
b = {name:'shabi', age:20}
b.name = 'erbi';
console.log(a.name); // doubi
2
3
4
5
6
内存示意如下:
let a = {name:'doubi', age:18}
,栈和堆都分配内存空间,栈指向堆中的内存空间:
let b = a;
,a 存储的地址赋值给 b,a和b指向堆中相同的空间:
b = {name:'shabi', age:20}
,在堆中新建了一个对象,开辟了新的内存空间,将 b 指向新的内存空间。
b.name = 'erbi';
,修改 b 指向的堆中的内存空间:
引用类型的数据是存储在堆中的,而栈中保存的是指向堆中数据的引用(即内存地址或指针)。堆中数据的大小不固定,且堆内存的分配和回收比栈慢。堆内存由垃圾回收机制(GC)管理。
虽然字符串是基本数据类型,它们通常存储在堆中,栈中存储的是指向堆中字符串的引用。
# 3 const修饰变量
前面说 const 用来定义常量,也就是使用 const 声明的变量是无法修改的。
const a = 123;
a = 456; // 错误,是无法修改a的值的。
2
但是需要注意,引用类型的变量是可以修改对象的值的,只是不能指向新的对象。
什么意思?举个栗子:
const a = {name:'Doubi', age: 13};
a = {}; // 错误,无法修改a的值,a的值是堆中的地址,所以修改a指向的堆中的对象
2
但是可以修改 a 指向的对象的值:
const a = { name: "Doubi", age: 13 };
a.name = "niubi"; // 这是可以的
console.log(a); //{name: 'niubi', age: 13}
2
3