table of contents

1对象为何而生

如果要我总结一下学习编程以来,我遇到了那些瓶颈,那么面向对象一定是第一个毫不犹豫浮现在脑海里的。尽管我现在已经对于面向对象有了一些掌握,但是当初那种似懂非懂的痛苦,依然历历在目。

为了帮助大家能够更加直观的学习和了解面向对象,我会尽量使用简单易懂的方式在展示面向对象的相关知识。并且精心准备了一些实用的例子帮助大家更快的体会到面向对象的真谛。

2对象为何而生

应用程序是对现实事物的描述。如果我们要使用代码的形式来描述一本书,我们没办法使用一个简单的基础数据类型的结构来描述。只能使用复杂的数据集合来表示。在 JavaScript 中,使用如下的方式:

code.ts
1
const book = {
2
title: 'JavaScript 核心进阶',
3
author: '这波能反杀',
4
publish: '电子工业出版社'
5
}

在 JS 中,这种数据/属性集合,就是对象。

当然,简单的属性值并不能完整的表达一个实例,对于具体实例而言,还应该有一些具体的行为。例如,我想要给书籍添加一个自动阅读的行为,再来一个自毁装置。

code.ts
1
const book = {
2
title: 'JavaScript 核心进阶',
3
author: '这波能反杀',
4
publish: '电子工业出版社',
5
autoRead: function() {},
6
destory: function() {}
7
}

在 ECMAScript 的标准中,对象被定义为无序属性的集合,其属性可以包含基本值,对象,函数。 ** 也就是说,对象是由一系列无序的 key-value 对组成。其中 value 可以是基本数据类型,对象,数组,函数等。这刚好符合了上面的案例。

3创建对象

我们可以通过对象字面量的形式创建一个简单对象。

code.ts
const obj = {}

也可以通过关键字 new 创建一个对象。

code.ts
const obj = new Object()

当我们想要给我们创建的对象添加属性与方法时

code.ts
1
// 可以这样
2
const person = {}
3
person.name = 'TOM'
4
person.age = 20
5
person.getName = function {}
6
7
// 也可以这样
8
const person = {
9
name: 'TOM',
10
age: 20,
11
getName: function() {}
12
}

4访问对象的属性与方法

假如我们有一个简单对象如下:

code.ts
1
const person = {
2
name: 'Jake',
3
age: 21,
4
run: () => {}
5
}

当我们想要访问它的 name 属性时,可以使用如下的方式

code.ts
1
person.name
2
3
// or
4
person['name']
5
6
// or
7
const _name = 'name'
8
person[_name]

因此,当我们想要访问的属性名是一个变量时,可以使用中括号的方式,例如

code.ts
1
['name', 'age'].forEach(item => {
2
console.log(person[item])
3
})

当我们访问一个不存在的属性时,返回结果为 undefined

code.ts
1
// person 对象中不存在 gender 属性
2
person.gender // undefined

5删除

我们可以使用 delete 关键字删除对象中的属性。

code.ts
1
const person = {
2
name: 'TOM'
3
}
4
5
delete person.name
6
console.log(person.name) // undefined

除了使用输出是否为 undefined 来判断属性是否还存在与对象中,还可以使用 in 操作符。

code.ts
1
const person = {
2
name: 'TOM',
3
age: 20
4
}
5
6
delete person.name
7
8
console.log('name' in person) // false
9
console.log('age' in person) // true

在实践中,in 操作符特别有用,例如快速判断当前应用环境是移动端还是 web 端

code.ts
1
// 为 true,就表示当前环境支持 touchstart 事件,就是在移动端
2
'ontouchstart' in document

6属性描述对象

在 ECMAScript 内部,针对对象的每一个属性,都有一个描述对象来表达该属性的状态和行为。该描述对象包含如下属性值:

configurable:表示该属性是否能被 delete 删除,其值为 false 时,描述对象的其他属性值都不能被改变。默认值为 true

enumerable:该属性是否能被枚举。也就是是否能被 for-in 遍历,默认值为 true

writable:该属性值是否能被修改。默认值为 true

value:该属性的具体值。默认为 undefined

get:当我们通过 person.name 访问 name 属性时,get 方法将被调用。该方法可以自定义返回的具体值。默认值为 undefined

set:当我们通过 person.name = 'Jake' 设置 name 值时,set 方法将被调用。该方法可以自定义设置值的具体方式。默认值为 undefined

