ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ES5严格模式下的保护对象

2020-05-01 10:02:11  阅读:343  来源: 互联网

标签:ES5 false 对象 Object 模式 严格 eid eric 属性


为什么要保护对象

在旧的js中的对象毫无自保能力,对象的结构和属性值可以被任意修改。

比如下面的例子

var eric = {
    eid: 1,
    ename: "xiaoming",
    money: "10",
    car: "自行车",
    ID: "112345677896541236", //隐私信息不能被随意遍历出来
};
console.log(eric);
delete eric.car; //属性car被删除了
eric.money = "10000000"; //属性money的值被修改了
for (const key in object) {
    console.log(object[key]); //结果所有属性值全部暴露出去
}

输出结果
在这里插入图片描述

这样子对象就显得十分的脆弱,定义的对象有点任人宰割的情况,那就需要把对象给保 护起来。其实ES5中,已经提供了保护对象属性和结构的新方法,接下来让我们一起学习一下是如何保护对象。

什么是保护对象

保护对象是指阻止对对象的属性值进行不符合规定的篡改,那ES5是如何保护的呢?
其实,ES5中已经把每个对象的属性,变成了一个缩微的小对象,
如下图:
在这里插入图片描述
是不是像上图画的那样呢?我们来验证一下,我们通过下面代码获得自己的属性的描述信息

var 属性的描述对象=Object.getOwnPropertyDescriptor(对象, "属性名");
<script>
      "use strict"; //记得要开启严格模式
      var eric = {
        eid: 1,
        ename: "xiaoming",
        money: "10",
        car: "自行车",
        ID: "112345677896541236", //隐私信息不能被随意遍历出来
      };
      var obj = Object.getOwnPropertyDescriptor(eric, "eid");
      //var 属性的描述对象=Object.getOwnPropertyDescriptor(对象, "属性名");
      console.log(obj);
    </script>

打印出来结果
在这里插入图片描述

可以看出对象的属性中真的还有一个缩微的小对象,小对象还有4个属性分别为value(值)、writable(可写)、enumerable(可枚举)、configurable(可配置)。介绍一下writable、enumerable、configurable属性

  1. writable:可写,默认值为true,在默认情况下可以修改属性值。如果writable设为false,则该属性值将无法修改,变成只读模式;
  2. enumerable:可枚举,默认值为true,在默认情况下可以被for in 遍历,如果enumerable设为false,则该属性值将无法被for in遍历,但可以被console.log()打印出来,属于半隐藏;
  3. configurable:可配置,默认值为true,在默认情况下该属性可以被删除掉的,如果configurable设为false,那么该属性不可删除,且writable,enumerable,configurable将无法再次修改;

如何保护对象

writable、enumerable、configurable就像是一个个开关一样,如果想对象中的一个属性的值不想被修改,将writable设为false就可以了,其余也是一样的道理。那应该怎么设置呢?我们可以这样设置

Object.defineProperty(对象, "属性名", {
			writable: true或false,
			    ... : ...
			    ... : ...
		})

还是来看跟上面一样的例子

<script>
      "use strict"; //记得要开启严格模式
      var eric = {
        eid: 1, //只读模式
        ename: "xiaoming", //不被遍历
        money: "10", //不能被删除
        car: "自行车", //不能被删除
        ID: "112345677896541236",
      };
      //保护对象,使用Object.defineProperty(对象, "属性名", {}) 这种方法 每次只能保护一个属性
      Object.defineProperty(eric, "eid", {
        writable: false,     //让eric的eid属性的值不能随便被修改
        configurable: false, //不允许再修改writable
      });
      Object.defineProperty(eric, "ename", {
        enumerable: false,   //让eric的ename属性不能随便被for in遍历
        configurable: false, //不允许再修改enumerable
      });
      Object.defineProperty(eric, "money", {
        enumerable: false,    //让eric的ename属性不能随便被for in遍历
        configurable: false, //不允许再修改enumerable
      });
      Object.defineProperty(eric, "car", {
        configurable: false, //不允许删除car属性
      });
      //修改eid的值  结果失败
      eric.eid = 3;
      console.log(eric.eid);
      // 报错  Cannot assign to read only property 'eid' of object 不能给只读属性赋值
      //用for in 遍历出ename
      for (var key in eric) {
        console.log(`${key}:${eric[key]}`);
        //只遍历出了eid car ID
      }
      //删除属性 money
      delete eric.money; //删除失败
      console.log(eric.money);
      //报错 Cannot delete property 'money' of  不能删除属性money
