JavaScript中的對象描述符(屬性特性)

  咱們先建立一個對象:函數

var person = {
  name: "Nicholas",
  _job: "Software Engineer",
  sayName: function(){
    alert(this.name);
  },
  get job(){
        return this._job;              
    },
  set job(newJob){
        this._job=newJob;   
    }
}

  在這個對象中,咱們定義了一個name屬性和一個_job屬性;至於以set和get開頭的兩處代碼,他們共同定義了一個屬性job。明顯屬性job和_job、name的是不一樣的。是的,JavaScript中的對象有兩種不一樣類型的屬性:數據屬性和訪問器屬性。性能

  name和_job是數據屬性,job是訪問器。數據屬性和訪問器屬性的最大的不一樣就在於:當訪問一個訪問器屬性時,獲得get後面函數的返回值;給一個訪問器屬性賦值時,執行的是set後面的函數,這個函數以賦的值爲參數:測試

console.info(person.job);//Software Engineer
person.job="Coder";
console.info(person.job);//Coder
console.info(person._job);//Coder

  在set函數中咱們經過this改變了_job的值(this指向person這個對象);this

  在瞭解了對象屬性的類型後,咱們再來看看對象屬性特性。每一個對象的屬性在建立時都帶有一些特徵值,JavaScript經過這些特徵值來定義它們的行爲。spa

咱們在建立person對象時沒有爲它的屬性們直接指定特徵值,JavaScript自動爲它們建立了屬性特性。在ES3中屬性特性不可訪問,可是ES5中屬性的特性能夠經過Object.getOwnPropertyDescriptorsObject.getOwnPropertyDescriptor獲得:prototype

var descriptors= Object.getOwnPropertyDescriptors(person)
//descriptors的內容以下;
{
    age:{value: 29, writable: true, enumerable: true, configurable: true}
    job:{enumerable: true, configurable: true, get: ƒ, set: ƒ}
    name:{value: "Nicholas", writable: true, enumerable: true, configurable: true}
    sayName:{writable: true, enumerable: true, configurable: true, value: ƒ}
    _job:{value: "Coder", writable: true, enumerable: true, configurable: true}
}

  Object.getOwnPropertyDescriptors返回一個對象,包含了描述person對象的全部屬性的特性。其中每個屬性的特性用一個對象表示,咱們叫它屬性描述符。咱們能夠看到:數據屬性的描述符有四個屬性:value,writable enumerable ,configurable ;訪問器屬性的描述符也有四個屬性:enumerable ,configurable,get,set ;code

那麼咱們分別看下數據屬性及訪問器屬性的這幾個描述符:對象

數據屬性

  數據屬性的描述符的有四個屬性分別是:blog

   1.value:包含這個屬性的數據值。讀取屬性的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。默認爲undefined.

  2.writable:表示可否修改屬性的值。是一個bool值,默認爲true

  3.enumerable:屬性是否可枚舉,即可否經過for-in循環返回屬性。是一個bool值,默認爲true

  4.configrable:屬性是否可配置。即屬性可否經過delete刪除,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性。是一個bool值,默認爲true

  咱們最初開始建立的person對象的屬性name,它的value爲「Nicholas」,其餘描述符爲true。繼承

請看例子:

訪問器屬性 

  訪問器屬性的描述符的有四個屬性分別是:

  1.get:在讀取屬性時調用的函數。默認值爲 undefined。

  2.set:在寫入屬性時調用的函數。默認值爲 undefined

  3.enumerable:屬性是否可枚舉,即可否經過for-in循環返回屬性。是一個bool值,默認爲true

  4.configrable:屬性是否可配置。即屬性可否經過delete刪除,可否修改屬性的特性,或者可否把屬性修改成數據屬性。是一個bool值,默認爲true

  咱們最初開始建立的person對象的屬性job,它的get和set值分別是咱們指定的函數,其餘描述符爲true。

  要修改屬性默認的特性,必須使用 ECMAScript 5 Object.defineProperty或 Object.defineProperties

  有了定義屬性特性的方法,那咱們經過代碼來探索下這些屬性特性的做用:

  數據屬性:

var person ={};
//除了configrable以外,其餘三個屬性相互之間不會影響,讀者能夠本身測試

console.info('---------------------writable start--------------------------------');
Object.defineProperty(person, "name", {
  writable: false,
  enumerable:true,
  configurable:true,
  value: "Nicholas"
});
console.info(person.name); //"Nicholas"
person.name = "Greg";
//writable爲false,屬性不可修改
console.info(person.name); //"Nicholas"

