深究JavaScript你不知道的JavaScript

《你不知道的JavaScript》之对象二

2018-04-08  本文已影响9人  弦五

接下来继续讨论对象的主要内容

一、属性描述符

对于对象中的属性,我们一般都是直接调用,但是属性本身又有什么特性呢,该怎么去描述?属性描述符就扮演了这样一种角色。属性描述符是在ES5以后才有的,看下面的代码。


  var myobjec = {

      a:2,

      };

      var descriptor = Object.getOwnPropertyDescriptor(myobjec,"a")

      console.log(descriptor);

      //返回的结果为一个object对象:

{

configurable:true

enumerable:true

value:2

writable:true

}


返回的对象中包含了四个描述符:configurable(可配置)、enumerable(可枚举)、value(值)、writable(可写入)。

这些属性描述符可以修改吗?当然,我们可以用Object.defineProperty(...)来添加一个新的属性或是修改一个已有属性。

1)writable

决定了属性是否可以被修改。如果将其设置为false,那么对应的value是不能被修改的。



      var myobject = {

          a:2,

      };

      Object.defineProperty(myobject,"a",{

      writable:false,

      configurable:true,

      enumerable:true,

      value:2

      })

      myobject.a = 4;

      console.log(myobject.a);//2


2)configurable(可配置)

只要是可配置,就可以用defineProperty来修改属性描述符。


      var myobject = {

         a:2,

      };

      myobject.a = 3;

      console.log(myobject.a ); //3

      Object.defineProperty(myobject,"a",{

      writable:true,

      configurable:false,

      enumerable:true,

      value:4

      });

      console.log(myobject.a);//4

      myobject.a = 5;

      console.log(myobject.a);//5

      Object.defineProperty(myobject,"a",{

      writable:false,

      configurable:false,

      enumerable:true,

      value:6

      });  //Uncaught TypeError


如果将configurable设置为false后,再去配置就会报“未捕获类型”错误。

3)enumerable(可枚举)

通常属性的这个描述符默认为true,比如在for循环中,便可以枚举此属性。如果你在循环遍历中不想让该属性出现,那么将它的configurable设置为false即可。

二、属性不可变性

很多时候,你都希望属性是不可变的,在ES5中可以通过很多方法来实现:

1)将属性描述符writable和configurable设置为false即可;

2)禁用:可以使用Object.preventExtensions(..)来禁止一个对象添加新属性并且保留已有属性;


    var myobject = {

       a:2,

      };

      Object.preventExtensions(myobject);

      myobject.b = 3;

      console.log(myobject.b);//undefined


3)密封:Object.seal(...),调用这个方法时会调用preventExtention,并把所有的属性都标记为configurable:false,什么意思呢?就是说调用密封方法后,任何为对象添加、删除新属性的操作都是不允许的,但是属性值可以被修改,

4)冻结:Object.freeze(),这个方法会调用seal方法,同时将writable标记为false,所以这个不能修改的级别就比较高了,不仅不能添加删除,而且也不能修改了。

三、[[get]] 和  [[put]]操作

在访问对象属性时,表面上我们只是获取了属性值这么简单,其实是在对象上进行了[[get]]操作,对象的内置方法 [[get]] 会查找对象中是否存在改属性,如果存在返回属性对应的值,如果不存在,其实会执行另外一种行为,就是查找[[ prototype ]]链,也就是原型链,这个后面会讲到。

如果无论如何都找不到这个属性,那么就会返回undefined。


      var myobject = {

      a:2,

      };

      console.log(myobject.b);//undefined

      console.log(c); //Uncaught ReferenceError: c is not defined


注意属性访问和变量访问的区别,如果对象中不存在属性,那么返回undefined,而变量在当前作用域中不存在的话,就会抛出 referenceError错误。原因就是属性访问调用了对象内置操作[[get]]。

对于[[put]]操作,你可能认为一般是在属性赋值时被调用,但实际情况并不完全这样,触发的原因有很多。不过首先要看对象中是否存在这个属性,如果存在的话,那么一般会检查下面的内容:

1)属性是否是属性描述符,如果是并且存在setter,就会调用setter,什么是setter呢,接下来会讲到;

2)属性描述符writable是否是false,如果是的话,在非严格模式下静默失败,在严格模式下抛出typeError错误;

3)如果上述情况都不是,那么将该值设置为属性值。

如果对象中不存在这个属性,那么[[put]]操作会查找原型链,这个后面会讲到。

四、getter 和  setter 操作

ES5中的getter和setter操作会改变对象属性的默认操作,但是只能应用在单个属性上而不是整个对象上。有的同学会问到,平时好像没有看到这两个方法啊?这是因为它们都是隐藏函数,都是在属性的获取和赋值时默默的调用的。

看下面这段代码:


var myobject = {

      get a(){

      return 2;

      }

      };

      console.log(myobject.a);//2

      Object.defineProperty(myobject,'b',{

      get:function(){

      return this.a*2;

      }

      })

      console.log(myobject.b);//4


上面两种定义getter的方法都使得在访问属性时调用了getter。不过既然有了getter,那么setter也不能少对吧。


var myobject = {

      get a(){

           return this._a;

      },

      set a(val){ 

            this._a = val*2;

           }

      };   

      myobject.a = 2;

      console.log(myobject.a); //4


到此为止,同上篇文章一起对对象的一些基本属性做了系列的讨论和讲解,相信勤奋好学的同学们有了更深刻的认识吧。

上一篇下一篇

猜你喜欢

热点阅读