冻结(freeze) VS 封闭(seal)

1,609 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

冻结对象 - Object.freeze()

Object.freeze()  方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。(以上来自MDN的解释)

增删查改

上面概念讲那么多,无非就是围绕着对一个对象,我们冻结之后,是否能对其进行增删查改的属性操作。

// 增删查改
var obj = {
  name: '橙某人'
};
Object.freeze(obj);

/**********本身属性**********/
// 增
obj.age = 18;
console.log(obj); // {name: "橙某人"}
// 删
delete obj.name;
console.log(obj); // {name: "橙某人"}
// 查
console.log(obj.name); // 橙某人
// 改
obj.name = 'yd';
console.log(obj); // {name: "橙某人"}

/**********原型属性**********/
// 增 (删查改也可以, 就不写了)
obj.__proto__.age = 18;
console.log(obj); // {name: "橙某人", [[Prototype]]: {age: 18}};
// 覆盖整个原型对象
obj.__proto__ = {}; // 报错: Uncaught TypeError: #<Object> is not extensible

结论:被冻结的对象,无法对本身属性进行增删改,只能查;而对象的原型对象如常,只是不能直接进行覆盖整个原型对象的操作。

返回值

// ES6 环境
var obj = {
  name: '橙某人'
};
var o = Object.freeze(obj);  
console.log(obj === o); // true
console.log(Object.freeze(123)); // 123
console.log(Object.freeze('123')); // '123'
console.log(Object.freeze(true)); // true
console.log(Object.freeze(() => {})); // () => {}

结论:Object.freeze() 方法在 ES6 以上环境返回值为被冻结的对象;但是在 ES5 环境下,参数不是对象的会直接报错。

image.png

(可以随便找一个低版本IE浏览器跑一下)

浅冻结

何为浅冻结?这和浅拷贝是差不多的意思,一般涉及对象的操作,我们都应该要本能的考虑到对象里面镶嵌对象可能会带来的问题。

var obj = {
  name: {
    nickname: '橙某人'
  }
};
var o = Object.freeze(obj);  
obj.name.nickname = 'yd';
console.log(obj); // {name: {nickname: 'yd'}}
obj.name.nickname = 'YD';
console.log(obj); // {name: {nickname: 'YD'}}
delete obj.name.nickname;
console.log(obj); // {name: {}}

结论:被冻结的对象,只有第一层会被冻结,深层次的属性依旧如常,能正常增删查改。

深冻结

既然上面提到了 Object.freeze() 方法只是浅冻结,那么我们要实现完全冻结一个完整对象,应该如何做呢?

Object.deepFreeze = (obj) => {
  // 这里为什么不使用Object.keys(), 因为它无法获取不能枚举的属性, 我们要保证获取到所有的key
  var propNames = Object.getOwnPropertyNames(obj);
  propNames.forEach(function(name) {
    var prop = obj[name];
    if (typeof prop === 'object' && prop !== null) {
      // 递归
      Object.deepFreeze(prop);
    }
  });
  return Object.freeze(obj);
}

var  obj = {
  name: {
    nickname: '橙某人'
  }
};
var o = Object.deepFreeze(obj); 
obj.name.nickname = 'yd';
console.log(obj); // {name: {nickname: '橙某人'}}
obj.name.nickname = 'YD';
console.log(obj); // {name: {nickname: '橙某人'}}
delete obj.name.nickname;
console.log(obj); // {name: {nickname: '橙某人'}}

结论:深冻结可以使用递归处理来自行解决。

密封对象 - Object.seal()

Object.seal() 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。(以上来自MDN的解释)

其实都和 Object.freeze() 方法差不多,但是,它能允许被更改!!!

增删查改

我们依旧用上面的例子,注意看注解的地方。

// 增删查改
var obj = {
  name: '橙某人'
};
Object.seal(obj);

/**********本身属性**********/
// 增
obj.age = 18;
console.log(obj); // {name: "橙某人"}
// 删
delete obj.name;
console.log(obj); // {name: "橙某人"}
// 查
console.log(obj.name); // 橙某人
// 改
obj.name = 'yd';
console.log(obj); // {name: "yd"}  !!! 可以被更改 !!!

/**********原型属性**********/
// 增 (删查改也可以, 就不写了)
obj.__proto__.age = 18;
console.log(obj); // {name: "yd", [[Prototype]]: {age: 18}};
// 覆盖整个原型对象
obj.__proto__ = {}; // 报错: Uncaught TypeError: #<Object> is not extensible

结论:被冻结的对象,无法对本身属性进行增删,只能查改;而对象的原型对象如常,只是不能直接进行覆盖整个原型对象的操作。

返回值

与上面 Object.freeze() 对应结论是完全一致的,就不在细说了。

浅密封

与上面 Object.freeze() 对应结论是完全一致的,就不在细说了。

深密封

Object.deepSeal = (obj) => {
  var propNames = Object.getOwnPropertyNames(obj);
  propNames.forEach(function(name) {
    var prop = obj[name];
    if (typeof prop === 'object' && prop !== null) {
      Object.deepSeal(prop);
    }
  });
  return Object.seal(obj);
}






至此,本篇文章就写完啦,撒花撒花。

image.png

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。