javascript进阶知识14-对象的属性

144 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

哈哈哈哈,奶奶的考试周终于过去了,俺又有时间学前端了哈哈。

1、对象的属性

对象的属性分为两种:数据属性和访问器属性。

数据属性

数据属性有4个特性描述他们的行为:

  • Configurable:是否可以被delete删除并重新定义,是否可以修改它的特性,以及是否可以被改为访问器属性, 能否被二次配置。
  • Enumerable:表示属性是否可以被for-in遍历
  • Writable:表示属性的值是否可以被修改
  • Value:属性值 其中的ConfigurableEnumerableWritable默认都为true
let user = {
  name: "ly",
  age: "18",
};

// Object.getOwnPropertyDescriptor(obj,key)可以读取对象里面某个值的属性
// 读取对象的单个值属性(name)
console.log(Object.getOwnPropertyDescriptor(user, "name"));
/*
            {
                value: "ly"        属性值
                writable: true       是否可写
                enumerable: true     是否可以被遍历           
                configurable: true   是否可以被删除获取重新被配置
            }
        */

// 读取队形里面所有值得属性
console.log(JSON.stringify(Object.getOwnPropertyDescriptors(user), null, 2));

访问器属性

访问器属性不包含数据值,相反,他们包含一个获取(getter)函数和一个设置(setter)函数。不过这两个函数不是必须的。

let person = {
    name: 'ly',
    _age: 18,

    get age() {
        return this._age;
    },

    set age(value) {
        if (typeof value === "number" && value > 10 && value < 100) {
            this._age = value;
        } else {
            console.error("设置的age不符合规范");
        }
    }
}

console.log(person.age); //18
person.age = 111; //设置的age不符合规范
console.log(person.age); //18
person.age = 20;
console.log(person.age); //20

我们可以发现,当我们要控制对象里面的值不能被随意更改,需要一定规则限制的时候可以用对象的访问器来实现。

此外,访问器还可以进行伪造属性,也就是对象本身没有的属性,可以使用访问器进行伪造。

let lesson = {
  list: [
    { name: "css", price: 60 },
    { name: "js", price: 70 },
    { name: "html", price: 80 },
    { name: "jquery", price: 90 },
    { name: "vue", price: 100 },
  ],
  // 可以利用访问器实现特定方法
  get total() {
    return this.list.reduce((v, l) => v + l.price, 0);
  },
  get namecount() {
    return `当前课程数量${this.list.length}`;
  },
  // 传入参数加到每个对象的name中
  set changename(val) {
    this.list.forEach((item) => {
      item.name = item.name + val;
    });
  },
  get changename() {
    return this.list;
  },
};
console.log(lesson.total); // 400
console.log(lesson.namecount); // 当前课程数量5
lesson.changename = "_火爆售卖中";
//   {"name":"css_火爆售卖中","price":60},{.....}
console.table(lesson.changename);

2、修改属性

我们上面看到了对象的默认属性有四个,可以用Object.getOwnPropertyDescriptor(obj, propertyName) 来进行查询有关某个属性的完整信息。

而要修改属性的默认属性,就必须使用 Object.definedProperty() 方法。

Object.defineProperty(obj, prop, descriptor)

obj:要定义属性的对象。
prop:要定义或修改的属性的名称。
descriptor:要定义或修改的属性描述符。

let person = {}
Object.defineProperty(person, 'name', {
    writable: false,
    value: 'ly',
    configurable: false
})
console.log(person.name); //ly
person.name = 'lyyy'
console.log(person.name); //ly
delete person.name;
console.log(person.name); //ly

我们为person对象赋予了一个只读的、不可配置的属性name, 且值为ly,那么这个属性的值就不能再被修改、这个属性不能从对象删除了。

但是要注意: configurable设置为false后,再调用Object.defineProperty()就会受到限制了。

let person = {}
Object.defineProperty(person, 'name', {
    writable: false,
    value: 'ly',
    configurable: false
})
console.log(person.name); //ly
person.name = 'lyyy'
console.log(person.name); //ly

Object.defineProperty(person, 'name', {
    writable: true
})

person.name = 'yyy' // Cannot redefine property: name
console.log(person.name); //ly

当configurable设置为false后,就不能对其属性进行二次配置了。

Object.defineProperty(obj, props)

obj :在其上定义或修改属性的对象。
props:定义其可枚举属性或修改的属性描述符的对象。

这个方法可以在一个对象上同时定义多个属性。

let person = {};
Object.defineProperties(person, {
    name: {
        value: 'ly',
        writable: true,
        configurable: true,
        enumerable: true
    },
    _age: {
        value: 18
    },
    age: {
        get() {
            return this.age;
        },
        set(value) {
            if (typeof value === "number" && value > 10 && value < 100) {
                this._age = value;
            } else {
                console.error("设置的age不符合规范");
            }
        }
    }
})

3、封闭对象

  • Object.seal(obj)封闭对象,即不能删除已有属性,但可以修改已有属性的值,不能添加新的属性
  • Object.isSealed(obj)查看对象是否被封闭,已封闭返回 true,未封闭返回 false
let user = {
    name: "ly",
    age: 18,
};
// Object.seal() 封闭对象
Object.seal(user);
// 已封闭返回true,未封闭返回false
console.log(Object.isSealed(user));

封闭对象的本质就是将对象可配置属性configurable变成false

console.log(JSON.stringify(Object.getOwnPropertyDescriptors(user), null, 2));

image.png

4、冻结对象

  • Object.freeze(obj)冻结对象,冻结后值不可被修改、属性不可重复修改、可以被遍历
  • Object.isFrozen(obj)查看对象是否被冻结,是返回 true,不是返回 false
let user = {
    name: "ly",
    age: 18,
};

Object.freeze(user); // true
console.log(Object.isFrozen(user)); //true

封闭对象的本质就是将对象可配置属性configurable和writable变成false

image.png

5、Vue中的数据代理与数据响应

总结到这里,就不得不提一下Vue中的数据代理和数据响应。Vue2的数据代理就是使用Object.defineProperty加访问器属性实现的。

在Vue2中,我们在template的data中写的数据,在Vue的实例vm上面其实是绑定在_data上面的,但是我们在使用data中的数据时,却没有{{_data.xxx}},这是因为,Vue自动使用数据代理,将_data中的数据代理到vm上面去了,同时,为每一个添加到vm上的属性,都添加了setter和getter的访问器属性。

let vm = {
    _data:{
        x:100
    }
}

Object.defineProperty(vm, 'x', {
    get() {
        return vm._data.x;
    },
    set(value) {
        vm._data.x = value;
    }
})

但是这有一个问题,就是对引用类型,尤其是数组,再对其进行增删查改,数据不能进行响应,所以在Vue3中,官方使用了Proxy和Reflect对引用数据类型进行了数据代理,而只是保留了对原始数据类型仍然使用Object.defineProperty进行数据代理。