深入理解Object 之 Properties

Data Property

1. Attributes

  数据属性指对象中那些有自己的值的属性(与下文Accessor Property相对),共有四个Attributes :

[[Configurable]] - “可配置的”,默认为True

    1. 标识该属性(Property)是否可以被delete删除
    2. 标识该属性(Property)的特性(attributes)是否可以被修改。具体而言,如果将configurable设置为false:
      configurable不可以修改
      enumerable不可被修改
      writable只能从true改为false,不可从false改为true
    3. 标识是否可以将该属性(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

特性:

  1. 使用该函数定义的属性,各attribute默认为false(直接在对象中添加的时,attribute默认为true)
  2. 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

 

你可能感兴趣的:(深入理解Object 之 Properties)