不少编程语言提供了const
关键词声明一个常量,在ES6中也是提供了const
,可是在前端的const
与其余编程语言不一样,其并不意味着声明的变量就是一个常量。使用const b = {}
声明了一个常量b,可是经过使用b.a = 1
去修改对象b却并无报错,咱们修改了一个本来觉得是常量其实是变量的对象。javascript
为何会这样?前端
实际上,const
定义的变量保存的是指向实际数据的指针,对于基本数据类型String、Boolean、Number、undefined、null、Symbol
而言, 其值保存在栈内存中的简单数据段,按值访问,就是等同于常量。可是相对于引用数据类型而言,const
只能保证指向保存在堆内存中的对象的指针保持不变,换句话说 const
可以保证变量始终指向同一个对象,至于对象的修改无能为力。java
因此,在前端中到底如何实现一个常量!es6
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);
}
}
复制代码
该方法能够将对象变为不可扩展即对象即不能添加新的属性,可是对象的原有属性依然能够被删除或修改,同时若是属性的值为对象,尽管设置了 不能被添加属性,可是其属性值为对象的属性依旧能够添加属性。编程语言
举个例子:ui
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.preventExtensions
相比,该方法一样可以将对象变为不能添加新属性,而且该方法禁止删除对象的属性。一样若是属性的值为对象, 属性值依旧能够添加新属性或删除属性。spa
举个例子3d
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(obj, prop, descriptor)
在MVVM中大放异彩,使用其也可以将将对象完整冻结。在写代码以前咱们 先了解下writable、Configurable
须要知道都内容,这才是这次冻结的关键。指针
对象属性的值是否可以被重写,为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
特性表示对象的属性是否能够被删除,以及除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
,请看这篇文章。
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, []);
}
复制代码