数据属性和访问器属性——Object.defineProperty()方法

一、数据属性

Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

通常我们给对象设置一个属性,configurable 、 enumerable和 writable都是true。

在调用 Object.defineProperty() 方法设置属性时,如果不指定, configurable 、 enumerable和 writable 特性的默认值都是 false。

当用Object.defineProperty()设置属性的时候:

  • 拥有布尔值的键 configurableenumerable 和 writable 的默认值都是 false
  • 属性值和函数的键 valueget 和 set 字段的默认值为 undefined

比如:设置属性值不可以被修改

const object1 = {};

Object.defineProperty(object1, 'property1', {
  value: 42,
  writable: false
});

object1.property1 = 77;
// throws an error in strict mode

console.log(object1.property1);
// expected output: 42

但是在调用Object.defineProperty() 方法修改属性时,指定修改属性的哪个描述值,而不会影响未被修改的描述值。

1、configurable

特点:

值为false,不允许删除该属性;不允许修改enumerable;允许writable从true改为false,不允许writable从false改为true;不管理value。(理解为控制力逐渐衰弱)

值为true,允许修改其他值,也可以修改configurable本身为false,当为false时,不可以修改configurable为true。

违反configurable特性的操作,都要报错。修改value值不管成功与否,都不报错,因为configurable不控制value。

当我们使用Object.defineProperty()方法时,依赖于对象的该属性的自身的设置情况。

比如下面实例中,同时修改属性的writable和value值,修改writable值要依赖属性修改前的configurable值;修改value的值,要依赖属性修改前的writable值。

        var obj = {name: "obj"};
        Object.defineProperty(obj,'age',{
            configurable: false,
            enumerable: true,
            writable: true,
            value: "24"
        });
        Object.defineProperty(obj,'age',{
            writable: false,
            value: "26"
        });
        obj.age = 25

        console.log(obj.age);--26

 上面obj.age = 25修改属性值,无效,但不会报错。但是当writable为false的后,再通过defineProperty设置value,报错

        var obj = {name: "obj"};
        Object.defineProperty(obj,'age',{
            configurable: false,
            enumerable: true,
            value: "24",
            writable: true
        });
        Object.defineProperty(obj,'age',{
            value: "26",
            writable: false
        });
        报错
        Object.defineProperty(obj,'age',{
            value: "24"
        });

2、enumerable

特点:enumerable表明该属性是显式的还是隐式的,值为true表明属性为显式,值为false表明属性为隐式的

        var obj = {name: "zhu", age: 24}
        Object.defineProperty(obj, "age", {
            enumerable: false
        })
        console.log(obj)

 结果:注意看name属性和age属性的颜色

enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。

该键值为false时,该属性不可被枚举,for...in 或 Object.keys 方法不能枚举到该属性。

 

for in遍历

        var obj = {name: "obj"};
        Object.defineProperty(obj,'age',{
            configurable: true,
            enumerable: false,
            value: "24",
            writable: true
        });
        var arr = [];
        for(prop in obj){
            arr.push(prop)
        }
        
        console.log(arr); 结果:["name"]

 Object.keys 返回一个所有元素为字符串的数组,其元素来自于从给定的object上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。

由对象或数组的可枚举的keys值构成的数组(每项都是字符串)。数组的keys值是数组的索引,对象的keys值是对象的属性。

例如:

        var obj = {name:"zhu",age: 24,nb:true};
        var arr = ["str",1,false,[0]];
        console.log(Object.keys(obj))  ["name", "age", "nb"]
        console.log(Object.keys(arr));  ["0", "1", "2", "3"]

 补充:如果你想获取一个对象的所有属性,,甚至包括不可枚举的,请查看Object.getOwnPropertyNames

 Object.keys 遍历

        var obj = {name: "obj"};
        Object.defineProperty(obj,'age',{
            configurable: true,
            enumerable: false,
            value: "24",
            writable: true
        });
        console.log(Object.keys(obj));  ["name"]

