摘要:王國維在人間詞話里談到了治學(xué)經(jīng)驗,他說古今之成大事業(yè)大學(xué)問者,必經(jīng)過三種之境界。其中談到中凍結(jié)一個對象幾種由淺入深的實踐。王國維已先自表明,吾人可以無勞糾葛。總結(jié)本文先后介紹了關(guān)于凍結(jié)一個對象的三種進階方法。
王國維在《人間詞話》里談到了治學(xué)經(jīng)驗,他說:“古今之成大事業(yè)、大學(xué)問者,必經(jīng)過三種之境界。”
巧合的是,最近受 git chat / git book 邀請,做了一個分享。
其中談到JS中凍結(jié)一個對象幾種由淺入深的實踐。想想也暗合國學(xué)大師所謂的三重境界。
這篇文章由淺入深討論JS中對象的一些鎖定特性。但都是一些基礎(chǔ)語法的實現(xiàn),相信即便是前端小白也可以大體領(lǐng)會。不過需要讀者預(yù)先了解JS中對象的特性,尤其是對象自身屬性的描述符:configurable、writable...
另外,如果您對JS中對象操作、不可變數(shù)據(jù)、函數(shù)式編程感興趣,同樣推薦我的其他一些相關(guān)文章:
如何優(yōu)雅安全地在深層數(shù)據(jù)結(jié)構(gòu)中取值
從JS對象開始,談一談“不可變數(shù)據(jù)”和函數(shù)式編程
解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場景數(shù)據(jù)設(shè)計
等等。
昨夜西風(fēng)凋碧樹 獨上高樓 望盡天涯路第一種境界:“昨夜西風(fēng)凋碧樹,獨上高樓,望盡天涯路。”
該詞句出自晏殊的《蝶戀花》,原意是說,“我”上高樓眺望所見的更為蕭颯的秋景,西風(fēng)黃葉,山闊水長,案書何達?
王國維此句中解成:做學(xué)問成大事業(yè)者,首先要有執(zhí)著的追求,登高望遠,瞰察路徑,明確目標(biāo)與方向,了解事物的概貌。
我們就從最基本的場景說起,究竟為什么要凍結(jié)一個對象?
場景一:
我們造了一個輪子,對外暴露一個對象,開放出來給第三方使用。同時需要保證這個對外暴露的對象完全安全,不能被業(yè)務(wù)代碼所改寫覆蓋或下鉤子(hook)函數(shù)。
場景二:
如果你看過Vue 2.* 版本源碼,你會發(fā)現(xiàn)凍結(jié)一個對象的操作頻繁出現(xiàn)。
我們先來看凍結(jié)對象的第一層實現(xiàn) —— 擴展特性鎖:
他包含了兩個基本方法:
Object.isExtensible
Object.preventExtensions
如果一個對象可以添加新的屬性,則這個對象是可擴展的。擴展特性鎖就是讓這個對象變的不可擴展,也就是不能再有新的屬性。
Object.isExtensibleMDN上內(nèi)容概述:
概述 Object.isExtensible() 方法判斷一個對象是否是可擴展的(是否可以在它上面添加新的屬性)。 語法 Object.isExtensible(obj) 參數(shù) obj 需要檢測的對象
例如,我們正常使用對象字面量聲明的對象都是可擴展的:
var person1 = {}; person1.name = "Lucas"; console.log(person1); // {name: "Lucas"}
同時:
Object.isExtensible(person1) === true; // true
你可能要問了,那么使用Object.create方法聲明的對象,并對該對象屬性進行配置是什么情況呢?
我們知道,用上面對象字面量聲明的對象相當(dāng)于:
var person1 = Object.create({},{ "name":{ value : "Lucas", configurable : true, //不可配置 enumerable : true , //可枚舉 writable : true //可寫 } });
即便嘗試將configurable設(shè)置為false:
var person1 = Object.create({},{ "name":{ value : "Lucas", configurable : false, //不可配置 enumerable : true, //可枚舉 writable : true //可寫 } });
仍然得到:
Object.isExtensible(person1) === true; // trueObject.preventExtensions
當(dāng)然,我們還是有方法可以使得一個對象變的不可擴展。
MDN上內(nèi)容概述:
概述 Object.preventExtensions() 方法讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性。 語法 Object.preventExtensions(obj) 參數(shù) obj 將要變得不可擴展的對象
幾個注意點包括但不限于:
不可擴展的對象的屬性通常仍然可以被刪除。
嘗試給一個不可擴展對象添加新屬性的操作將會失敗,不過可能是靜默失敗,也可能會拋出 TypeError 異常(嚴格模式下)。
Object.preventExtensions 只能阻止一個對象不能再添加新的自身屬性,仍然可以為該對象的原型添加屬性。
比如:
var person1 = { name: "Lucas" } Object.preventExtensions(person1); person1.age = 18; // 非嚴格模式下,這里不會有報錯,屬于靜默失敗 person1.age // undefined // 擴展新屬性失敗了
仍然可以向原型鏈添加屬性:
person1.__proto__.age = 18; person1.age // 18 // 可以從原型鏈上取到
同樣也可以復(fù)寫一些屬性:
person1.name = "Eros"; person1.name // "Eros"
也可以刪除已有屬性:
person1.name; // "Eros", delete person1.name; person1.name; // undefined
通過以上方法,我們實現(xiàn)了對一個對象屬性擴展的凍結(jié)。但是同樣也認識到,這并不是全面的保護:例如可以隨意改動去覆蓋已有屬性,在對象原型鏈上增加屬性也還是難以屏蔽。
衣帶漸寬終不悔 為伊消得人憔悴第二種境界:“衣帶漸寬終不悔,為伊消得人憔悴。”
這引用的是北宋柳永《蝶戀花》最后兩句詞,原詞是表現(xiàn)作者對愛的艱辛和愛的無悔。若把“伊”字理解為詞人所追求的理想和畢生從事的事業(yè),亦無不可。王國維則別有用心,以此兩句來比喻成大事業(yè)、大學(xué)問者,不是輕而易舉,隨便可得的,必須堅定不移,經(jīng)過一番辛勤勞動,廢寢忘食,孜孜以求,直至人瘦帶寬也不后悔。
下面介紹一個更深一層的做法:密封特性。
密封對象是指那些不能添加新的屬性,不能刪除已有屬性,以及不能修改已有屬性的可枚舉性(enumerable)、可配置性(configurable)、可寫性(writable),但可能可以修改已有屬性的值的對象。
他同樣包含了兩個基本方法:
Object.isSealed
Object.seal
Object.isSealedMDN上內(nèi)容概述:
概述 Object.isSealed() 方法判斷一個對象是否是密封的(sealed)。 語法 Object.isSealed(obj) 參數(shù) obj 將要檢測的對象
正常對象字面量聲明的對象是不被密封的:
var person1 = { name: "Lucas" } Object.isSealed(person1); // false
當(dāng)將這個對象禁止擴展時,它也不會變成密封的:
var person1 = { name: "Lucas" } Object.preventExtensions(person1); Object.isSealed(person1); // false
但是在此基礎(chǔ)上,使用Object.defineProperty方法,把屬性變得不可配置(configurable),則這個對象也就成了密封對象:
var person1 = { name: "Lucas" } Object.defineProperty(person1, "name", {configurable : false}); Object.isSealed(person1); // true
此時,我們有:
Object.getOwnPropertyDescriptor(person1, "name"); // 得到: Object { value: "Lucas", writable: true, enumerable: true, configurable: false }
根據(jù)這個getOwnPropertyDescriptor,我們可以更加深入的理解密封特性:被密封的對象,就是在不可擴展基礎(chǔ)上講屬性描述符configurable設(shè)置為false; 同時,被密封的對象,仍然有機會改變屬性的值。只不過對于此對象本身而言,不可以再擴展新的屬性,不可以更改已有屬性的配置信息。
Object.seal相對應(yīng)我們也有一個方法將一個對象密封。
MDN上內(nèi)容概述:
概述 Object.seal() 方法可以讓一個對象密封,并返回被密封后的對象。 語法 Object.seal(obj) 參數(shù) obj 將要被密封的對象
比如:
var person1 = { name: "Lucas" } Object.getOwnPropertyDescriptor(person1, "name"); // 得到: Object { value: "Lucas", writable: true, enumerable: true, configurable: true }
將此對象密封后:
Object.seal(person1); Object.getOwnPropertyDescriptor(person1, "name"); // 得到: Object { value: "Lucas", writable: true, enumerable: true, configurable: false }
也就是說:
person1.age = 18; person1.age; // undefined // 擴展新屬性失敗 // 同時調(diào)用defineProperty失敗 Object.defineProperty(person1,"name",{get : function(){return "g";}}); // 拋出異常
任何除更改屬性值以外的操作,非嚴格模式下都會靜默失敗,如上并如下:
delete person1.name; person1.name; // "Lucas"
而更改屬性值可以成功:
person1.name = "Eros"; person1.name; // "Eros"
怎么理解這樣的現(xiàn)象呢?牢記,被密封的對象擁有如下的屬性描述符:
Object { value: "Lucas", writable: true, enumerable: true, configurable: false }
而刪除屬性屬于configurable,更改屬性才屬于writable;
一點延伸借助于此,我們其實已經(jīng)可以完成凍結(jié)對象的第三重境界:達到即密封又不可修改原屬性值。因為可以這樣做:
var person1 = {name: "Lucas"}; Object.defineProperty(person1, "name", {configurable: false, writable: false}); Object.preventExtensions(o);
總結(jié)下就是設(shè)置:
configurable: false + writable: false + preventExtensions
或者因為
configurable: false+ preventExtensions = seal
所以也可以設(shè)置:
眾里尋他千百度,驀然回首,那人卻在,燈火闌珊處seal + writable: false
第三種境界:“眾里尋他千百度,驀然回首,那人卻在,燈火闌珊處。”
這是引用南宋辛棄疾《青玉案》詞中的最后四句。梁啟超稱此詞“自憐幽獨,傷心人別有懷抱”。這是借詞喻事,與文學(xué)賞析已無交涉。王國維已先自表明,“吾人可以無勞糾葛”。他以此詞最后的四句為“境界”之第三,即最終最高境界。
這雖不是辛棄疾的原意,但也可以引出悠悠的遠意:做學(xué)問、成大事業(yè)者,要達到第三境界,必須有專注的精神。反復(fù)追尋、研究,下足功夫,自然會豁然貫通,有所發(fā)現(xiàn),有所發(fā)明,就能夠從必然王國進入自由王國。
上邊那種凍結(jié)對象的方法,其實也有原生實現(xiàn),可謂:“眾里尋他千百度,驀然回首,那人卻在,燈火闌珊處”
我們這里所說的一個對象的凍結(jié)(frozen)是指它不可擴展,所有屬性都是不可配置的(non-configurable),且所有數(shù)據(jù)屬性(data properties)都是不可寫的(non-writable)。
或者說,凍結(jié)對象是指那些不能添加新的屬性,不能修改已有屬性的值,不能刪除已有屬性,以及不能修改已有屬性的可枚舉性、可配置性、可寫性的對象。也就是說,這個對象永遠是不可變的。
同樣,包含了兩個基本方法:
Object.isFrozen
Object.freeze
Object.isFrozenMDN上內(nèi)容概述:
概述 Object.isFrozen() 方法判斷一個對象是否被凍結(jié)(frozen)。 語法 Object.isFrozen(obj) 參數(shù) obj 被檢測的對象Object.freeze 方法
MDN上內(nèi)容概述:
概述 Object.freeze() 方法可以凍結(jié)一個對象。 語法 Object.freeze(obj) 參數(shù) obj 將要被凍結(jié)的對象
可以先理解為,這是最高一層的凍結(jié)對象:
var person1 = { name: "Lucas" } Object.freeze(person1);
此時,我們有:
Object.getOwnPropertyDescriptor(person1, "name") Object { value: "Lucas", writable: false, enumerable: true, configurable: false } // 對凍結(jié)對象的任何操作都會失敗 person1.name = "Eros"; // 改寫屬性值,非嚴格模式下靜默失敗; person1.age = 18; // 擴展屬性值,非嚴格模式下靜默失敗; Object.defineProperty(person1,"name",{value: "Eros"}); // 使用defineProperty會直接報錯
改寫屬性值,擴展新屬性,調(diào)用defineProperty,全部都會失敗。
但是,這種層面的凍結(jié),只是淺凍結(jié)。如果對象里面還嵌套有對象,那么這個內(nèi)部對象絲毫不受影響。
var person1 = { name: "Lucas", family: { brother: "Eros" } } Object.freeze(person1); person1.family.brother = "Tim"; person1.family.brother // "Tim"終極實現(xiàn)
那么,如果我們想深層次凍結(jié)一個對象呢?思路和深拷貝暗合,使用遞歸:
Object.prototype.deepFreeze = Object.prototype.deepFreeze || function (o){ var prop, propKey; Object.freeze(o); // 首先凍結(jié)第一層對象 for (propKey in o){ prop = o[propKey]; if(!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)){ continue; } deepFreeze(prop); // 遞歸 } }
這樣子,我們再回過頭來看:
var person1 = { name: "Lucas", family: { brother: "Eros" } } Object.deepFreeze(person1); person1.family.brother = "Tim"; person1.family.brother // "Eros"
已經(jīng)達到了深層次對象屬性的凍結(jié)。
總結(jié)本文先后介紹了關(guān)于凍結(jié)一個對象的三種進階方法。他們層層遞進,卻又相互關(guān)聯(lián)。關(guān)系如圖:
文章部分概念粘取了MDN語法介紹和Tomson的文章。
在《文學(xué)小言》一文中,王國維把上述三境界說成“三種之階級”。并說:“未有不閱第一第二階級而能遽躋第三階級者,文學(xué)亦然。此有文學(xué)上之天才者,所以又需莫大之修養(yǎng)也。”
與大家共勉。
Happy coding!
PS: 作者Github倉庫,歡迎通過代碼各種形式交流。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/50851.html
摘要:王國維在人間詞話里談到了治學(xué)經(jīng)驗,他說古今之成大事業(yè)大學(xué)問者,必經(jīng)過三種之境界。其中談到中凍結(jié)一個對象幾種由淺入深的實踐。王國維已先自表明,吾人可以無勞糾葛。總結(jié)本文先后介紹了關(guān)于凍結(jié)一個對象的三種進階方法。 王國維在《人間詞話》里談到了治學(xué)經(jīng)驗,他說:古今之成大事業(yè)、大學(xué)問者,必經(jīng)過三種之境界。 巧合的是,最近受 git chat / git book 邀請,做了一個分享。其中談到J...
摘要:程序員英語提升指南深入理解內(nèi)部機制即將到來,現(xiàn)在該考慮新的打包方案了嘛凍結(jié)對象的人間詞話完美實現(xiàn)究竟有幾層境界關(guān)于響應(yīng)式的另一種思考 程序員英語提升指南 深入理解 Node Stream 內(nèi)部機制 ES6 modules 即將到來,現(xiàn)在該考慮新的打包方案了嘛? JS 凍結(jié)對象的《人間詞話》 完美實現(xiàn)究竟有幾層境界? 關(guān)于響應(yīng)式的另一種思考 Best websites a program...
摘要:從能力上分,一個是搬運工,一個是設(shè)計者能寫代碼是愚公移山為什么說能寫代碼是愚公移山呢我們中國大部分程序員都應(yīng)該處于一個初級程序員的水平,怎么講只有少數(shù)的程序員處于中高級水平。 導(dǎo)語:你知道普通程序員和優(yōu)秀程序員之間的差距嗎?其實答案很簡單,那就是「愚公移山」和「女媧補天」之間的區(qū)別。 之所以提這個話題,跟前兩天在微信群里的討論有關(guān),年后本該是跳槽、找工作的高峰月份,各公司面試邀約應(yīng)該很...
摘要:地址為什么使用遷移學(xué)習(xí)根據(jù)聯(lián)合創(chuàng)始人斯坦福副教授吳恩達介紹,遷移學(xué)習(xí)將會成為機器學(xué)習(xí)商業(yè)成就的下一驅(qū)動力。遷移學(xué)習(xí)是一種機器學(xué)習(xí)技術(shù),允許在特定的數(shù)據(jù)集上再利用已訓(xùn)練的卷積神經(jīng)網(wǎng)絡(luò),并將其調(diào)整或遷移到其他數(shù)據(jù)集。 GitHub 地址:https://github.com/miguelgfierro/sciblog_support/blob/master/A_Gentle_Introducti...
閱讀 2967·2021-11-25 09:43
閱讀 3586·2021-11-24 11:13
閱讀 3354·2021-10-14 09:42
閱讀 2556·2021-09-23 11:53
閱讀 3605·2021-09-22 15:57
閱讀 3221·2021-09-02 09:54
閱讀 3499·2019-08-30 13:47
閱讀 1638·2019-08-29 16:55