『你不知道的Object』之『freeze对象冻结』【JavaScript面试不艰难系列】

Object.freeze方法可以冻结一个对象,一个冻结后的对象再也不能被修改。首先介绍一下基本用法:

语法

Object.freeze(obj)

参数

obj:要被冻结的对象

返回值

被冻结的对象

   let obj={
      a:1,
      b:2
    }
    let newObj=Object.freeze(obj);
    console.log(newObj===obj) // true

也就是说返回的对象跟源对象是同一个引用,并且对象被冻结后是只读的:

  let obj={
      a:1,
      b:2
    }
  Object.freeze(obj);
  console.log(obj.a) // 1

我们可以用Object.isFrozen方法来检测一个对象是否是被冻结过的对象

 let obj={
      a:1,
      b:2
    }
  Object.freeze(obj);
  console.log(Object.isFrozen(obj)) // true

被冻结对象自身的所有属性都不能以任何方式被修改。任何修改尝试都会失败,包括添加、修改、删除:

   let obj={
      a:1,
      b:2
    }
  Object.freeze(obj);
  obj.c=3;
  console.log(obj)  // {a:1,b:2} 冻结了一个对象则不能向这个对象添加新的属性
  
  obj.a=11;
  console.log(obj) // {a:1,b:2} 冻结了一个对象则不能修改这个对象中的属性
  
  delete obj.a
  console.log(obj) // {a:1,b:2} 冻结了一个对象则不能删除已有属性

以上在非严格模式下都不会有报错,但当我们处于严格模式下时:

     "use strict";
     let obj = {
      a: 1,
      b: 2,
    };
    Object.freeze(obj);
    obj.a = 11;
    console.log(obj); // TypeError: Cannot assign to read only property 'a' of object 提示该属性是只读的
    
    obj.c=3;
    console.log(obj) // TypeError: Cannot add property c, object is not extensible 提示该对象是不可扩展的 

修改原型

注意对象一旦被冻结之后该对象的原型也不可以被修改:

    function Test() {
      this.a = 1;
      this.b = 2;
    }
    Test.prototype.c=3
    let obj = new Test();
    Object.freeze(obj);
    obj.__proto__={aaa:111}
    console.log(obj) // Error: #<Test> is not extensible 是不可扩展的

但是原型中的属性却可以被修改,这是因为Object.freeze方法是浅冻结,也就是说只会冻结一层:

    function Test() {
      this.a = 1;
      this.b = 2;
    }
    Test.prototype.c=3
    let obj = new Test();
    Object.freeze(obj);
    obj.__proto__.c=4
    console.log(obj.c) // 4 
    
    // 原型也是一个对象,根据以下结构可以看出,原型里又是一个对象,所以原型对象里的属性是冻结不了的
    // obj:{
    //   a:1,
    //   b:2,
    //   原型:{
    //     c:3
    //   }
    // }

不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。

let obj={
    a:1,
    b:2
}
Object.defineProperty(obj,"a",{
    value:1,
    //enumerable:false
    //configurable:false,
    //writable:false
})
console.log(obj) //  Cannot redefine property: a at Function.defineProperty

通过对象的getset方法也不能修改冻结后的对象

    let obj = {
      a: 1,
      b: 2,
      get() {
        return this.a;
      },
      set(newVal) {
        this.a = newVal;
      },
    };
    Object.freeze(obj);
    obj.a = 11;
    console.log(obj); // {a:1,b:2}

在 ES5 中,如果这个方法的参数不是一个对象(一个原始值),那么它会导致 TypeError。在 ES6 中,非对象参数将被视为要被冻结的普通对象,并被简单地返回。

// ES6
let res=Object.freeze(123); // true
console.log(res); // 123  // true


// ES5
let res=Object.freeze(123);
console.log(res) // TypeError: 123 is not an object

也可以传入数组参数

let arr=[1,2,3];
Object.freeze(arr);
arr.push(4)
console.log(arr) // Cannot add property 3, object is not extensible

// 上述报错是因为数组也是一个特殊的对象,可以转换为以下结构,故也不能被新增、修改、删除
// arr={
//    "0":1,
//    "1":2,
//    "2":3,
//    "3":4
//}

下面来实现一个深冻结:

let obj = {
          a: 1,
          b: 2,
          c: {
            d: 3,
            e: 4,
            f: {
              g: 5,
            },
          },
        };
// 在Object对象上添加一个新的方法
Object.deepFreeze=function(obj){
    let keys=Object.getOwnPropertyNames(obj) // 获取对象自身所有属性(包括不可枚举属性)
    // 递归执行
    if(keys.length>0){
     keys.forEach(name=>{
         if(typeof obj[name] == "object" && obj[name] != null){
             Object.deepFreeze(obj[name])
         }
     })
    }
    return Object.freeze(obj)
}
   Object.deepFreeze(obj);
   // ...同以上新增、修改、删除相同
   obj.c.f.g = 333;
   console.log(obj); // {a:1,b:2,c{d:3,e:4,f{g:5}}} 所有属性都不可被修改

这样一个深度冻结就实现啦!
下次见~

原文链接:https://juejin.cn/post/7214373797691473975 作者:孤独的根号_

(0)
上一篇 2023年3月26日 上午10:11
下一篇 2023年3月26日 上午10:21

相关推荐

发表回复

登录后才能评论