前端如何定义一个常量

很多编程语言提供了const关键词声明一个常量,在ES6中也是提供了const,但是在前端的const与其他编程语言不同,其并不意味着声明的变量就是一个常量。使用const b = {}声明了一个常量b,但是通过使用b.a = 1去修改对象b却并没有报错,我们修改了一个原本以为是常量实际上是变量的对象。

为什么会这样?

实际上,const定义的变量保存的是指向实际数据的指针,对于基本数据类型String、Boolean、Number、undefined、null、Symbol而言,
其值保存在栈内存中的简单数据段,按值访问,就是等同于常量。但是相对于引用数据类型而言,const只能保证指向保存在堆内存中的对象的指针保持不变,换句话说
const能够保证变量始终指向同一个对象,至于对象的修改无能为力。

所以,在前端中到底如何实现一个常量!

Object.freeze

Object.freeze可以冻结对象,不能新增和删除属性,同时对象已有属性都是不可枚举、不可配置、不可写。需要注意的是使用该方法只能让对象浅冻结,其内部属性为对象时
依然能够被篡改,要想实现完全冻结,那么就需要进行如下操作。

function deepConst(data){
  Object.freeze(data);
  for(let key in data){
    let prop = data[key];
    if(!data.hasOwnProperty(key) || !(typeof prop === "object") || Object.isFrozen(prop)){
      continue;
    }
    deepConst(prop);
  }
}

Object.defineProperty、Object.preventExtensions、Object.seal

Object.preventExtensions

该方法可以将对象变为不可扩展即对象即不能添加新的属性,但是对象的原有属性依然可以被删除或修改,同时如果属性的值为对象,尽管设置了
不能被添加属性,但是其属性值为对象的属性依旧可以添加属性。

举个例子:

let obj = {a:1,b:2,c:{d:3}};
Object.preventExtensions(obj);
obj.d = 1;
obj.a = 2;
delete obj.b;
obj.c.e = 10;
//输出{a:1,c:{d:3,e:10}
console.log(obj);

Object.seal

Object.preventExtensions相比,该方法同样能够将对象变为不能添加新属性,并且该方法禁止删除对象的属性。同样如果属性的值为对象,
属性值依旧可以添加新属性或删除属性。

举个例子

let obj = {a:1,b:2,c:{d:3}};
Object.seal(obj);
obj.e = 10;
delete obj.a;
delete obj.c.d;
obj.c.f = 10;
//输出{a:1,b:2,c:{f:10}
console.log(obj);

Object.defineProperty

Object.defineProperty(obj, prop, descriptor)在MVVM中大放异彩,使用其也能够将将对象完整冻结。在写代码之前我们
先了解下writable、Configurable需要知道都内容,这才是此次冻结的关键。

writable

对象属性的值是否能够被重写,为true表示允许,为false即被禁止,默认为false。如果属性的值为对象,
尽管设置了不能被重写,其属性为对象的值依旧能够被重写。

举个例子:

let obj = {a:1,b:2,c:{d:3}};
Object.defineProperty(obj,"a",{writable:true});
Object.defineProperty(obj,"b",{writable:false});
Object.defineProperty(obj,"c",{writable:false});
Object.defineProperty(obj,"e",{writable:false});
obj.a = 2;
obj.b = 3;
obj.c.d = 4;
//输出为2,即a属性的值被重写了
console.log(obj.a);
//输出依然为2,即b属性的值没有被重写
console.log(obj.b);
//输出依然为{d:4},如果属性的值为对象,尽管设置了不能被重写,其属性为对象的值依旧能够被重写。
console.log(obj.c);
Configurable

configurable特性表示对象的属性是否可以被删除,以及除writable特性外的其他特性是否可以被修改。为true表示允许被修改
false表示禁止修改,默认为false,如果属性的值为对象,尽管设置了属性不能被修改,其属性为对象的属性依旧能够被修改。
举个例子

let obj = {a:1,b:2,c:{d:3}};
Object.defineProperty(obj,"a",{configurable:true});
Object.defineProperty(obj,"b",{configurable:false});
Object.defineProperty(obj,"c",{configurable:false});
delete obj.a;
delete obj.b;
delete obj.c;
//输出 {b:2,c:{}},如果属性的值为对象,尽管设置了属性不能被修改,其属性为对象的属性依旧能够被修改。
console.log(obj);

上面这三个方法单独拿出来并不能够完美的将对象变为一个常量,但是我们组合一下就可以生成一个常量。

function deepConst(data){
  if (!data || typeof data !== 'object') {
    return;
  }
  //Object.preventExtensions(data);也可以实现
  Object.seal(data);
  Object.keys(data).forEach(function(key) {
    unWriteConfig(data, key, data[key]);
  });
}
function unWriteConfig(data, key, val) {
  deepConst(val);
  Object.defineProperty(data, key, {
    writable:false,
    configurable:false
  });
}

Proxy

Proxy在目标对象之前进行了一层拦截,外界对对象的访问和修改都需要通过这层拦截,所以我们可以操控拦截来控制对对象对访问和修改。Proxy
支持的拦截操作众多,下面只列举与文章相关的操作,如果想更深入了解Proxy,请看这篇文章

function createDeepProxy(target) {
  function makeHandler() {
    return {
      set(target, key, value, receiver) {
        return false;
      },
      deleteProperty(target, key) {
        return false;
      }
    }
  }
  
  function proxify(obj, path) {
    for(let key of Object.keys(obj)) {
      if(typeof obj[key] === 'object') {
        obj[key] = proxify(obj[key], [...path, key]);
      }
    }
    let p = new Proxy(obj, makeHandler());
    return p;
  }
  return proxify(target, []);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容