</script>

上面代码运行后会有两处报错,第一个是“不能给只读属性赋值”,第二个是“不能删除属性money”,所以就成功的将对象保护起来不被随意的篡改。
但是,使用上面的代码保护有很明显的缺点,就是每个属性都要设置一次这样代码看起来就很臃肿,也不便于管理,还有没有其他简单点的办法呢?ES5中还有下面的方法可以保护对象

Object.defineProperties(对象, {
			属性名:{
				writable: true或false,
				  ... : ...
			},
			属性名:{
				... : ...
			},
			... : { ... }
		})

还是上面的例子

<script>
      "use strict"; //记得要开启严格模式
      var eric = {
        eid: 1, //只读模式
        ename: "xiaoming", //不被遍历
        money: "10", //不能被删除
        car: "自行车", //不能被删除
        ID: "112345677896541236",
      };
      //保护对象,使用Object.defineProperties(对象, {})
      Object.defineProperties(eric, {
        eid: {
          writable: false, //让eric的eid属性的值不能随便被修改
          configurable: false, //不允许再修改writable
        },
        ename: {
          enumerable: false, //让eric的ename属性不能随便被for in遍历
          configurable: false, //不允许再修改enumerable
        },
        money: {
          enumerable: false, //让eric的ename属性不能随便被for in遍历
          configurable: false, //不允许再修改enumerable
        },
        car: {
          configurable: false, //不允许删除car属性
        },
      });
      //修改eid的值  结果失败
      eric.eid = 3;
      console.log(eric.eid);
      // 报错  Cannot assign to read only property 'eid' of object 不能给只读属性赋值
      //用for in 遍历出ename
      for (var key in eric) {
        console.log(`${key}:${eric[key]}`);
        //只遍历出了eid car ID
      }
      //删除属性 money
      delete eric.money; //删除失败
      console.log(eric.money);
      //报错 Cannot delete property 'money' of  不能删除属性money
    </script>

运行的结果还是跟上面的一样的!也可以实现保护对象的功能,但是,上面的保护功能很弱,不灵活,无法使用自定义的规则保护属性值。如何解决这个问题呢?

给程序的属性请保镖——访问器

  1. 什么是访问器属性: 不实际存储属性值,仅提供对另一个保存数据的属性的保护。
  2. 为什么使用访问器属性: 上面的保护不够灵活,无法用自定义规则灵活保护属性值
  3. 什么时候用到访问器属性:只要用自定义规则,灵活保护属性值时,都用访问器属性。

这个访问器就好比给属性请一个保镖,做什么事情都让保镖先试试水,安全了才执行。看看下面的需求

需求:定义一个对象,对象有个eage属性,要求年龄可以修改,但年龄必须在18-45之间

用访问器该怎么做呢?
现在对象中定义一个_eage属性,利用Object.defineProperties里的enumerable属性设置为false将_age隐姓埋名 — 不想让外人随意使用。再从Object.defineProperties里面“请保镖”eage,用eage来代替_eage属性,在eage里面写 get:function(){ reutrn this.受保护的变量}set:function(value){ … } 其中 getset 不能改变,必须这么写。
来看以下代码

<script>
     //要求年龄可修改,但是年龄必须介于18~65之间
    var eric={
      eid:1001,
      ename:"埃里克",
      _eage:25 //隐姓埋名 —— 不想让外人随意使用
    }
    Object.defineProperties(eric,{
      _eage:{ //半隐藏
        enumerable:false,
        configurable:false
      },
      //请保镖: 
      eage:{//冒名顶替
        //保镖一请就是一对儿
        //专门负责从受保护的属性中,获取属性值
        get:function(){
          console.log("自动调用eage的get()")
          //返回受保护的属性_eage的值
          return this._eage;
        },
        //专门负责将要修改的新值,结果验证后,才保存到受保护的属性中
        set:function(value){
          console.log(`自动调用eage的set(${value})`)
          if(value>=18&&value<=65){
            this._eage=value;
          }else{
            throw Error(`年龄必须介于18~65之间!`)
          }
        },
        enumerable:true, //让eage替_eage抛头露面
        configurable:false //不能轻易删除保镖
        //因为保镖不实际保存属性值,所以没有value属性
        //因为writable开关无法灵活保护属性值,所以保镖也没有writable开关
      }
    
    })
      //观察现在eric的对象结构是怎么样的
   	  console.log(eric)
  </script>

