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 方法,因此陷入死循環,造成方法棧溢出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM