詳解JavaScript常量定義
相信同學(xué)們?cè)诳匆娺@個(gè)標(biāo)題的時(shí)候就一臉懵逼了,什么?JS能常量定義?別逗我好嗎?確切的說,JS當(dāng)中確實(shí)沒有常量(ES6中好像有了常量定義的關(guān)鍵字),但是深入一下我們可以發(fā)現(xiàn)JS很多不為人知的性質(zhì),好好利用這些性質(zhì),就會(huì)發(fā)現(xiàn)一個(gè)不一樣的JS世界。
首先,在JS當(dāng)中,對(duì)象的屬性其實(shí)還含有自己的隱含性質(zhì),比如下面對(duì)象:
var obj = {}; obj.a = 1; obj.b = 2;
在這里我們定義了一個(gè)對(duì)象 obj ,并且定義了這個(gè)對(duì)象的兩個(gè)屬性 a 、 b ,我們可以修改這兩個(gè)屬性的值,可以用 delete 關(guān)鍵字刪除這兩個(gè)屬性,也可以用 for ... in ... 語句枚舉 obj 對(duì)象的所有屬性,以上的這些操作叫做對(duì)象屬性的性質(zhì),在我們平常編寫代碼的時(shí)候我們會(huì)不知不覺的默認(rèn)了這些性質(zhì),把他們認(rèn)作為JS應(yīng)有的性質(zhì),殊不知這些性質(zhì)其實(shí)是可以修改的。我通常的定義的屬性的方法,默認(rèn)了屬性的性質(zhì),不過我們也可以在定義屬性的時(shí)候修改屬性的性質(zhì),比如:
var obj = {}; obj.a = 1; obj.b = 2; //等價(jià)于 var obj = { a: 1, b: 2 } //等價(jià)于 var obj = {}; Object.defineProperty(obj, "a", { value: 1, //初始值 writable: true, //可寫 configurable: true, //可配置 enumerable: true //可枚舉 }); Object.defineProperty(obj, "b", { value: 2, //初始值 writable: true, //可寫 configurable: true, //可配置 enumerable: true //可枚舉 });
這里涉及到了一個(gè)方法,Object.defineProperty(),該方法是ES5規(guī)范中的,該方法的作用是在對(duì)象上定義一個(gè)新屬性,或者修改對(duì)象的一個(gè)現(xiàn)有屬性,并對(duì)該屬性加以描述,返回這個(gè)對(duì)象,我們來看一下瀏覽器兼容性:
特性 | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
基本支持 | 4.0 (2) | 5 | 9 [1] | 11.60 | 5.1 [2] |
還是天煞的IE8,如果你的項(xiàng)目要求兼容IE8,那么這個(gè)方法也就不適用了,不過IE8也對(duì)該方法進(jìn)行了實(shí)現(xiàn),只能在DOM對(duì)象上適用,而且有一些獨(dú)特的地方,在這里就不講解了。
Object.defineProperty() 方法可以定義對(duì)象屬性的數(shù)據(jù)描述和存儲(chǔ)描述,這里我們只講數(shù)據(jù)描述符,不對(duì)存儲(chǔ)描述符講解,數(shù)據(jù)描述符有以下選項(xiàng):
configurable 當(dāng)且僅當(dāng)該屬性的 configurable 為 true 時(shí),該屬性描述符才能夠被改變,也能夠被刪除。默認(rèn)為 false。 enumerable 當(dāng)且僅當(dāng)該屬性的 enumerable 為 true 時(shí),該屬性才能夠出現(xiàn)在對(duì)象的枚舉屬性中。默認(rèn)為 false。 value 該屬性對(duì)應(yīng)的值??梢允侨魏斡行У?JavaScript 值(數(shù)值,對(duì)象,函數(shù)等)。默認(rèn)為 undefined。 writable 當(dāng)且僅當(dāng)該屬性的 writable 為 true 時(shí),該屬性才能被賦值運(yùn)算符改變。默認(rèn)為 false。
注意,當(dāng)我們用常規(guī)方法定義屬性的時(shí)候,其除 value 以外的數(shù)據(jù)描述符默認(rèn)均為 true ,當(dāng)我們用 Object.defineProperty() 定義屬性的時(shí)候,默認(rèn)為 false。
也就是說,當(dāng)我們把 writable 設(shè)置為 false 的時(shí)候,該屬性是只讀的,也就滿足了常量了性質(zhì),我們把常量封裝在CONST命名空間里面:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, writable: false, //設(shè)置屬性只讀 configurable: true, enumerable: true }); console.log(CONST.A); //1 CONST.A = 2; //在嚴(yán)格模式下會(huì)拋錯(cuò),在非嚴(yán)格模式下靜默失敗,修改無效。
但是這樣定義的常量不是絕對(duì)的,因?yàn)槲覀円廊豢梢酝ㄟ^修改屬性的數(shù)據(jù)描述符來修改屬性值:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, writable: false, configurable: true, enumerable: true }); Object.defineProperty(CONST, "A", { value: 2, writable: true, //恢復(fù)屬性的可寫狀態(tài) configurable: true, enumerable: true }) console.log(CONST.A); //2 CONST.A = 3; console.log(CONST.A); //3
想要做到真正的常量,還需要將屬性設(shè)置為不可配置:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, writable: false, //設(shè)置屬性只讀 configurable: false, //設(shè)置屬性不可配置 enumerable: true }); console.log(CONST.A); //1 CONST.A = 2; //錯(cuò)誤!屬性只讀 Object.defineProperty(CONST, "A", { value: 2, writable: true, configurable: true, enumerable: true }); //錯(cuò)誤!屬性不可配置
但是如果只設(shè)置屬性為不可配置狀態(tài),依然可以對(duì)屬性值進(jìn)行修改:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, writable: true, //設(shè)置可寫 configurable: false, //設(shè)置屬性不可配置 enumerable: true }); console.log(CONST.A); //1 CONST.A = 2; console.log(CONST.A); //2
進(jìn)而我們可以推斷出,configurable 描述符僅凍結(jié)屬性的描述符,不會(huì)對(duì)屬性值產(chǎn)生影響,也就是說該描述符會(huì)凍結(jié) writable、configurable、enumerable 的狀態(tài),不會(huì)對(duì)屬性值加以限制:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, writable: false, //設(shè)置不可寫 configurable: false, //設(shè)置屬性不可配置 enumerable: false //設(shè)置不可枚舉 }); Object.defineProperty(CONST, "A", { value: 2, //該屬性本身不受 configurable 的影響,但由于屬性不可寫,受 writable 的限制 writable: true, //錯(cuò)誤!屬性不可配置 configurable: true, //錯(cuò)誤!屬性不可配置 enumerable: true //錯(cuò)誤!屬性不可配置 });
但是 configurable 的限制有一個(gè)特例,就是 writable 可以由 true 改為 false,不能由 false 改為 true:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, writable: true, //設(shè)置可寫 configurable: false, //設(shè)置屬性不可配置 enumerable: false //設(shè)置不可枚舉 }); Object.defineProperty(CONST, "A", { value: 2, //該屬性本身不受 configurable 的影響,由于屬性可寫,修改成功 writable: false, configurable: false, enumerable: false }); console.log(CONST.A); //2 CONST.A = 3; //錯(cuò)誤!屬性只讀
可枚舉描述符用于配置屬性是否可以枚舉,也就是是否會(huì)出現(xiàn)在 for ... in ... 語句中:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, writable: false, configurable: false, enumerable: true //可枚舉 }); Object.defineProperty(CONST, "B", { value: 2, writable: false, configurable: false, enumerable: false //不可枚舉 }); for (var key in CONST) { console.log(CONST[key]); //1 };
有了以上的基礎(chǔ),我們也就學(xué)會(huì)一種定義常量的方法,使用屬性的數(shù)據(jù)描述符,下次我們需要用到常量的時(shí)候,就可以定義一個(gè) CONST 命名空間,將常量封裝在該命名空間里面,由于屬性描述符默認(rèn)為 false,所以我們也可以這樣定義:
var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, enumerable: true }); Object.defineProperty(CONST, "B", { value: 2, enumerable: true });
以上方法是從屬性的角度的去定義一組常量,不過我們還可以用另外一種方法,從對(duì)象的角度去配置一個(gè)對(duì)象包括它的所有屬性,Object.preventExtensions() 方法可以讓一個(gè)對(duì)象不可擴(kuò)展,該對(duì)象無法再添加新的屬性,但是可以刪除現(xiàn)有屬性:
var CONST = {}; CONST.A = 1; CONST.B = 2; Object.preventExtensions(CONST); delete CONST.B; console.log(CONST); //CONST: { A: 1} CONST.C = 3; //錯(cuò)誤!對(duì)象不可擴(kuò)展
在該方法的基礎(chǔ)之上,我們可以使用 Object.seal() 來對(duì)一個(gè)對(duì)象密封,該方法會(huì)阻止對(duì)象擴(kuò)展,并將該對(duì)象的所有屬性設(shè)置為不可配置,但是可寫:
var CONST = {}; CONST.A = 1; CONST.B = 2; Object.seal(CONST); CONST.A = 3; console.log(CONST.A); //3 Object.defineProperty(CONST, "B", { value: 2, writable: true, configurable: true, //錯(cuò)誤!屬性不可配置 enumerable: false, //錯(cuò)誤!屬性不可配置 }) CONST.C = 3; //錯(cuò)誤!對(duì)象不可擴(kuò)展
也就是說 Object.seal() 方法相當(dāng)于幫助我們批量的將屬性的可配置描述符設(shè)置為 false ,所以說在代碼實(shí)現(xiàn)層面相當(dāng)于:
Object.seal = function (obj) { Object.preventExtensions(obj); for (var key in obj) { Object.defineProperty(obj, key, { value: obj[key], writable: true, configurable: false, enumerable: true }) }; return obj; }
在以上兩個(gè)方法基礎(chǔ)上,我們可以 Object.freeze() 來對(duì)一個(gè)對(duì)象進(jìn)行凍結(jié),實(shí)現(xiàn)常量的需求,該方法會(huì)阻止對(duì)象擴(kuò)展,并凍結(jié)對(duì)象,將其所有屬性設(shè)置為只讀和不可配置:
var CONST = {}; CONST.A = 1; CONST.B = 2; Object.freeze(CONST); CONST.A = 3; //錯(cuò)誤!屬性只讀 Object.defineProperty(CONST, "B", { value: 3, //錯(cuò)誤!屬性只讀 writable: true, //錯(cuò)誤!屬性不可配置 configurable: true, //錯(cuò)誤!屬性不可配置 enumerable: false, //錯(cuò)誤!屬性不可配置 }) CONST.C = 3; //錯(cuò)誤!對(duì)象不可擴(kuò)展
從代碼實(shí)現(xiàn)層面上相當(dāng)于:
Object.freeze = function (obj) { Object.preventExtensions(obj); for (var key in obj) { Object.defineProperty(obj, key, { value: obj[key], writable: false, configurable: false, enumerable: true }) }; return obj; }
最后我們?cè)趤砜匆幌逻@三個(gè)方法的兼容性:
Object.preventExtensions()
Feature | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Basic support | 4 (2.0) | 6 | 9 | 未實(shí)現(xiàn) | 5.1 |
Object.seal()
Feature | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Basic support | 4 (2.0) | 6 | 9 | 未實(shí)現(xiàn) | 5.1 |
Object.freeze()
Feature | Firefox (Gecko) | Chrome | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Basic support | 4.0 (2) | 6 | 9 | 12 | 5.1 |
到底還是萬惡的IE,均不兼容IE8
現(xiàn)在,我們也就有了兩種方法在JS中定義常量,第一種方法是從屬性層面上來實(shí)現(xiàn),在命名空間上可以繼續(xù)添加多個(gè)常量,而第二種方法是從對(duì)象層面上來實(shí)現(xiàn),對(duì)凍結(jié)對(duì)象所有屬性以及對(duì)象本身:
//第一種方法:屬性層面,對(duì)象可擴(kuò)展 var CONST = {}; Object.defineProperty(CONST, "A", { value: 1, enumerable: true }); //第二種方法:對(duì)象層面,對(duì)象不可擴(kuò)展 var CONST = {}; CONST.A = 1; Object.freeze(CONST);
關(guān)于JS常量的問題就講到這里了,許多書籍在介紹JS基礎(chǔ)的時(shí)候都會(huì)提到JS當(dāng)中沒有常量,導(dǎo)致許多JS開發(fā)者在一開始就默認(rèn)了JS是沒有常量的這一說法。從嚴(yán)格語法意義上來講,JS確實(shí)是沒有常量的,但是我們可以通過對(duì)知識(shí)的深入和創(chuàng)造力來構(gòu)建我們自己的常量,知識(shí)是死的,人是活的,只要我們不停的探索,滿懷著創(chuàng)造力,就會(huì)發(fā)現(xiàn)其中不一樣的世界。
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)無限級(jí)遞歸樹的示例代碼
這篇文章主要介紹了JavaScript實(shí)現(xiàn)無限級(jí)遞歸樹的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03javascript中傳統(tǒng)事件與現(xiàn)代事件
本文給大家介紹的是使用傳統(tǒng)事件的方法來模擬現(xiàn)代事件,十分的簡單實(shí)用,有需要的小伙伴可以參考下。2015-06-06JavaScript組合設(shè)計(jì)模式--改進(jìn)引入案例分析
這篇文章主要介紹了JavaScript組合設(shè)模式改進(jìn)引入案例,結(jié)合實(shí)例形式分析了JavaScript組合設(shè)計(jì)模式特性改進(jìn)的引入示例相關(guān)操作技巧,需要的朋友可以參考下2020-05-05基于JS+HTML實(shí)現(xiàn)彈窗提示是否確認(rèn)提交功能
這篇文章主要介紹了基于JS+HTML實(shí)現(xiàn)彈窗提示是否確認(rèn)提交功能,需要的朋友可以參考下2020-06-06微信小程序開發(fā)實(shí)用技巧之?dāng)?shù)據(jù)傳遞和存儲(chǔ)
數(shù)據(jù)傳遞與存儲(chǔ)是我們?cè)谌粘i_發(fā)中遇到的再正常不過的一個(gè)需求, 這篇文章主要給大家介紹了關(guān)于微信小程序開發(fā)實(shí)用技巧之?dāng)?shù)據(jù)傳遞和存儲(chǔ)的相關(guān)資料,需要的朋友可以參考下2021-05-05使用JS在瀏覽器中判斷當(dāng)前網(wǎng)絡(luò)連接狀態(tài)的幾種方法
本篇文章主要介紹了使用JS在瀏覽器中判斷當(dāng)前網(wǎng)絡(luò)狀態(tài)的幾種方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05JS給swf傳參數(shù)的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄狫S給swf傳參數(shù)的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09