资讯专栏INFORMATION COLUMN

前端如何定义一个常量

widuu / 3019人阅读

摘要:很多编程语言提供了关键词声明一个常量,在中也是提供了,但是在前端的与其他编程语言不同,其并不意味着声明的变量就是一个常量。所以,在前端中到底如何实现一个常量可以冻结对象,不能新增和删除属性,同时对象已有属性都是不可枚举不可配置不可写。

很多编程语言提供了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, []);
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102535.html

相关文章

  • 前端如何保护共享对象

    摘要:只要保证在开发中没有对共享对象的写入操作,那么发布到线上时肯定也没有写入操作,这时候这个保护方法就是多余的。 什么是共享对象 被多次使用到的同一个对象即为共享对象 比如我们用标准的es模块来写一个导出单位转换的模块 //converter module export default { cmToIn(){ //convert logic } } 当我们在...

    solocoder 评论0 收藏0
  • 一只前端小白的JS note

    摘要:是一个运行在浏览器端的脚本语言现在已经不仅仅局限于浏览器端是一门解释型动态类型的语言解释型指语言在执行时不需要编译,由浏览器自动编译基本语法区分大小写是一种区分大小写的语言定义变量名称一般用小写定义常量名称一般用大写空格和换行中会 JavaScript - JavaScript是一个运行在浏览器端的脚本语言(现在已经不仅仅局限于浏览器端) - JavaScript是一门解释型、动态类型...

    chavesgu 评论0 收藏0
  • 【进阶1-5期】JavaScript深入之4类常见内存泄漏及如何避免

    摘要:本期推荐文章类内存泄漏及如何避免,由于微信不能访问外链,点击阅读原文就可以啦。四种常见的内存泄漏划重点这是个考点意外的全局变量未定义的变量会在全局对象创建一个新变量,如下。因为老版本的是无法检测节点与代码之间的循环引用,会导致内存泄漏。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题...

    red_bricks 评论0 收藏0
  • 前端知识点总结——JS基础

    摘要:语法常量名值常量名在程序中,通常采用大写形式。结果为类型作用表示使用的数据不存在类型只有一个值即当声明的变量未赋值未初始化时,该变量的默认值就是类型用于表示不存在的对象。结果为按位或,对小数取整将任意小数与做按位或,结果则取整数部分。 前端知识点总结——JS基础 1.javascript概述(了解) 1.什么是javascript javascript简称为js,是一种运行于js解释器...

    wangbjun 评论0 收藏0
  • 前端代码规范笔记

    摘要:代码规范编码基本要求缩进量为个字节循环分支层次不要超过层空行和空白字符也是一种特殊注释判断条件,多用保证类型和值者一致所有变量在调用前必须被初始化对所有的用户输入,必须进行合法性检查。一个方法的圈复杂度推荐最大为,多了请重构。 javaScript 代码规范 编码基本要求 1缩进量 tab为4个字节 2循环、分支层次不要超过 3 层3空行和空白字符也是一种特殊注释4判断条件,多用...

    李增田 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<