浅谈js对象之数据属性、访问器属性、Object.defineProperty方法

一、对象

  这个不用多说,常见的几种创建对象的方法有:

  1.通过构造函数创建对象,如下所示:

 function Person(){

        }
 var person = new Person();

  2.通过Object创建简单对象,例如:

 var obj = new Object();

  3.通过字面量创建对象。

 var obj = {};

  常用的一般是第一种和第三种方法。

二、属性类型

  javascript中有两种属性:数据属性和访问器属性,确切的说这两种特性是用来描述对象属性的各种特征,比如说这个对象属性的值能否被改变,因为这些特性是内部值,而javascript又不能直接访问,所以在规范中把他们放在了两对儿化括号中,例如[[Enumerable]]。下面 来看看这两种具体的内部属性。

  1.数据属性

   数据属性包含一个数据值。在这个位置可以读取和写入值,

     [[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为访问器属性(这个后面会说),默认为true;

     [[Enumerable]]:用来修饰对象属性的枚举特性,表示能否通过用for in循环返回属性,默认为true;

     [[Writable]]:用来修饰对象属性值的可写特性,默认为true;

   [[Value]]:包含这个属性的数据值,访问器属性里面是没有这个特性的。默认为undefined;比如var person = {name:'lili'},这里设置了一个名叫name的属性,它的值是"lili", [[Value]]特性将被设置为"lili",其余的是三个特性都是默认值true。

  下面就用具体的实例来理解上面的特性。首先这里要先谈的是Object.defineProperty()方法,这个方法接收三个参数:属性所在的对象、属性的名字、和一个描述符对象(也就是上面的四个数据属性,用来描述对象属性的特性),这里要注意了,采用Object.defineProperty方法创建属性时候,数据属性[[configurable]]、[[writable]]、[[enumerable]]默认为false,这要和字面量直接声明属性时默认值相反但是如果只是用Object.defineProperty改变原来已有属性的值则没有此限制,具体看下面例子:

     var person = {};
    Object.defineProperty(person,'name',{ //创建name属性 value:'lili', }); console.log(person.name); //lili person.name = 'shasha'; //writable默认是false 不可改动属性值 console.log(person.name); //lili

  修改writable的值为true可以看到,person.name的值打印出来是'shasha';

     var person = {};
    Object.defineProperty(person,'name',{//创建name属性
            writable:true,
            value:'lili',
        });
        console.log(person.name); //lili
        person.name = 'shasha'; //writable设置为true, 属性值被重写
        console.log(person.name); //shasha

 但是当person里面有属性,采用Object.defineProperty方法只是修改特性和值时,默认值都为true,具体请看下面示例:

       var person = {age:22};
        Object.defineProperty(person,'age',{  //重新定义修改age属性
            value:33,
        });
        person.age = 66;
        console.log(person.age); //66  说明writable此时为true,

  设置[[enumrable]]特性的规则跟[[writable]]相同,为true时才能用for in 循环遍历出属性。下面来重点看看[[Configurable]]特性的作用,例子如下:

      var person = {};
        Object.defineProperty(person,'name',{
            configurable:false,
            value:'lili',
        });
        console.log(person.name); //lili
        delete person.name;
        console.log(person.name); //lili

  把configurable设置为false,表示不能删除对象属性,对这个这个属性调用delete方法,非严格模式下什么也不会发生,严格模式下则会抛出错误,而且一旦把属性从configurable设置为false,以后就不能把它设置为true,此时在调用Object.defineProperty方法修改除writable、value之外的特性,都会导致错误,看下面例子:

   var person = {};
        Object.defineProperty(person, 'name', {
            configurable: false,
            enumerable: true,
            writable: true,
            value: 'lili',
        });
        Object.defineProperty(person, 'name', {
            // configurable: true,  //会报错额!
            // enumerable: false,  //会报错的!
            writable: false,
            value: 'tangtang',
        });
        person.name = 'wangdachui';
        console.log(person.name);//tangtang

  2.访问器属性

  访问器属性不包括数据值,它包含一对getter和setter函数,在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,访问器属性有如下四个特性:

   [[Configurable]]:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,能否把属性修改为数据属性(这个后面会说),默认为true;

   [[Enumerable]]:用来修饰对象属性的枚举特性,表示能否通过用for in循环返回属性,默认为true;

   [[Get]]:在读取属性时候调用的函数,默认为undeifne;

   [[Set]]:在写入属性时候调用的函数,默认为undeifne;

  访问器属性不能直接定义,必须使用Object.defineProperty()来定义。请看下面的例子。

      var book = {
            _year: 2018,
            edition: 1,
        };
        Object.defineProperty(book, 'year', {
            get: function () {
                alert('通过对象方法访问 哈哈!');
                return this._year;
            },
            set: function (newValue) {
                if (newValue > 2004) {
                    this._year = newValue;
                    this.edition += newValue - 2004;
                    alert(this.edition);
                }
            }
        });
        console.log(book.year) //通过对象方法访问 get函数里面alert出对应信息;
        book.year = 2005;
        console.log(book.edition); //2

  以上例子中,book对象有两个默认属性:_year 和 edition ,其中_year前面加了下划线,用于表示只能通过对象方法去访问这个属性,即通过get方法返回属性值。如果直接访问book.edition的值,则不会调用get方法返回。book.year=2005即是修改book的year属性值,会调用set方法,参数newValue就是设置的2005这个值,在上面例子中set函数里还改变了属性edition的值。

3.定义多个属性

  采用Object.defineProperties我们也可以为一个对象定义多个属性,这个方法有两个参数:第一个要定义属性的对象,第二个也是对象,表示要添加的多个属性和其对应的属性描述符,具体怎么添加呢,请看下面的例子:

      var book = {};
        Object.defineProperties(book,{
            _year:{
                writable:true,
                value:2004,
            },
            edition:{
                writable:true,
                value:1,
            },
            year:{
                get:function(){
                    return this._year;
                },
                set:function(newValue){
                    if(newValue > 2004){
                        this._year = newValue;
                        this.edition += newValue - 2004;
                    }
                }
            }
        });

  以上代码在book对象上定义了两个数据属性(_year和edition)和一个访问器属性(year)。

  4.读取属性的特性

  上面我们一直是在讨论如何设置属性的描述特性,那么对于数据属性和访问器属性里面的具体特性我们怎么读取呢?这里介绍另一个Object的方法:getOwnPropertyDescriptor方法,该方法接收两个参数:属性所在的对象和要读取其描述符的属性名称,它的返回是一个对象,来我们来看看具体的实例:

      var book = {};
        Object.defineProperties(book, {
            _year: {
                writable: true,
                value: 2004,
            },
            edition: {
                writable: true,
                value: 1,
            },
            year: {
                get: function () {
                    return this._year;
                },
                set: function (newValue) {
                    if (newValue > 2004) {
                        this._year = newValue;
                        this.edition += newValue - 2004;
                    }
                }
            }
        });
        book.year = 2005;
        console.log(book.edition); //2
        var descriptor1 = Object.getOwnPropertyDescriptor(book, '_year');
        console.log(descriptor1.value); //2005
        console.log(descriptor1.configurable);//false
        console.log(typeof descriptor1.get); //undefine
        var descriptor2 = Object.getOwnPropertyDescriptor(book, 'year');
        console.log(descriptor2.value); //2005
        console.log(descriptor2.configurable);//false
        console.log(typeof descriptor2.get); //function;

  

  

  

  

  

 
原文地址:https://www.cnblogs.com/homeStrong/p/8836823.html