Data Property
1. Attributes
数据属性指对象中那些有自己的值的属性(与下文Accessor Property相对),共有四个Attributes :
[[Configurable]] - “可配置的”,默认为True
-
- 标识该属性(Property)是否可以被delete删除
- 标识该属性(Property)的特性(attributes)是否可以被修改。具体而言,如果将configurable设置为false:
configurable不可以修改
enumerable不可被修改
writable只能从true改为false,不可从false改为true - 标识是否可以将该属性(Property)从Data Property改为Accessor Property
[[Enumerable]] - “可枚举的”,默认为True
标识该属性(Property)是否可以在for-in循环中被枚举到。
[[Writable]] - “可写的”,默认为True
标识该属性(Property)的值是否可以被修改
[[Value]] - 值,默认为undefined
在对象中定义一个属性并赋值时,如:
let Person = { name: "Nicholas" };
该语句为Person对象生成了名为“name”的Data Property,并将name的[[Configurable]],[[Enumerable]],[[Writable]]特性赋值为true,将[[Value]]赋值为"Nicholas"。
2. 修改属性(Property)的特性(Attribute)- Object.defineProperty()
参数:属性隶属的对象,属性名称,和一个描述各个特性(attribute)的object
特性:
- 使用该函数定义的属性,各attribute默认为false(直接在对象中添加的时,attribute默认为true)
- configurable属性一旦设置为false,不可再逆转。
以下语句为person添加了一个完全不可改动的属性Id,和一个只能修改Value的属性Name:
let Person = {}; Object.defineProperty(Person, "name", { writable: true, configurable: false, value: "Jack" }); console.log(Person.name); //Jack Person.name = "JackChanged"; console.log(Person.name); //JackChanged Object.defineProperty(Person, "Id", { writable: false, configurable: false, value:"10101" }); console.log(Person.Id); //10101 Person.Id = "20202"; //throw error in strict mode console.log(Person.Id); //10101 Object.defineProperty(Person, "Id", { writable: true //throw an error });
(也就是说,使用defineProperty定义的属性默认是完全不可修改的,因为writable和configurable都默认为false)
Accessor Property
存取属性没有value值,取而代之的是两个函数get()和set()。当存取属性的值被读取时,get()被调用,并返回一个值;当属性的值被写入时,set()被调用,并决定要对传入的值进行什么操作。简言之,该属性没有自己的数据值,是一个用来读取其他data property的媒介。
存取属性同样有四个Attribute:
[[Configurable]] - “可配置的”,同data property
[[Enumerable]] - “可枚举的”,同data property
[[Get]] - 读取属性值时被调用的函数,需要返回一个值。默认为undefined
[[Set]] - 写入属性值时被调用的函数,需要传入一个值。默认为undefined
利用存取属性可以实现“伪private”属性,对一些我们不希望在对象外部被直接用“=”读写的属性,通过存取属性的getter和setter函数读写。
以下语句为book添加了year属性,用于读写“类private”属性 year_ 的值,同时会自动更新public属性edition的值:
//定义对象book,year_为“类private”属性 let book = { year_ : 2019, edition: 1 } //添加存取属性year用于读写year_的值,同时更新edition Object.defineProperty(book, "year", { get(){ return this.year_; }, set(newVal){ this.year_ = newVal; this.edition += newVal - 2019; } }) book.year = 2020; console.log(book.edition); //2
定义多个属性 - Object.defineProperties()
let book = {}; Object.defineProperty(book, { year_:{ value: 2019 }, edition:{ value: 1 }, year:{ get(){ return this.year_; }, set(newVal){ if(newVal > 2019){ this.year_ = newVal; this.edition += newVal - 2019 } } } })
读取Properties
Object.getOwnPropertyDescriptor()
let privateYearDescriptor = Object.getOwnPropertyDescriptor(book, "year_"); console.log(privateYearDescriptor.value);//2019 console.log(privateYearDescriptor.configurable);//false(通过defineProperty定义) console.log(privateYearDescriptor.get); //undefined let accessorYearDescriptor = Object.getOwnPropertyDescriptor(book, "year"); console.log(accessorYearDescriptor.value);//undefined console.log(accessorYearDescriptor.configurable);//false(通过defineProperty定义) console.log(typeof accessorYearDescriptor.get); //function
Object.getOwnPropertyDescriptors()
let bookDescriptors = Object.getOwnPropertyDescriptors(book);
console.log(bookDescriptors);
console.log(bookDescriptors.year_);
console.log(bookDescriptors.year_.value);
简写
用变量来初始化对象的属性值时,如果对象的属性名和变量名一样,可以直接写变量名。以下两种代码是相同的效果:
let name = 'Matt'; let person = { name: name }; console.log(person); //{name: "Matt"}
和
let name = 'Matt'; let person = { name }; console.log(person); //{name: "Matt"}
当属性名在对象中被返回时也同样可以使用:
function makePerson(name){ return { name }; } let person = makePerson("Matt"); console.log(person.name); //'Matt"
表达式作属性名
将属性用[]方括号括起,则括号内的内容会被视作表达式,而不是字符串。
const nameKey = 'name'; const ageKey = 'age'; const jobKey = 'job'; let person = { [nameKey]: "Matt", [ageKey]: 22, [jobKey]: 'Software Engineer' } console.log(person);//{name: 'Matt', age: 22, job: 'Software Engineer'}
可以使用函数动态生成属性名:
const nameKey = 'name'; const ageKey = 'age'; const jobKey = 'job'; let uniqueToken = 0; function getUniqueKey(key){ return `${key}_${uniqueToken++}`; } let person = { [getUniqueKey(nameKey)]: "Matt", [getUniqueKey(ageKey)]: 22, [getUniqueKey(jobKey)]: 'Software Engineer' } console.log(person);//{name_0: "Matt", age_1: 22, job_2: "Software Engineer"}
注:如果用于计算属性名的表达式报错,对象的生成会终止,但生成过程中已经进行的计算不会撤回。
对象的解构
let person = { name: "Matt", age: 27, } let {name} = person; let {age: personAge, name: personName} = person; console.log(name); //Matt console.log(personName, personAge); //Matt 27