浅谈js对象的拓展、密封以及冻结的三大特性

1,293 阅读6分钟

由于最近一直在看Vue的源码,里面有对冻结特性Object.freeze的应用,于是找了资料,发现了以前没有用过的js的新特性,发现以前没用过的东西的感觉真爽,于是决定把他们总结下来。

冻结

一个对象是冻结的(frozen)是指它不可扩展,所有属性都是不可配置的(non-configurable),且所有数据属性(data properties)都是不可写的(non-writable)。

数据属性是值那些没有取值器(getter)或赋值器(setter)的属性。

  • Object.isFrozen 方法
    • 概述 Object.isFrozen() 方法判断一个对象是否被冻结(frozen)。
    • 语法 Object.isFrozen(obj)
    • 描述 一个对象是冻结的(frozen)是指它不可扩展,所有属性都是不可配置的(non-configurable),且所有数据属性(data properties)都是不可写的(non-writable)。数据属性是值那些没有取值器(getter)或赋值器(setter)的属性。
      //一个对象默认是可扩展的,所以他也是非冻结的.
      console.log(Object.isFrozen({}) === false);//true
      //一个非空对象默认也是非冻结的.
      var oneProp = { p:42 };
      console.log(Object.isFrozen(oneProp) === false);//true
    
  • Object.freeze 方法
    • 概述 Object.freeze() 方法可以冻结一个对象。 冻结对象是指那些不能添加新的属性,不能修改已有属性的值,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性的对象。 也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。
    • 语法 Object.freeze(obj)
    • 描述 冻结对象的所有自身属性都不可能以任何方式被修改。 任何尝试修改该对象的操作都会失败,可能是静默失败,也可能会抛出异常(严格模式中)。 数据属性的值不可更改,访问器属性(有getter和setter)也同样(但由于是函数调用,给人的错觉是还是可以修改这个属性)。 如果一个属性的值是个对象,则这个对象中的属性是可以修改的,除非它也是个冻结对象。
      var obj = {
        prop: function() {},
        foo: 'bar'
      };
      // 新的属性会被添加, 已存在的属性可能
      // 会被修改或移除
      obj.foo = 'baz';
      obj.lumpy = 'woof';
      delete obj.prop;
      // 作为参数传递的对象与返回的对象都被冻结
      // 所以不必保存返回的对象(因为两个对象全等)
      var o = Object.freeze(obj);
      
      o === obj; // true
      Object.isFrozen(obj); // === true
      
      // 现在任何改变都会失效
      obj.foo = 'quux'; // 静默地不做任何事
      // 静默地不添加此属性
      obj.quaxxor = 'the friendly duck';
    
    如该方法 MDN 的描述所述,倘若一个对象的属性是一个对象,那么对这个外部对象进行冻结,内部对象的属性是依旧可以改变的,这就叫浅冻结,若把外部对象冻结的同时把其所有内部对象甚至是内部的内部无限延伸的对象属性也冻结了,这就叫深冻结。

密封特性

密封对象是指那些不可 扩展 的,且所有自身属性都不可配置的(non-configurable)对象。

或则说 密封对象是指那些不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性,但可能可以修改已有属性的值的对象。

  • Object.isSealed 方法
    • 概述 Object.isSealed() 方法判断一个对象是否是密封的(sealed)。
    • 语法 Object.isSealed(obj)
    • 描述 如果这个对象是密封的,则返回 true,否则返回 false。
    //新建的对象默认不是密封的
    var empty = {};
    console.log(Object.isSealed(empty) === false);//true

    //如果把一个空对象变得不可扩展,则它同时也会变成个密封对象.
    Object.preventExtensions(empty);
    console.log(Object.isSealed(empty) === true);//true
  • Object.seal 方法
    • 概述 Object.seal() 方法可以让一个对象密封,并返回被密封后的对象。 密封对象是指那些不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性,但可能可以修改已有属性的值的对象。
    • 语法 Object.seal(obj)
    • 描述 通常情况下,一个对象是可扩展的(可以添加新的属性)。 密封一个对象会让这个对象变的不能添加新属性,且所有已有属性会变的不可配置。 属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。 但属性的值仍然可以修改。 尝试删除一个密封对象的属性或者将某个密封对象的属性从数据属性转换成访问器属性,结果会静默失败或抛出TypeError 异常(严格模式)。 不会影响从原型链上继承的属性。但 proto ( ) 属性的值也会不能修改。
    var obj = {             //声明一个对象
        prop:function(){},
        foo:"bar"
    };
    //可以添加新的属性,已有属性的值可以修改,可以删除
    obj.foo = "baz";
    obj.lumpy = "woof";
    delete obj.prop;

    var o = Object.seal(obj);//将 obj 密封,且返回原对象
    console.log(o === obj);//true
    console.log(Object.isSealed(obj) === true);//true

扩展特性

如果一个对象可以添加新的属性,则这个对象是可扩展的。 让这个对象变的不可扩展,也就是不能再有新的属性

  • Object.isExtensible 方法
    • 概述 Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
    • 语法 Object.isExtensible(obj)
//新对象默认是可扩展的无论何种方式创建的对象,这里使用的是字面量方式
var empty = {a:1};
console.log(Object.isExtensible(empty) === true);//true

//等价于 使用属性描述符
empty = Object.create({},{
    "a":{
        value : 1,
        configurable : true,//可配置
        enumerable : true,//可枚举
        writable : true//可写
    }
});
console.log(Object.isExtensible(empty) === true);//true

//对象是否可以扩展与对象的属性是否可以配置无关
empty = Object.create({},{
    "a":{
        value : 1,
        configurable : false,//不可配置
        enumerable : true,//可枚举
        writable : true//可写
    }
});
console.log(Object.isExtensible(empty) === true);//true
  • Object.preventExtensions 方法
    • 概述 Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
    • 语法 Object.preventExtensions(obj)
    • 描述 如果一个对象可以添加新的属性,则这个对象是可扩展的。 preventExtensions 可以让这个对象变的不可扩展,也就是不能再有新的属性。 需要注意的是不可扩展的对象的属性通常仍然可以被删除。 尝试给一个不可扩展对象添加新属性的操作将会失败,不过可能是静默失败,也可能会抛出 TypeError 异常(严格模式)。
      Object.preventExtensions 只能阻止一个对象不能再添加新的自身属性,仍然可以为该对象的原型添加属性。
    var obj = {};
    var obj2 = Object.preventExtensions(obj);
    console.log(obj === obj2);//true

    //新创建的对象默认是可扩展的
    var empty = {};
    console.log(Object.isExtensible(empty) === true);//true
    empty.a = 1;//添加成功

    //将其变为不可扩展对象
    Object.preventExtensions(empty);
    console.log(Object.isExtensible(empty) === false);//true