4.1 对象

构造对象的方法

1
2
let user = new Object(); // “构造函数” 的语法
let user = {}; // “字面量” 的语法
文本和属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let user = {
name: "Randolfluo",
"Nian lin": 20
};

alert(user.name);

user.country = "China" // 添加属性
alert(user.country);

delete user.country; // 删除属性
alert(user.country); //undefined

user["Nian lin"] = 18; //有空格或者数字开头的属性,需要用中括号
//这里的Nian lin可以由程序运行时计算得出
方括号
1
2
3
4
5
6
7
8
//因此我们实现返回用户需要的属性值
let user1 = {
name: "Randolfluo",
age: 20
}
let key = prompt("你想知道什么?", "name");
alert(user1[key]);
//当然,.点符号不能以类似的方式使用
计算属性

我们还可以在运行中输入属性名

1
2
3
4
5
6
7
8
9
10
11
12
let attr = prompt("Please input a attribute","name");

let human = {
attr : "Defalut"
};

let value = prompt("Please input a value","Randolfluo");

human[attr]="value: " + value;
alert(human[attr]); //value: Randolfluo
console.log(attr); //name

属性名简写

如果我们需要通过一个函数创建大量对象,可以这样写

1
2
3
4
5
6
7
8
9
10
function makeUser(name,age){
return {
name, // 与 name: name 相同
age, // 与 age: age 相同
};
}

user1 = makeUser("John", 30);
user2 = makeUser("Pete", 35);
user3 = makeUser("Randolfluo",20);
属性存在性测试,in操作符

我们想访问一个属性的值,那么只有该属性存在我们的访问才有意义。

可以通过in来判断该属性是否存在.

1
2
3
4
5
6
7
let user = {
"name": "Randolfluo",
"age": 20
};

alert( "name" in user ); // true
alert( "country" in user ); //false
“for..in”循环

可以通过“for..in”循环遍历对象

1
2
3
4
5
6
7
8
9
let user = {
"name": "Randolfluo",
"age": 20
};

for (let key in user){
alert(key);
alert(user[key]);
}
像对象一样排序

获取属性的顺序为:整数属性会被进行排序,其他属性则按照创建的顺序显示。

4.2 对象引用和复制

引用

当一个对象变量被复制 —— 引用被复制,而该对象自身并没有被复制。

1
2
3
4
5
let user = { name: "Randolfluo" };
let admin = user; // 复制引用
admin.name = "God";
alert(user.name); // God
alert(admin.name); // God

使用 const 声明的对象也是可以被修改的。

1
2
3
4
5
6
7
const user = {
name: "John"
};
//user = ...作为整体时才会报错。
user.name = "Pete"; // (*)

alert(user.name); // Pete使用 const 声明的对象也是可以被修改的
复制

使用for…in复制

1
2
3
for (let key in user) {
clone[key] = user[key];
}

使用assign复制

1
let clone = Object.assign({}, user);

assign还可以逐个添加不同对象属性

1
2
3
let permissions1 = {...};
let permissions2 = {...};
Object.assign(user, permissions1, permissions2);

垃圾回收

垃圾回收 (javascript.info)

4.4 对象方法,”this”

方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义一个包含一个方法的对象
let user = {
sayHi3 (){
alert("Hello from sayHi3");
}
};
// 定义一个独立的函数,并将该函数加入对象中
function sayHi1() {
alert("Hello from sayHi1");
}
user.sayHi1 = sayHi1;

// 直接定义并添加一个匿名函数作为对象的属性
user.sayHi2 = function() {
alert("Hello from sayHi2");
};

user.sayHi1();
user.sayHi2();
user.sayHi3();
方法中的this

有时候我们需要修改对象的属性,当对象名被修改后我们就无法使用属性.变量,来访问他,我们可以使用this解决这一问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let user = {
name: "Randolfluo",
age: 18,

};

function print(){
alert( this.name);
}

//不要忘记这样是使user1指向user所指向的对象,因此输出一样
let user1 = user;
user1.name = "Dick";
user.print = print;
user.print(); //Dick
user1.print = print;
user1.print(); //Dick

//我们可以通过this使得不同对象调用同一函数
let user2 = new User("Jack", 25);
user2.name = "Jack";
user.print = print; //Dick
user.print();
user2.print = print; //Dick
user2.print();