3、writable

writable控制value是否可以被修改,如果writable为true,value值可以修改;如果writable为false,value值不可以被修改。

当 writable 属性设置为 false 时,该属性被称为“不可写的”,普通方式 对象.属性 修改无效defineProperty修改会报错。 

注意:不可修改地址,如果引用值,修改地址所指空间的内容,是允许的。

let arr = [1,2,3];
let obj = {
    name: "zhu",
    hobby: arr
}
Object.defineProperties(obj, {
    "name": {
        writable: false
    },
    "hobby": {
        writable: false
    }
})
obj.name = "yu"; //无法修改
obj.hobby[0] = "a";//修改成功

console.log(obj)

4、value

value值的设置就是属性值。

二、拓展知识:

1、Object.defineProperties() 定义多个属性:

        var obj = {}
        Object.defineProperties(obj,{
            name:{
                configurable: true,
                enumerable: true,
                writable: true,
                value:"zhu"
            },
            age: {
                writable: true,
                value:24
            }
        })
        console.log(obj); 

2、Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应属性的描述符对象。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性) 

        var obj = {name: "zhu", age: 24}
        Object.defineProperty(obj, "age", {
            enumerable: false
        })
        var descriptor = Object.getOwnPropertyDescriptor(obj, "age")
        console.log(descriptor);

3、Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符。

使用:Object.getOwnPropertyDescriptors(obj)

        var obj = {}
        Object.defineProperties(obj,{
            name:{
                configurable: true,
                enumerable: true,
                writable: true,
                value:"zhu"
            },
            age: {
                writable: true,
                value:24
            }
        })
        console.log(Object.getOwnPropertyDescriptors(obj)); 

二、访问器属性

1、访问器属性:包含getter和setter函数。读取访问器属性时,调用getter函数,返回有效的值;在写入访问器属性时,调用setter函数传入新值。它包含了4个特性:

  1. [[Configurable]]:表示是否能通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为访问器属性。

  2. [[Enumerable]]:表示能否用for-in循环或Object.keys()返回。

  3. [[Get]]:读取属性时调用的函数,默认undefined。

  4. [[Set]]:写入属性时调用的函数,默认undefined。

实际上,writable、value和get、set属性不能共存,这正是区分数据属性和访问器属性的根本所在。

注意:get和set函数中使用的变量,要在Object.defineProperty外声明。

        var obj = {name: "zhu"};
        Object.defineProperty(obj, "age", {
            // 使用了方法名称缩写(ES2015 特性)
            // 下面两个缩写等价于:
            // get : function() { return bValue; },
            // set : function(newValue) { bValue = newValue; },
            get() {
                console.log("getter");打印getter
                return bValue;
            },
            set(newValue) {
                console.log("setter");打印setter
                bValue = newValue;
            },
            enumerable: true,
            configurable: true
        })
        obj.age = 24; 这一步走的就是setter
        console.log(obj.age); 这一步走的是getter

2、关于继承

如果访问器属性在原型上,通过实例对象,也可以修改原型上的访问器属性。(普通属性和数据属性是不可以通过实例修改原型的属性值。当执行修改操作时,实际上是在实例上添加设置该属性,而非在原型上)

function myclass() {
}

var value;
Object.defineProperty(myclass.prototype, "x", {
  get() {
    return value;
  },
  set(x) {
    value = x;
  }
});

var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1

3、关于this

切记访问器属性中的get和set函数中的this,谁调用了这两个方法,this就指向谁。

        function myclass() {}

        Object.defineProperty(myclass.prototype, "x", {
            get() {
                return this.stored_x;
            },
            set(x) {
                this.stored_x = x;
            }
        });

        var a = new myclass();
        var b = new myclass();
        a.x = 1; 在a对象上添加了stored_x属性,且值为x 1
        console.log(myclass.prototype.x);  获取的时候,获取的是原型上的stored_x

需要注意的是,实例对象调用原型的访问器属性

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值