freeze-js原理系列篇

407 阅读1分钟

原生Object身上带的freeze方法是浅冻结,这里我们写 一个深度冻结。也就是浅冻+递归浅冻。

作用:冻结一个对象,让其不能再 增/减/改 属性。 

也不能修改该对象已有属性的 可枚举性、可配置、可写性、也不能修改已有属性的值和它的原型属性,返回被冻住的传递的对象,而不是创建一个被冻结的副本。

知识储备

- 对象的三大特性:扩展性、密封性、冻结性。

- 扩展性:是否可添加新属性。Object.preventExtensions

- 密封性:不可增减属性,但已有属性可改。Object.seal

- 冻结性:seal基础上不可改值。分浅冻、 深冻。浅冻结:就是只冻结一层。Object.freeze

三个属性对应的判断方法:Object.isExtensible、Object.isSealed、Object.isFrozen 

实现步骤

  1. 先对 对象 进行密封,这样对象就不可以增减属性
  2. 然后改写的属性配置,把是否可写 关闭
  3. 如果子还是对象进一步递归调用自身进行冻结。

代码实现

function _freeze(obj){
    if(obj instanceof Object){
        Object.seal(obj) //1.密封
        
        for(let key in obj){
            if(obj.hasOwnProperty(key)){
                Object.defineProperty(obj,key,{
                    writable:false //2.不可改
                })
            }

            _freeze(obj[key]) //3.递归 深度冻结
        }
    }
}

换一种写法:

代码实现

function deepFreeze(obj) {

  // 取回定义在obj上的属性名
  var propNames = Object.getOwnPropertyNames(obj);

  // 在冻结自身之前冻结属性
  propNames.forEach(function(name) {
    var prop = obj[name];

    // 如果子是个对象,冻结它
    if (typeof prop == 'object' && prop !== null)
      deepFreeze(prop);
  });

  // 冻结自身(no-op if already frozen)
  return Object.freeze(obj);
}

代码测试

let obj = {
    people:{
        name:"macrolam"
    }
} 
_freeze(obj)
obj.people.age = 30

console.log(obj)//age并没有添加成功
console.log(Object.isFrozen(obj) === true);//true 读取是否被冻结