美文网首页
在JS中如何如何定义一个常量对象

在JS中如何如何定义一个常量对象

作者: 孤星伴明月 | 来源:发表于2018-11-24 20:45 被阅读0次

本文讨论如何定义一个常量对象。解释了const关键字的用法,用Object.freeze() 和 Proxy() 给出了一个定义常量对象的解决方案。

1. 需求

对于给定的常量如下:

const constSettings = {
    appName:"fan",
    info: {p1:200,p2:300 }
};

要求:

  1. 不能添加属性
constSettings.other = "abc";  // 报错
  1. 不能修改属性
constSetting.appName = "good"; // 报错
  1. 不能修改属性的属性
constSetting.info.p1 = 200;   // 报错

报错的意思是你的设置是不会生效的,并且抛出一个主动抛出一个Error让整个代码终止。

2.理解const

const 是用来定义常量的关键字。但它只是规定变量中保存的的地址值是不能修改的,而不是变量的值不能修改。

2.1 赋值运算符 =

const varName = initValue;

上面这句代码到底发生了什么事?

  1. 在内存中找个地方(房间)保存initValue的真实值。
  2. 把内存地址(房间号)保存在varName中。

const关键字不容许你修改varName中保存的房间号,但是,你可以修改房间中initValue的真实值。

下面举两个例子。

2.1.1 const对基本数据类型是生效

const a = 1;
a = 2; // 报错,达到了const的效果

分析:
第二句代码a = 2可以这样理解:

  1. 在内存中找个地方(房间)保存2。
  2. 把内存地址(房间号)保存在varName中。

注意,这里的第二步是不被const允许的,所以报错了。

2.1.2 const对引用数据类型是无效

const obj = {name:"a"};
obj = "a"      // 报错,达到了const的效果
obj.name = "b" // 不报错,成功地修改了obj的属性

分析:

第二句代码obj = "a"可以这样理解:

  1. 在内存中找个地方(房间)保存a。
  2. 把内存地址(房间号)保存在varName中。

注意,这里的第二步是不被const允许的,所以报错了.

第三句代码obj.name = "b"可以这样理解:

  1. 在内存中找个地方(房间)保存"b"。
  2. 把内存地址(房间号)保存在obj.name中。const只是规定了obj的地址不能修改,并没有规定它的属性的地址不能修改。

3. Object.freeze() 冻结整个对象

Object.freeze()方法可以冻结一个对象。具体来说冻结指的是:

  • 不能向这个对象添加新的属性
  • 不能修改其已有属性的值
  • 不能删除已有属性
  • 以及不能修改该对象已有属性的可枚举性、可配置性、可写性。

具体用法参考:这里

注意:
这个方法返回传递的对象,而不是创建一个被冻结的副本。或者说它直接修改了入参(参照理解 Array的reverse方法)。

3.1 Object.freeze()的用法示例

const constSettings = {
    appName:"fan",
    info: {p1:200,p2:300 }
};
Object.freeze(constSettings);

constSettings.appName = 1 ; // 悄悄地无效

constSettings.other = "abc"; // 悄悄地无效
constSettings.info.p1 = 100; // 生效了

console.info(constSettings)// {appName:"fan",info:{p1:100,p2:300}


注意:

  1. 不需要额外定义一个常量,写 const obj = Object.freeze(constSettings) 。 freeze()会直接修改入参。
  2. 添加other属性,修改appName 没有显示地报错误,也没有成功。
  3. 还是可以修改属性的属性:constSettings.info.p1 = 100; 原因如上所述的const部分。

3.2 递归的Object.freeze()

const constSettings = {
    appName:"fan",
    info: {p1:200,p2:300 }
};
Object.freeze(constSettings);

上面的constSettings对象确实被冻住了,但它的属性info的值也是一个对象,而这个对象并没有被冻住,所以你仍然可以通过constSettting.info找到这个对象,再对它的属性做进一步的修改操作。

下面,我们要通过递归,把属性的属性的属性.... 也冻起来(前提是它也是一个对象)。

通过一个函数来完成这个过程。下面的函数deepFreeze来自这里