4.5 构造器和操作符 “new”

构造函数

  1. 它们的命名以大写字母开头。
  2. 它们只能由 "new" 操作符来执行。
  3. 用于实现可重用的对象创建代码。
  4. 在开始时创建了空的 this,并在最后返回填充了值的 this
1
2
3
4
5
6
7
8
9
10
11
12
13
function User(name, age){
this.name = name;
this.age = age;
this.print = function(){
alert(this.name + " " + this.age);
}
};

let Ran = new User("Randolfluo", 20);
let Dick = new User("Dick", 18);

Ran.print(); //Randolfluo 20
Dick.print(); //Dick 18

4.6 可选链 “?.”

  • 用于解决访问“不存在的属性”的报错的问题
  • ?. 链使我们能够安全地访问嵌套属性。
  • 如果 ?. 左边部分不存在,就会立即停止运算(“短路效应”)。
访问可能不存在的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let user = {
name : "Randolfluo",
age : 20
};

alert(user.address.name); //error

/*
注意usr.address只会显示undefined,
即我们访问undefined对象的元素才会报错
*/

alert(user?.address?.name); //undefined
//短路效应,不会访问到name
?.()和?.[]

可以使用 ?. 来安全地读取或删除,但不能写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let user1 = {
"first name" : "Randolfluo",
sayHello(){
alert("Hello");
}
};

let user2 = {};

user1.sayHello?.(); //输出Hello
user2.sayHello?.(); //无输出

alert(user1?.["first name"]); //输出Randolfluo
alert(user2?.["first name"]); //输出undefined

//实现安全删除
delete user1?.sayHello;
//使用 delete 操作符删除对象的方法时,不能加上括号,因为加上括号会调用该方法,而不是删除它。我们应该只删除方法的引用,而不是调用方法。
delete user1?.["first name"];
user1.sayHello?.(); //无输出
alert(user1?.["first name"]); //undefined

//不能写入
user1?.["first name"] = "Randolfluo";
//Error Invalid left-hand side in assignment

4.7 symbol

什么是symbol
  • symbol是全局唯一的,但是它们的可访问性取决于该模块是否拥有该实例,如(另一个js文件如果没有从此js文件导入id1和id2,则无法访问这个symbol实例。
  • symbol 不会被自动转换为字符串,我们可以使用toString()description来输出symbol的描述。
1
2
3
4
5
6
7
8
let id1 = Symbol("id");
let id2 = Symbol("id");

alert(id1 == id2); // false

//输出symbol的描述
alert(id1.toString()); //symbol(id)
alert(id2.description); //id
通过symbol隐藏属性
  • 可以看到我们在没有持有symbol的情况无法修改user[id],实现了属性的隐藏。

  • symbol 属性不参与 for..in 循环。

1
2
3
4
5
6
7
8
9
10
let id = Symbol("id");

let User = {
[id] : 114514, //需要变量 id 的值作为键,而不是字符串 “id”
};

User.id = 12345;

alert(User[id]); //114514
alert(User.id); //12345
全局 symbol 注册表
  • 我们可以在其中创建 symbol 并在稍后访问它们,它可以确保每次访问相同名字的 symbol 时,返回的都是相同的 symbol。
  • Symbol.for(key) 按描述返回一个 symbol。
  • Symbol.keyFor(sym),通过全局 symbol 返回一个名字

4.8 对象 —— 原始值转换

hint
  • 在JavaScript中,类型转换的“hint”通常指的是在使用typeofinstanceofin等操作符时,或者在进行比较操作时,JavaScript引擎是如何解释操作数的。
Symbol.toPrimitive

如果 Symbol.toPrimitive 方法存在,则它会被用于所有 hint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let user = {
name: "John",
money: 1000,

[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};

// 转换演示:
alert(user); // hint: string -> {name: "John"}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500
toString和 valueOf

如果没有 Symbol.toPrimitive,那么 JavaScript 将尝试寻找 toStringvalueOf 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let user = {
name: "John",
money: 1000,

// 对于 hint="string"
toString() {
return `{name: "${this.name}"}`;
},

// 对于 hint="number" 或 "default"
valueOf() {
return this.money;
}

};

alert(user); // toString -> {name: "John"}
alert(+user); // valueOf -> 1000
alert(user + 500); // valueOf -> 1500