//writable爲false,但configrable爲true,咱們能夠從新配置屬性描述符,
Object.defineProperty(person, "name", {
  writable: false,
  enumerable:true,
  configurable:true,
  value: "John"
});
console.info(person.name)//John

delete person.name
//但configrable爲true,屬性能夠被刪除
console.info (person.name)//undefined 

console.info('---------------------writable end--------------------------------');

console.info('---------------------enumerable start--------------------------------');
var person={};
Object.defineProperty(person, "name", {
  writable: false,
  enumerable:true,
  configurable:true,
  value: "Nicholas"
});


//enumerable爲true屬性可枚舉
for(var prop in person){
  console.info(prop)//name
}

Object.defineProperty(person, "name", {
  writable: false,
  enumerable:false,
  configurable:true,
  value: "Nicholas"
});


//enumerable爲false屬性不可枚舉,循環體不執行
for(var prop in person){
  console.info(prop)//
}

console.info('---------------------enumerable end--------------------------------');
console.info('---------------------configurable start--------------------------------');

var person={};
Object.defineProperty(person, "name", {
  writable: true,
  enumerable:true,
  configurable:false,
  value: "Nicholas"
});
//configurable爲false,writable爲true,屬性仍然可修改
person.name="John"
console.info(person.name);//John

//configurable爲false,writable爲true,仍然能夠經過配置的方式改變屬性值
Object.defineProperty(person, "name", {
    writable: true,
    enumerable:true,
    configurable:false,
    value: "Nicholas"
});

console.info(person.name)

//configurable爲false,enumerable爲ture,屬性可枚舉
for(var prop in person){
  console.info(prop)//name
}

//configurable爲false,咱們仍然能夠把writable屬性由true改成false
Object.defineProperty(person, "name", {
  writable: false,
  enumerable:true,
  configurable:false,
  value: "Nicholas"
});
console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false}

//configurable爲false,writable爲false,不能經過配置改變value的值
try{
  Object.defineProperty(person, "name", {
    writable: false,
    enumerable:true,
    configurable:false,
    value: "John"
  });
}catch(error){
  console.info("value change error");
  console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false}
}


//configurable爲false,可是不能把writable屬性由false改成true
try{
  Object.defineProperty(person, "name", {
    writable: true,
    enumerable:true,
    configurable:false,
    value: "Nicholas"
  });
}catch(error){
  console.info("writable false to true error")
  console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false}
}

//configurable爲false,不能改變enumerable的值
try{
  Object.defineProperty(person, "name", {
    writable: false,
    enumerable:false,
    configurable:false,
    value: "Nicholas"
  });
}catch(error){
  console.info("enumerable change error");
  console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: false, enumerable: true, configurable: false}
}

var person={};
Object.defineProperty(person, "name", {
  writable: true,
  enumerable:true,
  configurable:true,
  value: "Nicholas"
});

//configurable爲true,能夠把數據屬性修改成訪問器屬性
try{
  Object.defineProperty(person, "name", {
    get: function(){return "Nicholas"},
    enumerable:true,
    configurable:false
  });
}catch(error){
  console.info("get error");
}
console.info(Object.getOwnPropertyDescriptor(person,"name"))//{set: undefined, enumerable: true, configurable: false, get: ƒ}


var person={};
Object.defineProperty(person, "name", {
  writable: true,
  enumerable:true,
  configurable:false,
  value: "Nicholas"
});
//configurable爲false,不能夠把數據屬性修改成訪問器屬性
try{
  Object.defineProperty(person, "name", {
    get: function(){return "Nicholas"},
    enumerable:true,
    configurable:false
  });
}catch(error){
  console.info("get error");
}
console.info(Object.getOwnPropertyDescriptor(person,"name"))//{value: "Nicholas", writable: true, enumerable: true, configurable: false}

console.info('---------------------configurable end--------------------------------');

 訪問器屬性

//訪問器屬性

console.info("------------------------------------------------------");
var person ={_name:"Nicholas"};
Object.defineProperty(person, "name", {
  get: function(){
          console.info("get 被調用")
          return this._name
       },
  set:function(newName){
         console.info("set 被調用")
         this._name=newName
      },
  enumerable:true,
  configurable:true,
});
person.name;//get 被調用
person.name="John";//set 被調用

console.info("------------------------------------------------------");
console.info("----------------------不設set 開始--------------------------------");

var person ={_name:"Nicholas"};

Object.defineProperty(person, "name", {
  get: function(){
          console.info("get 被調用")
          return this._name
       },
  enumerable:true,
  configurable:true,
});
person.name;//get 被調用
person.name="John";//沒有設置set,什麼也沒發生
console.info(person.name)//Nicholas,