function deepFreeze(obj) {

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

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

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

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

const constSettings = {
    appName:"fan",
    info: {p1:200,p2:300 }
};

deepFreeze(constSettings);

constSettings.appName = 1// 悄悄地无效
constSettings.other = "abc" // 悄悄地无效
constSettings.info.p1 = 100 // 也悄悄地无效
console.info(constSettings)

下面,我们只剩一件事了: 不要 悄悄地无效,要明确地抛出一个错误! 这两种用户体验是完全的不同的。我更倾向于后者:如果这件事你允许我去做,但我做完了却没有没有得到正确的反馈,那么,还不如不让我做。

4. Proxy 去监听对象的操作

关于Proxy的用法可以参考这里, 也可以去看看我的另一篇文章 。 这里不做详细的介绍了。

4.1 修改属性就报错

回到我们前面的提的需求:对一个常量对象,修改属性的操作是不合法的,要报错。

我们可以代理属性的set操作,在具体的逻辑中,什么事都不做:直接报错!

const constSettings = {
    appName:"fan",
    info: {p1:200,p2:300 }
};

const con = new Proxy(constSettings, {
    set(target,paraName){
    alert(paraName+"  no modify!!") // 直接报错
  }
})

con.appName = "good"

当然,可以更进一步,如果试图访问一个不存在的属性,也报错。这只需要在get之前判断一下即可。

get(target,p){
  if(!target.hasOwnProperty(p)){
    throw new Error(p+ " no exist")
  }
  else{
    return target[p]
  }
},
set(target,p,value){
  throw new Error(p+ " can not be modifiy")
}

5. 封装一个工具函数

最后,封装一个函数来生成真正的常量对象。


function createConst(obj) {

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

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

    // 如果prop是个对象,冻结它
    if (typeof prop == 'object' && prop !== null)
      obj[name] = createConst(prop);
  });

  // 冻结自身(no-op if already frozen)
  Object.freeze(obj);
  
  return new Proxy(obj,{
    get(target,p){
      if(!target.hasOwnProperty(p)){
        throw new Error(p+ "no exist")
      }
      else{
        return target[p]
      }
    },
    set(target,p,value){
      throw new Error("no change"+p)
    }
  })
}


const settings = {
    appName:"fan",
    info: {p1:200,p2:300 }
};

const SETTINGS = createConst(settings)

// SETTINGS 就能够满足我们前面提的要求了。

相关文章

  • 在JS中如何如何定义一个常量对象

    本文讨论如何定义一个常量对象。解释了const关键字的用法,用Object.freeze() 和 Proxy() ...

  • ecshop中ajax的调用原理

    1:首先ecshop是如何定义ajax对象的。 ecshop中的ajax对象是在js/transport.js文件...

  • 1.3 PHP常量与标量

    1. PHP常量的定义 如何定义常量: PHP中的常量需要用define()函数来定义,并且一个常量被定义后就不能...

  • JavaScript面试考点

    1、JS 如何使属性或者对象不能被改变? 1)对象常量 结合writable: false和configurabl...

  • JS定义常量对象

    ES6中新增const关键字,用于声明创建一个值得只读引用。 但其只是规定变量的标识符不能重新分配,例如声明变量是...

  • python的枚举使用

    在开发项目的时候,我们经常要定义常量,在python中我们如何定义常量?答案:同样是枚举 Enum类在pytho...

  • Go基础系列:5. 常量及运算符

    学到什么 什么是常量? 如何定义常量? 常量和变量有什么不同? 如何使用常量? 有哪些运算符? 如何使用运算符? ...

  • 简易版微信小程序跨页面传值

    1.定义一个eventBus.js 2.定义时间module 3.在app.js中定义常量 4.页面中使用

  • 面向对象编程(1)

    1.如何在一个类中定义一些常量,每个对象都可以方便访问这些常量而不用重新构造?2.如果一个函数不涉及到访问修改这个...

  • 如何动态加载js?

    如何动态加载js?如何使用js实现多个html页面访问同一个常量?如何不使用框架,只有Html时,动态加载文件? ...

网友评论

      本文标题:在JS中如何如何定义一个常量对象

      本文链接:https://www.haomeiwen.com/subject/tdoyqqtx.html