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

 写的有点冗长,可是算是基本测试了各类状况,方便查阅;