文章目录
前言
“js 对象属性的特性”的学习笔记,介绍属性特性的种类和说明,以及如何获取和修改属性特性。
在 js 中,可以把对象想象成一个散列表(类似于 Python 中的字典类型,或者是 Java 中的 HashMap),其中的内容就是一组名/值对,值可以是数据或者函数。
一、创建简单的对象
创建一个简单的对象主要有两种方法,第一种是先创建一个 Object 实例,然后给这个实例添加属性和方法:
let person = new Object();
person.name = "ec50n9";
person.age = 19;
person.sayName = function(){
console.log(this.name);
};
这样就创建好一个名为 person
的对象了,它包含有 name
、age
、sayName
三个属性。其中 sayName
的值是一个函数,可以这样子调用:
person.sayName(); // "ec50n9"
// 👇也可以尝试一下另一种获取属性值的方式
person["sayName"](); // "ec50n9"
还有一种看起来更加直观的创建方法,那就是使用对象字面量来创建对象,下面这种写法等价于上面的,只是看起来更简洁:
let person = {
name: "ec50n9",
age: 19,
sayName(){
console.log(this.name);
}
};
这种写法看起来就和 Python 中的字典很类似了,实际上,如果不考虑 js 的原型链的话,它俩的用法好像确实差别不大🤪。
二、对象属性的特性
对于对象中的属性,除了可以对其赋值以外,每一个属性的内部都包含有一些内部特性来描述属性自身的特征的。
这些内部特性,开发者是无法直接访问的,只能通过 Object.defineProperty()
这个函数来对其进行修改。
不同的对象属性又有不同的内部特性,这里分开来介绍。对象的属性分两种:数据属性和访问器属性。
数据属性
数据属性就是最普通的属性,包含一个保存数据的位置。值都在同一个地方读写,比如这里的 name
就是一个数据属性:
let person = {
name: "ec50n9"
};
这种属性有四个特性描述他们的行为:
1. configurable “可配置的”
默认值:true
作用:
- 是否可以通过 delete 删除并重新定义
- 是否可以修改其特性
- 是否可以把它改为访问器属性。
解释:
如果把 configurable
设置成 false
,就意味着这个属性不能在对象上删除。非严格模式下对这个属性调用 delete
没有效果(返回 false
),严格模式下会抛出错误。
而且,当一个属性被设置为不可配置之后,就不能再设置回可配置的了,也无法修改其他内部特性,因为 Object.defineProperty()
无法对 configurable
值为 false
的属性进行修改。
2. enumerable “可列举的”
默认值:true
作用:是否可以通过 for-in
循环返回。(for-in
循环可以列举对象的属性)
3. writable “可写的”
默认值:true
作用:它的值(也就是👇下面的 value
属性)是否可被修改。
4. value “值”
默认值:undefined
作用:属性实际的值。
在👆上面 person.name
这个示例属性中,它的 value
就为 "ec50n9"
。
📜 修改方法
Object.defineProperty()
需要传入三个参数,第一个是需要修改属性的对象,第二个是属性的名称,第三个是需要修改的特性的集合。
例如我想把 name
属性修改为不可枚举且不可修改:
// 操作前:person.name 的内部属性是这样的↙️
// ✅可修改、✅可列举、✅可写,值为"ec50n9"
Object.defineProperty(person, "name", {
// 把可列举设置为 false
enumerable: false,
// 把可写设置为 false
wirtable: false
});
// 操作过后,person.name 属性的内部特性就会发生改变,变成下面这样↙️
// ✅可修改、❌可列举、❌可写,值为"ec50n9"
person.name = "new name"; // 尝试修改
console.log(person.name); // "ec50n9" 还是原有值,没有被改变,说明不可写。
函数的第三个参数只需传入需要修改的特性和值即可,对于没传入的特性,比如上面没有传入的 configurable
和 value
这两个特性将会保持原有值不变。
如果第二个参数传入的属性名是对象上没有的属性,就会为那个对象添加一个属性。例如:
// person 上面没有 age 属性
Object.defineProperty(person, "age", {
enumerable: true,
wirtable: true,
value: 19
});
console.log(person) // {name: "ec50n9", age: 19}
访问器属性
访问器属性不包含数据值,取而代之的是一个 getter
函数和一个 setter
函数,不过这两个函数都不是必须的。
在读取访问器属性时,会调用 getter
函数,并返回函数的返回值。
在写入访问器属性的时候,会调用 setter
函数,并传入新值,由 setter
函数来决定如何处理这个新值。
访问器属性也有四个特性,其中 configurable
和 enumerable
特性和数据属性是一样的,不同的是另外两个特性被 get
和 set
取代:
1. configurable “可配置的”
默认值:true
作用:👆同数据属性一样。
2. enumerable “可列举的”
默认值:true
作用:👆同数据属性一样。
3. get “获取函数”
默认值:undefined
作用:获取函数,在读取属性时调用。
4. set “设置函数”
默认值:undefined
作用:设置函数,在写入属性时调用。
set
函数可以为空,如果只设置了get
函数而没有设置set
函数的话,则意味着该属性为只读属性,尝试修改属性会被忽略(在严格模式下会抛出错误❌)。
📜 修改方法
同数据属性类似,访问器属性的特性也是通过 Object.defineProperty()
来定义的:
// 定义一个对象,包含伪私有成员 year_ 和公共成员 edition
let book = {
year_: 2017,
edition: 1
};
Object.defineProperty(book, "year", {
get(){
// 直接返回 year_ 的值
return this.year_;
},
set(newValue){
// 对新的值进行判断
if(newValue > 2017){
// 把 year_ 设置为新的值
this.year_ = newValue;
// 同时修改 edition 的值
this.edition += newValue - 2017;
}
}
});
year_
中的下划线常用来表示该属性并不希望在对象方法的外部被访问。实际上,你仍可以在外部像访问普通属性一样访问到year_
,这只是一个约定,没有真的隐藏(防君子不防小人的那种)。如果想真正隐藏内部变量,可以试试闭包。
通常,我们可以在 get
函数里面对即将返回的数据进行一些修饰处理什么的,也可以在 set
函数里面进行对新的值的一些验证,以防设置了一个不符合要求的值等等…
三、定义多个属性
上面用到的 Object.defineProperty()
函数每次就只能定义一个属性的特性,如果想要同时定义多个属性就需要用到 Object.defineProperties()
函数了。
例如,把上面的几个属性同时定义:
let book = {};
Object.defineProperties(book, {
year_: {
value: 2017
},
edition: {
value: 1
},
year: {
get(){
return this.year_;
},
set(newValue){
if(newValue > 2017){
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
}
});
四、读取属性的特性
读取属性可以使用Object.getOwnPropertyDescriptor(对象, 属性名)
函数。拿上面👆定义的 book
对象作示例:
// 读取一个数据属性
Object.getOwnPropertyDescriptor(book, "edition");
// 结果👇
Object {
value: 1,
writable: false,
enumerable: false,
configurable: false
}
// 再试试读取一个访问器属性
Object.getOwnPropertyDescriptor(book, "year");
// 结果👇
Object {
get: get(),
set: set(newValue),
enumerable: false,
configurable: false
}
ECMAScript 2017
新增了 Object.getOwnPropertyDescriptors(对象)
函数,从函数名后面的 s
就可以看出来,它可以获取传入对象上的全部自有属性的特性。实际上,它会在传入对象的每个自有属性上调用 Object.getOwnPropertyDescriptor()
函数,并在一个新对象中返回它们。例如:
Object.getOwnPropertyDescriptors(book);
运行结果如下:
以上。