zoukankan      html  css  js  c++  java
  • JavaScript ECAMScript5 新特性——get/set访问器

           之前对get/set的理解一直有误,觉得get set 是对象属性方法。看了别人的博客也有很多疑问,今天系统的做了很多测试终于弄明白了。(自己通过看书和写demo测试的,如有不对欢迎大家批评指正)

            get/set访问器不是对象的属性,而是属性的特性。大家一定要分清楚。特性只有内部才用,因此在javaScript中不能直接访问他们。为了表示特性是内部值用两队中括号括起来表示如[[Value]]。

             1.先简单介绍一下属性的这些特性(这里是简单的背书)

             (1)数据属性——包含一个数据值的位置。这个位置可以读入和写入值。

                                     数据属性有描述其行为的四个特性:

                 [[Configurable]]:是否可配置

                 [[Enumerable]]:是否可枚举

                   [[Writable]]:是否可读

                 [[Value]]: 属性值    

        (2)访问器属性属性——不包含数据值,包含一个getter和setter函数(这两个函数不是必须的)

                                访问器属性也有描述其行为的四个特性:              

                 [[Configurable]]:是否可配置

                 [[Enumerable]]:是否可枚举

                  [[Get]]:在读取属性时调用的函数,默认是undefined

                  [[Set]]:在写入属性时调用的函数,默认是undefined  

              2.这里着重介绍[[Get]]/[[Set]]就是我们所说的get/set访问器

                   先说一个书上说的 get/set访问器行为特点:get/set访问器可以不用定义,不定义也可以读写属性值。也可以只定义一个。只定义get,则被描述的属性只可读,不可写。只定义set,则被描述的属性只可写,不可读。

                 (1)我们原来的get set方法是这样的:

     1 function Foo(val){
     2         var value=val;
     3         this.getValue=function(){
     4           return value;
     5         };
     6         this.setValue=function (val){
     7          value=val;
     8         };
     9       }
    10       var obj=new Foo("hello");
    11       alert(obj.getValue());//"hello"
    12       obj.setValue("hi");
    13       alert(obj.getValue());//"hi"

               以上代码只是利用闭包作用域实现的get set 方法,注意是方法即实例对象的属性方法,不是属性特性。如果不定义,将无法访问value值

          function Foo(val){
            var value=val;
        /*    this.getValue=function(){
              return value;
            };
            this.setValue=function (val){
             value=val;
            };
            */
          }
          var obj=new Foo("hello");
         alert( obj.value);//undefined
         

                 以下例子也是对象的属性方法,不是属性特性。

    var obj={
            name:"john",
           get:function (){
              return this.age;
           }//只定义了get ,没有定义set,但是仍然可以读,写,name属性,即使这里是age
           //这里这样定义的方法不会影响属性的get,set 特性。只是普通的对象属性
        };
    
        alert(obj.name);//john 可读
        obj.name="jack";//可写
        alert(obj.name);//jack

       (2)作为访问器属性的特性的get/set访问器。

         再说一遍不是对象的属性,他们 决定属性能否、怎么读写。如果不设置也行,就和平时的读写一样(属性可读可写,读写访问的都是属性本身的值)

                     要改变属性的get /set 特性,有两种方式:

         a.就是用Object.defineProperty()

    var object={
          _name:"Daisy" 
        };
        Object.defineProperty(object,"name",{//这里的方法名name,就表示定义了一个name属性(因此才能通过object.name访问),只定义了getter访问器,没有定义[[value]]值
           get:function (){//只定义了get 特性,因此只能读不能写
              return this._name;
           }
        
        });
        alert(object.name);//"Daisy"
        object.name="jack";//只定义了getter访问器,因此写入失效
        alert(object.name);//"Daisy"

                   注意Object.defineProperty(object,pro,{})中的属性名一定要和object.pro访问的属性对应

                 b.就是用用 get set 关键字:

    var object={
         _name:"Daisy",
          get name(){//这里的方法名name ,就表示定义了一个name属性(因此才能通过object.name访问),只定义了getter访问器,没有定义[[value]]值
             return this._name;
          }//get,set方法只是属性的特性 ,不是对象方法,决定属性能否、怎么读写
          };
        alert(object.name);// Daisy这里去掉下划线 方法就是Daisy  ;加上就是undefined
        object.name="jack";//只定义了getter访问器,因此只能读不能写
        alert(object.name);//Daisy

                 以上两种方法等效。注意的是以上两种方法object对象当中都将有有两个属性:_name(有初始值) name(无初始值),通过浏览器控制台可以看到

                 那么这个name属性实在什么时候定义的呢?我们知道Object.defineProperty(object,pro,{})可以给对象定义一个新属性pro,既然get pro(){}/set pro(){}和Object.defineProperty(object,pro,{})等效,则也会定义一个新属性pro .这就是为什么object里面有两个属性的原因。

    chrome 控制台   (3)学习博客http://kb.cnblogs.com/page/75072/中关于标准标准的Get和Set访问器的实现:引发的思考

                     我自己也写了一个一样的例子

    function Foo(val){
           this.value=val;//定义了value属性  并没有定义_value
          }
          Foo.prototype={
            set value(val){//注意方法名和属性名相同,在prototype里定义了value属性
               this._value=val;
             },
            
             get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
               return this._value;
             }
             
          }; 
    //访问器返回和设置的都是_name,这里并没有定义_name属性为什么也可以读可以写???? 

        var obj=new Foo("hello");
        alert(obj.value);
    //"hello"
        obj.value="yehoo";
        alert(obj.value);//"yehoo"

                 为了解决以上这个疑问,做了很多测试,我们一一来看:

         先看这个例子,在prototype里面只定义get 特性,在obj.value读value属性时,在实例里面寻找没有,然后在原型里面找到,调用的是原型的get方法,只能读不能写

    function Foo(val){
           this._value=val;//这里 的属性是带下划线的,初始化实例对象的_value属性,_value属性可读可写
          }
          Foo.prototype={
            // set value(val){//注意方法名和属性名相同,在prototype里定义了value属性
            //   this._value=val;
            // },
            
             get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
               return this._value;
             }
             
          }; 
          var obj=new Foo("hello");
          alert(obj.value);//hello  访问的是prototype里面的value 属性
          obj.value="yehoo";//只定义了name 属性的get 特性,因此只能读不能写,写入失效
          alert(obj.value);//hello

        如果构造函数里面this._value 去掉下划线,在prototype里面定义的value属性,定义了get 特性。依然可以控制value属性的读写 。也就是说obj.value访问属性时,会调用get方法,先在对象本身寻找,如果没有,再到prototype寻找,如果都没有才算没有定义,默认的既可读又可写

     function Foo(val){
           this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效
          }
          Foo.prototype={
            //  set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性
             //   this._value=val;
              //},
            
             //value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah"
             
            //只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值
            
             get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
               return this._value;
             }
             
          }; 
          var obj=new Foo("hello");//"hello"没有写入成功
          alert(obj.value);//undefined  
          obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效
          alert(obj.value);//undefined

    为了证明上面例子是可读不可写的:手动写入_value:"hah",就可以读取value 但不能写入。

      function Foo(val){
           this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效
          }
          Foo.prototype={
            //  set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性
             //   this._value=val;
              //},
            
            _value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah"
             
            //只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值
            
             get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
               return this._value;
             }
             
          }; 
          var obj=new Foo("hello");//"hello"没有写入成功
          alert(obj.value);//"hah" 
          obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效
          alert(obj.value);//"hah"

                     如果手动写入的是value:"hah",那么可以争取读取value的值吗?由于get方法返回的this._value并没有定义,obj.value读取value值调用get value(){}方法失效,但是value仍然不能写入。

      function Foo(val){
           this.value=val;//在原型里面只定义了value的get特性,因此这里写入失效
          }
          Foo.prototype={
            //  set value(val){//注意方法名和属性名相同,在prototype里定义了value属性的set特性
             //   this._value=val;
              //},
            
            value:"hah",//即使手动写入value值,由于get方法返回的是this._value,因此也不能正确读取value:"hah"
             
            //只要声明了get pro (){}和set pro (){}属性就都能读能写,但是如果函数定义错误,依然不能按要求访问到正确的属性值
            
             get value(){//方法名和属性名相同,在prototype里面定义了value属性和它的get 特性
               return this._value;
             }
             
          }; 
          var obj=new Foo("hello");//"hello"没有写入成功
          alert(obj.value);//undefined  读取失效  因为只要obj.value就会调用get ,而get返回的是this._value,没有这个值,因此undefined
          obj.value="yehoo";//只定义了get 特性,因此只能读不能写,写入失效
          alert(obj.value);//undefined

          再看这个例子,get set 都定义了,但是返回没有定义的this._value。可以发现value既可读又可写。去掉原型里面的get set方法,依然可读可写

     function Foo(val){
           this.value=val;
          }
          Foo.prototype={
              set value(val){
                this._value=val;
              },
             get value(){
               return this._value;
             }
             
          }; 
          var obj=new Foo("hello");
          alert(obj.value);//hello 
          obj.value="yehoo";
          alert(obj.value);//yehoo
     function Foo(val){
           this.value=val;
          }
         //和平时的操作是一样的了,就是回到了不定义get /set访问器特性的默认状态
          var obj=new Foo("hello");
          alert(obj.value);//hello 
          obj.value="yehoo";
          alert(obj.value);//yehoo

                 总结

                 只声明了get pro(){}属性 可读不可写;

            只声明 set pro(){}属性可写不可读。
            如果都不声明,属性可读可写;
            如果都声明就按照,get set 定义的方法,读写;
            如果都声明了,但是定义的读写方法不能正确读写,get/set失效。变成默认的可读可写

            在prototype里面定义的value属性,定义了get 特性。依然可以控制value属性的读写 。也就是说obj.value访问属性时,会调用get方法,先在对象本身寻找,如果没有,再到prototype寻找,如果都没有才算没有定义,默认的既可读又可写。

         补充:

        不管是用get pro(){}/set pro (){} 还是用Object.defineProperty(object,pro,{
                get:function (){
                     return this._name;
                  } });
             pro不能和 return this. 后面的属性一样,不然会报下面的错误:(具体我也不知道为什么,好像是自身调用引起的栈溢出)

             经大神指正,明白为什么这里报错:在get value(){}方法里返回 this.value,就会又去调用value的get 方法,因此陷入死循环,造成方法栈溢出。

  • 相关阅读:
    利用模板方法模式导出Excel文件
    利用模板方法模式实现导出CSV文件功能
    抽象类多实现,省略this关键字调用实现类方法
    Spring工具类
    DateUtils工具类
    Java获取时间戳
    springMVC接受类型为Map,ajax请求参数类型为字符串将导致的请求400错误
    Set转逗号分隔字符串
    html标签中自定义属性
    编译安装MySQL
  • 原文地址:https://www.cnblogs.com/DaisyWang/p/Git.html
Copyright © 2011-2022 走看看