console.info("----------------------不設set 結束--------------------------------");

console.info("----------------------不設get 開始--------------------------------");

var person ={_name:"Nicholas"};

Object.defineProperty(person, "name", {
  set:function(newName){
         console.info("set 被調用")
         this._name=newName
      },
  enumerable:true,
  configurable:true,
});
console.info(person.name);//沒有get,獲得 undefined
console.info(person._name);//Nicholas
person.name="John";//set 被調用
console.info(person._name)//John,經過set,_name的值被改變

console.info("----------------------不設get 結束--------------------------------");

console.info("----------------------不設get set開始--------------------------------");
//雖然不報錯,可是這個屬性沒有任何意義
var person ={_name:"Nicholas"};
Object.defineProperty(person, "name", {
  enumerable:true,
  configurable:true,
});
console.info("----------------------不設get 結束--------------------------------");

console.info("----------------------enumerable 開始--------------------------------");
var person ={_name:"Nicholas"};
Object.defineProperty(person, "name", {
  get: function(){
          console.info("get 被調用")
          return this._name
       },
  set:function(newName){
         console.info("set 被調用")
         this._name=newName
      },
  enumerable:true,
  configurable:true,
});
for(var prop in person){
  console.info(prop)//_name,name
}

Object.defineProperty(person, "name", {
  get: function(){
          console.info("get 被調用")
          return this._name
       },
  set:function(newName){
         console.info("set 被調用")
         this._name=newName
      },
  enumerable:false,
  configurable:true,
});
for(var prop in person){
  console.info(prop)//_name
}
console.info("----------------------enumerable 結束--------------------------------");

console.info("----------------------configurable 開始--------------------------------");
var person ={_name:"Nicholas"};
Object.defineProperty(person, "name", {
  get: function(){
          console.info("get 被調用")
          return this._name
       },
  set:function(newName){
         console.info("set 被調用")
         this._name=newName
      },
  enumerable:true,
  configurable:false,
});

person.name;//get 被調用
person.name="John";//set 被調用
console.info(person.name);//John

//報錯
try{
  Object.defineProperty(person, "name", {
    get: function(){
            console.info("get 被調用")
            return this._name
         },
    set:function(newName){
           console.info("set 被調用")
           this._name=newName
        },
    enumerable:true,
    configurable:false,
  });
}catch(e){
  console.info("不能從新定義name的屬性標識符")
}

//報錯
try{
  Object.defineProperty(person, "name", {
    value:"123",
    writable:true,
    enumerable:true,
    configurable:false,
  });
}catch(e){
  console.info("不能從新定義name的屬性標識符")
}

console.info("----------------------configurable 結束--------------------------------");

 

 總結一下:

  1.writable爲true,屬性值就能夠修改,不管是經過.運算符仍是經過Object.defineProperty方法;

  2.writable爲false,不能經過.運算符修改屬性。可是在configurable爲true的狀況下能夠經過經過Object.defineProperty方法從新設置value值,從而修改屬性值;

  3.只要enumerable爲true,屬性就可枚舉,爲false則不可枚舉;

  4.configurable爲true的狀況下,能夠對屬性描述符對象進行任何修改;

  5.configurable爲fasle的狀況下,能夠經過Object.defineProperty把writable改成true,在writabe爲true的狀況下,能夠修改value的值。

  6.configurable爲false的狀況下,除第5條的所述的狀況外,不能經過Object.defineProperty修改屬性的任何特性值。

可擴展性

  ES5定義了三個方法Object.preventExtensionsObject.sealObject.freeze分別定義了不一樣級別的可擴展性。可點擊鏈接前往MDN閱讀;

最後:get、set 與繼承

function Person(){}

var p=Person.prototype;
p._name="John";
Object.defineProperty(p,"name",{
    get: function(){return this._name},
    set: function(newName){ this._name=newName},
    enumerable:true,
    configurable:true
})

var person=new Person();
console.info(person.name)//John
console.info(person.hasOwnProperty("_name"))//false

//雖然name屬性的set get方法是定義在原型中的
//可是經過person調用時,它們的this屬性會指向person
//因此經過person.name設置屬性時,執行set方法中的this._name=newName時,
//會給person對象添加_name屬性,並把newName賦給person新建的屬性_name;
person.name="Nicholas";
console.info(person.name);//Nicholas
console.info(p._name)//John
console.info(person.hasOwnProperty("name"))//false
console.info(person.hasOwnProperty("_name"))//true

 寫的有點冗長,可是算是基本測試了各類狀況,方便查閱;

相關文章
相關標籤/搜索