不少編程語言提供了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, []);
}
複製代碼