• 浅谈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;

      

      

      

      

      

     
  • 相关阅读:
    Prometheus入门教程(二):Prometheus + Grafana实现可视化、告警
    Prometheus 入门教程(一):Prometheus 快速入门
    Prometheus 系列开篇:为什么要学 Prometheus ?
    你总是遗憾,是因为你还没想好,你的一生想怎么过!
    搞 Java 的年薪 40W 是什么水平?
    闪送,为何能从顺丰中杀出一条血路?
    安全攻击溯源思路及案例
    Windows下登录凭证窃取技巧
    Linux下登录凭证窃取技巧
    如何探测内网存活主机
  • 原文地址:https://www.cnblogs.com/homeStrong/p/8836823.html
Copyright © 2020-2023  润新知