输出的结果

如果外界想要修_eage属性值时,都会先去修改eage这个保镖,如果修改的值符合set规定的规则后,才保存到受保护的属性中,就像下图所示
在这里插入图片描述
在上面的代码的下边加上以下代码

//外界
    //试图读取eric的年龄时
    console.log(eric.eage)
    //试图修改eric的年龄为26
    eric.eage=26;
    console.log(eric.eage)
    //试图修改eric的年龄为-2
    eric.eage=-2;

输出结果:
在这里插入图片描述
这样子就做出这个需求了,那么还有一个问题,上面代码中的set:function({})中的this指向的是谁呢?通过上面打印出了eric其实答案就已经出来了。这里的this是指eric对象。

保护对象的结构

有三种级别

  1. 防扩展: 阻止为对象添加新属性
    在旧js中: 可以随时给对象添加新属性
    如何禁止为对象添加新属性:使用Object.preventExtensions(对象)
    示例: 阻止为对象添加新属性:
<script>
    "use strict";

    var eric={
      eid:1001,
      ename:"埃里克"
    }
    //希望eid只读
    Object.defineProperty(eric,"eid",{
      writable:false,
      configurable:false
    })
    //防止对eric添加新属性: 
    Object.preventExtensions(eric);
    //试图为对象添加新的不同名的eid属性,另起炉灶
    eric.Eid=1003; //报错: 
    //Cannot add property Eid, object is not extensible
    //  不能 添加  属性    Eid(因为)对象是  不   扩展 可以
    //                                    不可扩展
    console.log(eric);
  </script>
  1. 密封:
    什么是密封:既 阻止给对象添加新属性,又阻止删除对象的现有属性
    为什么要用密封:因为几乎对象中的所有属性,都应该是禁止删除的,但是每个属性都要写configurable:false,太麻烦了!所以几乎定义对象都应该使用密封
    如何使用密封:Object.seal(对象)
    原理: seal()做了两件事:
    1). 自动调用Object.preventExtensions()阻止对当前对象的扩展
    2). 自动为每个属性都添加configurable:false,从此我们不需要再手动为每个属性添加configurable:false

示例: 密封一个对象

<script>
    "use strict";

    var eric={
      eid:1001,
      ename:"埃里克"
    }
    //希望eid只读,且不能删除
    //希望ename也不能删除
    Object.defineProperties(eric,{
      eid:{
        writable:false,
        //configurable:false
      },
    })
    //密封对象: 
    Object.seal(eric)

    //试图添加新属性: 
    //eric.Eid=1003;//报错: 
    //Cannot add property Eid, object is not extensible
    //试图删除eid属性
    //delete eric.eid; //报错: Cannot delete property 'eid'
    //试图删除ename属性 
    //delete eric.ename; //报错: Cannot delete property 'ename'
    console.log(eric);
  </script>
  1. 冻结:
    什么是冻结: 既不能添加删除现有属性,又不能修改属性值,是一种十分严格的保护模式。
    什么时候用: 如果多个模块共用的对象,就不应该让某一个模块擅自修改对象的属性值,一旦修改,牵一发而动全身。
    怎么用:Object.freeze(对象)
    原理: 做了三件事:
    1). 也自动调用preventExtensions()阻止添加新属性
    2). 也自动为每个属性添加configurable:false
    3). 自动设置每个属性的writable:false
    示例: 冻结对象
<script>
    "use strict";

    var obj={
      host:"192.168.0.100",
      port:3306,
      db:"xz"
    }
    //希望obj对象中所有属性,禁止修改,禁止删除
    //且禁止给obj添加新属性
    Object.freeze(obj);

    //尝试给obj添加新属性: 
    //obj._host="127.0.0.1"; //报错: Cannot add property _hosts
    //尝试删除obj中现有属性
    //delete obj.host; //报错: Cannot delete property 'host'
    //尝试修改obj中的host属性值: 
    //obj.host="localhost"; //报错: Cannot assign to read only property 'host'
  </script>
东哥笔记

标签:ES5,false,对象,Object,模式,严格,eid,eric,属性
来源: https://blog.csdn.net/weixin_43348064/article/details/105814030

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有