我们可以通过 Object.defineProperty 来修改属性的描述对象。需要注意的是,不能同时设置 value/writable 与 get/set 的值。

下面我们使用一些具体的实践案例来演示这些属性的具体表现。

configurable

code.ts
1
// 用普通的方式给person对象添加一个name属性,值为TOM
2
const person = {
3
name: 'TOM'
4
}
5
6
// 使用delete删除该属性
7
delete person.name; // 返回true 表示删除成功
8
9
// 通过Object.defineProperty重新添加name属性
10
// 并设置name的属性类型的configurable为false,表示不能再用delete删除
11
Object.defineProperty(person, 'name', {
12
configurable: false,
13
value: 'Jake' // 设置name属性的值
14
})
15
16
// 再次delete,已经不能删除了
17
delete person.name // false
18
19
console.log(person.name) // 值为Jake
20
21
// 试图改变value
22
person.name = "alex";
23
console.log(person.name) // Jake 改变失败,结果仍然为 Jake

enumerable

code.ts
1
const person = {
2
name: 'TOM',
3
age: 20
4
}
5
6
// 使用for-in枚举person的属性
7
const params = [];
8
9
for (var key in person) {
10
params.push(key);
11
}
12
13
// 查看枚举结果
14
console.log(params); // ['name', 'age']
15
16
// 重新设置name属性的类型,让其不可被枚举
17
Object.defineProperty(person, 'name', {
18
enumerable: false
19
})
20
21
const params_ = [];
22
for (var key in person) {
23
params_.push(key)
24
}
25
26
// 再次查看枚举结果
27
console.log(params_); // ['age']

writable

code.ts
1
const person = {
2
name: 'TOM'
3
}
4
5
// 修改name的值
6
person.name = 'Jake';
7
8
// 查看修改结果
9
console.log(person.name); // Jake 修改成功
10
11
// 设置name的值不能被修改
12
Object.defineProperty(person, 'name', {
13
writable: false
14
})
15
16
// 再次试图修改name的值
17
person.name = 'alex';
18
19
console.log(person.name); // Jake 修改失败

value

code.ts
1
const person = {}
2
3
// 添加一个name属性
4
Object.defineProperty(person, 'name', {
5
value: 'TOM'
6
})
7
8
console.log(person.name) // TOM

get/set

code.ts
1
const person = {}
2
3
// 通过get与set自定义访问与设置name属性的方式
4
Object.defineProperty(person, 'name', {
5
get: function () {
6
// 一直返回TOM
7
return 'TOM'
8
},
9
set: function (value) {
10
// 设置name属性时,返回该字符串,value为新值
11
console.log(value + ' in set');
12
}
13
})
14
15
// 第一次访问name,调用get
16
console.log(person.name) // TOM
17
18
// 尝试修改name值,此时set方法被调用
19
person.name = 'alex' // alex in set
20
21
// 第二次访问name,还是调用get
22
console.log(person.name) // TOM

在使用 get/set 时,应该尽量同时自定义 get/set,如果仅仅只设置了 get,我们将无法设置该属性值,如果仅仅只设置了 set,我们也无法读取该属性值。

Object.defineProperty 只能设置一个属性的属性对象值,当我们想要同时设置多个属性的特性值时,需要使用 Object.defineProperties

code.ts
1
const person = {}
2
3
Object.defineProperties(person, {
4
name: {
5
value: 'Jake',
6
configurable: true
7
},
8
age: {
9
get: function () {
10
return this.value || 22
11
},
12
set: function (value) {
13
this.value = value
14
}
15
}
16
})
17
18
person.name // Jake
19
person.age // 22

读取属性的描述对象

属性的描述对象为私有属性,无法直接访问,但是我们可以使用 Object.getOwnPropertyDescriptor 方法读取某一个属性的特性值。

code.ts
1
const person = {}
2
3
Object.defineProperty(person, 'name', {
4
value: 'alex',
5
writable: false,
6
configurable: false
7
})
8
9
var descripter = Object.getOwnPropertyDescriptor(person, 'name');
10
11
console.log(descripter); // 返回结果如下
12
13
descripter = {
14
configurable: false,
15
enumerable: false,
16
value: 'alex',
17
writable: false
18
}
专栏首页
到顶
专栏目录