# 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变量
1
2
3
4
5
6
7
8
9
  • 使用 new Object() 创建一个对象;
  • 对象中添加的一个个选项叫属性,我们可以随意添加属性,并赋值,格式为:对象.属性=值;上面给 person 对象添加了三个属性,并分别赋值。

执行结果:

{name: '逗比', gender: '男', age: 18}


创建对象还有简单的写法:

let person = Object();  // 可以省略new
console.log(person);  // {}
1
2
  • 打印没有添加属性的对象,结果为 {}

创建对象还可以通过字面量 {} 的方式来创建,举个栗子:

// 创建一个对象,赋值给person变量
let person = {};

person.name = '逗比';

console.log(person);  // 逗比
1
2
3
4
5
6

这种方式可以在创建对象的时候,直接添加属性:

// 创建一个对象,赋值给person变量
let person = { name: '逗比', gender: '男', age: 18 };

console.log(person);  // {name: '逗比', gender: '男', age: 18}
1
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
1
2
3
4
5
6
7
8
9
10
11

# 3 修改属性值

修改属性值和添加属性是一样的:

// 创建一个对象,赋值给person变量
let person = new Object();

person.name = '逗比';  // 没有该属性的时候就是添加属性
person.name = '牛比';  // 修改属性值

console.log(person);  // {name: '牛比'}
1
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
1
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['^*(&%)']);  // 乱七八糟
1
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); // 逗比
1
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
1
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添加的属性'}
1
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属性
1
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
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 对象属性多层嵌套其实就是获取到属性后,继续按照访问对象的访问方式继续操作就可以了。
  • 如果有更多层,也是按照这样的讨论继续访问就可以了。

其实上面的初始化太复杂了,像下面这些写就方便很多:

let person = {
  name: '逗比',
  birthday: {
    year: 2025,
    month: 12,
    day: 10
  }
};
1
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]);
}
1
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
1
2
3
4
  • 在上面的代码中,修改 a 的值,并不会影响 b 的值。

  • let a = 2; 会在栈中为 a 分配内存空间,并赋值为 2;
  • let b = a; 会在栈中位 b 分配内存空间,并复制 a 的值给 b;
  • a = 5; 会修改 a 的内存空间的值为 5,并不会修改 b 的值。

如下图:

在 JS 中基本类型(undefinednullbooleannumbersymbol)的变量在内存中是直接存储在栈中的,存储的是值本身。但是字符串比较特别,一般是存储在堆中的,因为字符串可能比较长,会占用比较大的内存空间。


# 2 引用类型变量

在看一下下面的代码:

let a = {name:'doubi', age:18}
let b = a;

a.name = 'niubi';
console.log(b.name);  // niubi
1
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
1
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的值的。
1
2

但是需要注意,引用类型的变量是可以修改对象的值的,只是不能指向新的对象。

什么意思?举个栗子:

const a = {name:'Doubi', age: 13};
a = {};  // 错误,无法修改a的值,a的值是堆中的地址,所以修改a指向的堆中的对象
1
2

但是可以修改 a 指向的对象的值:

const a = { name: "Doubi", age: 13 };
a.name = "niubi";  // 这是可以的
console.log(a);  //{name: 'niubi', age: 13}
1
2
3