摘要:標(biāo)準(zhǔn)對(duì)象,語(yǔ)義由本規(guī)范定義的對(duì)象。這意味著雖然有,本質(zhì)上依然是構(gòu)造函數(shù),并不能像那樣表演多繼承嵌套類等高難度動(dòng)作。不過(guò)這里的并不是我們所說(shuō)的數(shù)據(jù)類型,而是對(duì)象構(gòu)造函數(shù)。
序
ECMAScript is an object-oriented programming language for performing computations and manipulating computational objects within a host environment.
— 摘自《ECMAScript? 2018 Language Specification》.
最近在看 ES 規(guī)范,越看越覺(jué)得這是一門神奇的語(yǔ)言。如標(biāo)準(zhǔn)所言,ES 是一門面向?qū)ο蟮木幊陶Z(yǔ)言,但它基于 prototype 的 OO 又很非主流。更神奇的是很多 JSer 拿著這門 OOP 來(lái)搞函數(shù)式和過(guò)程式編程,并且還浪的飛起。本文會(huì)圍繞 ES 中的對(duì)象來(lái)展開(kāi),一起探索技術(shù),走近科學(xué)(瞎扯淡,這并不是一篇正兒八經(jīng)的技術(shù)文章)。
創(chuàng)世篇 nullnull是萬(wàn)物之伊始,也是生命的盡頭。null 是一種空靈的狀態(tài),似是非是,似空非空,正如 null == undefined 但是 null !== undefined,看起來(lái)什么都沒(méi)有,其實(shí)是支潛力股。
在 JS 宇宙中,null 是站在原型鏈頂端的男人,是所有對(duì)象原型的盡頭,擁有毀滅一切的能力,看誰(shuí)不爽賦值為 null,這樣黑白無(wú)常在垃圾回收的時(shí)候便可以一波帶走。
后來(lái) JS 的造物主 Brendan 覺(jué)得 null 的能力過(guò)于強(qiáng)大,于是便創(chuàng)造了超級(jí)英雄
undefined 與之抗衡。undefined 誕生的初衷,是為了維護(hù) JS 宇宙秩序,解決錯(cuò)誤處理和類型轉(zhuǎn)換帶來(lái)的問(wèn)題。但是自 undefined 出現(xiàn)之后世界卻變得更加混亂,人類不知道何時(shí)召喚 null 何時(shí)該召喚 undefined。
簡(jiǎn)單的說(shuō),undefined 表示此處應(yīng)該有個(gè)值,但是這個(gè)值還沒(méi)給出來(lái),其實(shí)就是占了個(gè)坑,這個(gè)坑是語(yǔ)言內(nèi)部實(shí)現(xiàn)幫你做的,程序員完全沒(méi)有必要在代碼中顯示返回或者指定某個(gè)變量為 undefined,undefined 的處理完全交給程序?qū)崿F(xiàn)就是了。所以這其實(shí)是個(gè)無(wú)需暴露給用戶的能力,傳說(shuō) Google 爸爸在創(chuàng)建 Dart 宇宙的時(shí)候就去掉了 undefined,只保留了 null。
Object古人信奉五行陰陽(yáng)之說(shuō),認(rèn)為世界由金木水火土五種基本元素構(gòu)成,基本元素構(gòu)成各種物質(zhì),物質(zhì)構(gòu)成世界。在 JS 宇宙中也一樣,基本語(yǔ)言類型構(gòu)成了各種對(duì)象,對(duì)象構(gòu)成了整個(gè) JS 世界,要理解這個(gè)世界,就得從找對(duì)象開(kāi)始。
在 ES5 時(shí)代,對(duì)象分三種:
native object(原生對(duì)象),指語(yǔ)義完全由規(guī)范定義并且不摻雜任何宿主環(huán)境定義的的對(duì)象;
host object(宿主對(duì)象),由執(zhí)行環(huán)境提供,比如瀏覽器的window對(duì)象和history對(duì)象。JS里的對(duì)象不是原生對(duì)象就是宿主對(duì)象。
build-in object(內(nèi)置對(duì)象),由ECMA實(shí)現(xiàn)提供,程序執(zhí)行時(shí)就存在的對(duì)象。所有內(nèi)置對(duì)象都是原生對(duì)象。
這三類對(duì)象相輔相成,亦相克相生。所謂的道生一,一生二,二生三,三生萬(wàn)物,在 JS 的世界觀中,大概就是指的 null 生 Object,Object 生 native & host,native 又分化出buid-in,是為三才,森羅萬(wàn)象。
然而編碼不止,變化不息,在 ES6 時(shí)代,規(guī)范中有關(guān)對(duì)象的劃分又變成了四種:
ordinary object:普通對(duì)象,需要具備了對(duì)象的所有基本內(nèi)置方法。
exotic object:外來(lái)對(duì)象,如果不完全具備標(biāo)準(zhǔn)對(duì)象擁有的基本內(nèi)置方法,就是外來(lái)對(duì)象。JS里的對(duì)象不是普通對(duì)象就是外來(lái)對(duì)象。
standard object:標(biāo)準(zhǔn)對(duì)象,語(yǔ)義由本規(guī)范定義的對(duì)象。
built-in object:內(nèi)置對(duì)象,跟ES5中描述一樣。
對(duì)比來(lái)看,ES5 中對(duì)象是以宿主環(huán)境為條件劃分的,ES6 中則是根據(jù)對(duì)象的基本內(nèi)置方法。這其實(shí)要?dú)w結(jié)于 ES6 跨越性的變化,必然要?jiǎng)訐u到一些基本規(guī)范描述來(lái)?yè)肀ё兓?。所謂的無(wú)極生太極,太極生兩儀,兩儀生四象,在 JS 宇宙中大概,也許,null 就是無(wú)極,Object 就是太極,一內(nèi)一外是兩儀,四象嘛,當(dāng)然就是上面那四種對(duì)象了啦......
當(dāng)然我是在扯淡。
Class在我開(kāi)始用 JS 搬磚之前,是不那么認(rèn)真的先用了 Java 和 C++。所以一般看來(lái),在有對(duì)象之前必先有類,對(duì)象只是類的一個(gè)實(shí)例而已。就好比找女朋友之前先得構(gòu)造一個(gè)理想女票該有的屬性和行為的類,然后再?gòu)脑擃悓?shí)例化一個(gè)對(duì)象:
public class GirlFriend { public static final Integer age = 18; public static final String sex = "女"; public void eat() {} public void shop() {} public void sleep() {} public static void main(String[] args) { GirlFriend honey = new GirlFriend(); } }
這很符合人類的常識(shí),人們喜歡分類,這樣便于組織管理,可以將復(fù)雜的問(wèn)題簡(jiǎn)單化,清晰化。但是,這并不是世界原本的樣子,也不能表現(xiàn)出內(nèi)心最真實(shí)的渴求,只是我們自己一廂情愿的束縛?;蛟S只有當(dāng)你放下類,放下包袱,放棄規(guī)則,放縱去愛(ài),放肆自己,放空未來(lái)......才能享受這盛夏光年激蕩地青春,才會(huì)發(fā)現(xiàn),會(huì)發(fā)現(xiàn)自己喜歡的并不是蘿莉,而是御姐......
這便是 JS 的無(wú)類哲學(xué):世界本無(wú)類,對(duì)象亦無(wú)根,本是弱類型,何處惹塵埃?
然塵世熙攘,你我結(jié)廬于人境,誰(shuí)能不聞車馬喧?能夠做到超脫的畢竟是少數(shù),這不,ES6 中還是引入了類的概念。
不過(guò)在我看來(lái),ES6 的 class 并未動(dòng)搖 JS 無(wú)類對(duì)象的哲學(xué)根基,更像是普渡眾生的炫邁語(yǔ)法糖。畢竟,typeof class GirlFriend {} 返回的并不是一個(gè)類,而是一個(gè) function。這意味著JS雖然有 class,本質(zhì)上依然是構(gòu)造函數(shù),并不能像 Java 那樣表演多繼承、嵌套類等“高難度”動(dòng)作。
這樣也好,讓 JSer 們繼續(xù)做一個(gè)不拘一格的自由主義者。
混沌篇 function VS object有很長(zhǎng)一段時(shí)間,我無(wú)法清晰的理解 function 和 object 之間的曖昧關(guān)系,就像這樣:
Function instanceof Object // 返回 true Object instanceof Function // 依然返回 true
那么問(wèn)題來(lái)了,到底是先有 Object,還是先有 Function ?
按照創(chuàng)世篇的理念來(lái)講,必然是先有 Object 的概念,然后才孕育出 Function,所以 Object 是蛋,F(xiàn)unction 是雞(有關(guān)先有蛋還是先有雞的哲學(xué)問(wèn)題,我是更傾向于先有蛋的。這個(gè)蛋是天地未開(kāi),陰陽(yáng)一體,混沌之道。所謂道生蛋,蛋生雞,雞中有蛋,蛋中有雞,雞又生蛋,蛋又生雞,蛋蛋雞雞,無(wú)窮盡也,說(shuō)的就是這個(gè)道理)。
當(dāng)然我是有理論依據(jù)的,按照 ES2018 中關(guān)于 Object 的描述:
In ECMAScript, an object is a collection of zero or more properties each with attributes that determine how each property can be used—for example, when the Writable attribute for a property is set to false, any attempt by executed ECMAScript code to assign a different value to the property fails. Properties are containers that hold other objects, primitive values, or functions. A primitive value is a member of one of the following built-in types: Undefined, Null, Boolean, Number, String, and Symbol; an object is a member of the built-in type Object; and a function is a callable object. A function that is associated with an object via a property is called a method.
這里明確指出了函數(shù)是可調(diào)用的對(duì)象。對(duì)象本身的定義就是屬性的集合,函數(shù)就是擁有特定屬性的集合。所以從表面上看,確實(shí)是先有對(duì)象的概念才衍生出函數(shù)的概念。然細(xì)思極恐,如果你仔細(xì)研讀標(biāo)準(zhǔn),就會(huì)發(fā)現(xiàn)在對(duì)象誕生之時(shí),函數(shù)其實(shí)已經(jīng)出現(xiàn)了!所以標(biāo)準(zhǔn)中才叫function object:
An ECMAScript function object is an ordinary object and has the same internal slots and the same internal methods as other ordinary objects.
這是一種既滿足先有蛋后有雞又滿足同時(shí)有蛋和雞的量子疊加態(tài)。所以這個(gè)時(shí)候討論誰(shuí)先誰(shuí)后已經(jīng)沒(méi)有意義了,本自同根生,相煎何太急?
況且他們真的是同根生,這是有科學(xué)依據(jù)的:
Object.getPrototypeOf(Function) === Object.getPrototypeOf(Object)
規(guī)范中使用[[prototype]]表示原型,并提供了getPrototypeOf方法來(lái)獲取它,瀏覽器有一個(gè)非標(biāo)準(zhǔn)的實(shí)現(xiàn),可通過(guò)__proto__內(nèi)部屬性來(lái)訪問(wèn),本文圖方便就使用__proto__來(lái)訪問(wèn)。
回到最初的問(wèn)題,正是因?yàn)?Object 和 Function 的[[prototype]]相同,所以 instanceof 才會(huì)返回 true。不過(guò)這里的 Object 并不是我們所說(shuō)的 Object 數(shù)據(jù)類型,而是對(duì)象構(gòu)造函數(shù)。
typeof Function // 返回 "function" typeof Object // 依然返回 "function"
構(gòu)造函數(shù)都有一個(gè)prototype屬性,所有通過(guò)構(gòu)造函數(shù)實(shí)例化的對(duì)象的[[prototype]]都會(huì)指向該構(gòu)造函數(shù)的 prototype 屬性的引用,即:
實(shí)例對(duì)象.__proto__ === 構(gòu)造函數(shù).prototype // ①
所有函數(shù)都是基于 Function 構(gòu)造出來(lái)的,由式①可知:
// Function 和 Object 作為構(gòu)造函數(shù),自然不例外 Function.__proto__ === Function.prototype // ② Object.__proto__ === Function.prototype // ③ 在座的各位函數(shù).__proto__ === Function.prototype // ④
前面也有說(shuō)到,所有對(duì)象的原型都會(huì)指向一個(gè)最基本的太極對(duì)象,太極原型終于無(wú)極。Function 構(gòu)造函數(shù)作為一個(gè)特殊的對(duì)象,自然也有:
Object.prototype.__proto__ === null Object.__proto__.__proto__ === Object.prototype Function.__proto__.__proto__ === Object.prototype // ⑤ Function.prototype.__proto__ === Object.prototype // 可由②⑤推出 在座的各位函數(shù).__proto__.__proto__ === Object.prototype // 可由④⑤推出
明白了上述道理,也就明白了 JS 原型的真諦,可謂玄之又玄,眾妙之門。
prototype chainES 原型鏈的設(shè)計(jì),其實(shí)是非常符合自然法則的。
我們每個(gè)人看似獨(dú)立的個(gè)體,其實(shí)都可以追溯到共同的祖先。就好比 JS 中的對(duì)象看似獨(dú)立,其實(shí)都有著同一個(gè)原型。原型鏈就跟血液一樣,可以遺傳父輩屬性實(shí)現(xiàn)繼承,但是比起血緣關(guān)系,又更像是血繼限界,除了遺傳之外還能進(jìn)化出新的能力。這一點(diǎn),比起基于類的繼承更加靈活,也更符合進(jìn)化論的思想。
但是有一點(diǎn),ES 是單原型單繼承的,這不符合自然規(guī)律?,F(xiàn)實(shí)中孩子一般繼承了父母雙方的基因。試想一下,如果對(duì)象的原型是一個(gè)數(shù)組,可繼承每一個(gè)原型對(duì)象的屬性,那么 JS 世界會(huì)發(fā)生哪些變化?
最直接的就是可以支持多繼承了,但本質(zhì)上不會(huì)有變化,最終都會(huì)上溯到 Object.prototype。不過(guò)查戶口會(huì)變得異常困難。如果要判斷一個(gè)對(duì)象是否具有某個(gè)屬性,要遍歷的就不是原型鏈了,而是原型網(wǎng),這是一個(gè)十分耗時(shí)的操作,所以單繼承雖然喪失了生物的多樣性,卻保持了血統(tǒng)的純正性,讓這門語(yǔ)言可以一直保持簡(jiǎn)單,優(yōu)雅。
嗯,我成功的說(shuō)服了自己,單原型單繼承并不是 JS 的缺陷,而是體現(xiàn) JS 簡(jiǎn)單耐用的神來(lái)之筆,在前端開(kāi)發(fā)場(chǎng)景下,更能突顯出它的優(yōu)勢(shì)。因?yàn)?,老?shí)說(shuō),前端的業(yè)務(wù)場(chǎng)景本就沒(méi)有后端復(fù)雜,沒(méi)必要引入一套復(fù)雜的體系。
然而原型鏈設(shè)計(jì)的最讓我詫異的是,實(shí)例對(duì)象竟然可直接訪問(wèn)并修改原型,從而影響所有其他實(shí)例對(duì)象,不愧是一把原型鏈,連接彼此心,牽動(dòng)你和我:
function GirlFriend() {} // 或者 class GirlFriend {} // 假設(shè)張無(wú)忌同時(shí)談了兩個(gè)女朋友 let zhaoMin = new GirlFriend() let zhouZhiRuo = new GirlFriend() // 某天周芷若黑化跟張無(wú)忌分手了 zhouZhiRuo.breakUp = true // 周芷若一氣之下將其原型也修改了 zhouZhiRuo.__proto__.breakUp = true // 然后趙敏也躺槍了,張無(wú)忌成單身狗了 console.log(zhaoMin.breakUp) // true
這是不是一件非??膳碌氖虑椋∵@肯定是一件非??膳碌氖虑椋≮w敏是無(wú)辜的??!韋小寶該怎么辦?
后來(lái),我在規(guī)范中看到這樣一段描述:
Every ordinary object has a Boolean-valued [[Extensible]] internal slot that controls whether or not properties may be added to the object. If the value of the [[Extensible]] internal slot is false then additional properties may not be added to the object. In addition, if [[Extensible]] is false the value of the [[Prototype]] internal slot of the object may not be modified. Once the value of an object"s [[Extensible]] internal slot has been set to false it may not be subsequently changed to true.
看樣子,只要將原型對(duì)象的內(nèi)部屬性[[Extensible]]設(shè)置為 false 即可防止被子對(duì)象篡改。然而由于是內(nèi)部屬性,并不屬于 ES 語(yǔ)言的一部分,瀏覽器也沒(méi)有像暴露原型一樣將其暴露出來(lái),所以此路不通。另外,即使用 ES6 新增的 class,也無(wú)法避免被子對(duì)象修改的命運(yùn),估計(jì)在后面的
ES 版本中會(huì)加上 class 限定符吧。
難道就沒(méi)有別的辦法了嗎?解鈴還須系鈴人,既然問(wèn)題出在原型上,那么還是得從原型下手。趙敏心想,如果我也直接修改原型上breakUp屬性為 false,那么周芷若也會(huì)回到無(wú)忌哥身邊,干脆一不做二不休:
function Wife() {} zhaoMin.__proto__ = Wife.prototype
從此,張無(wú)忌和趙敏過(guò)上了幸??鞓?lè)的生活。這個(gè)故事有些夸張,但你我身邊,或許就有周芷若和趙敏這樣的人才。
可以說(shuō),ES 的原型鏈設(shè)計(jì)的相當(dāng)自由,它只是提供了一個(gè) playground,至于怎么去寫(xiě),怎么去玩,規(guī)則都可以由你自己定義。ES 設(shè)計(jì)之初的理念就是越簡(jiǎn)單越好,所謂大道至簡(jiǎn),悟在天成,JS 的靈活,得益于它的簡(jiǎn)單,JS 的復(fù)雜,亦歸咎于它的簡(jiǎn)單。
飛升篇慢慢地,我開(kāi)始覺(jué)著 ES 的設(shè)計(jì)理念由內(nèi)到外散發(fā)著一股自由的氣息,在 JS 的世界中,你可以很面向?qū)ο?,也可以很面向過(guò)程,還可以很函數(shù)式;時(shí)而騰云駕霧游九州,時(shí)而不慎跌落終結(jié)谷;有精華亦有糟粕,正如有光明必有陰影。JS 開(kāi)發(fā)路上,可能會(huì)經(jīng)歷人生的大徹大悟,大起大落,但這不正是我們生活的真實(shí)寫(xiě)照嗎?我們要時(shí)刻保持一種包容和謙卑的態(tài)度,去書(shū)寫(xiě)更加優(yōu)雅和睿智的人生,打造屬于前端開(kāi)發(fā)者的未來(lái)。
好了我編不下去了了,先這樣吧。本文是《ECMAScript 2018 標(biāo)準(zhǔn)導(dǎo)讀》中的第一篇番外,感興趣的話可以關(guān)注下,帶著哲思搞技術(shù),你會(huì)發(fā)現(xiàn)編程竟如此有趣。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/83646.html
摘要:標(biāo)準(zhǔn)對(duì)象,語(yǔ)義由本規(guī)范定義的對(duì)象。三個(gè)冒號(hào)作為分隔符分割數(shù)字字符串文法的產(chǎn)生式。所有因?yàn)閹?lái)的問(wèn)題,基本上是占著茅坑不拉屎的行為導(dǎo)致。以數(shù)組測(cè)試操作為例,標(biāo)準(zhǔn)中的描述如下相對(duì)于來(lái)說(shuō),規(guī)范中增加了對(duì)的處理。 前言 本文是對(duì)《ECMAScript 2018 Language Specification》的解讀。本文是對(duì)標(biāo)準(zhǔn)的概述性導(dǎo)讀,不是對(duì) ES2018特性的詳細(xì)描述,也不會(huì)針對(duì)某個(gè)技術(shù)...
摘要:優(yōu)點(diǎn)簡(jiǎn)單粗暴,直接調(diào)用缺點(diǎn)兼容性不太好,不過(guò)的話都支持你可能不知道的前端知識(shí)點(diǎn)對(duì)象和的方法。下面從深層次剖析一下對(duì)于開(kāi)始的兩個(gè)賦值語(yǔ)句,,,相當(dāng)于,而顯然等于。同理可以分析第三個(gè)賦值語(yǔ)句 有些東西很好用,但是你未必知道;有些東西你可能用過(guò),但是你未必知道原理。 實(shí)現(xiàn)一個(gè)目的有多種途徑,俗話說(shuō),條條大路通羅馬。很多內(nèi)容來(lái)自平時(shí)的一些收集以及過(guò)往博客文章底下的精彩評(píng)論,收集整理拓展一波,發(fā)...
摘要:優(yōu)點(diǎn)簡(jiǎn)單粗暴,直接調(diào)用缺點(diǎn)兼容性不太好,不過(guò)的話都支持你可能不知道的前端知識(shí)點(diǎn)對(duì)象和的方法。下面從深層次剖析一下對(duì)于開(kāi)始的兩個(gè)賦值語(yǔ)句,,,相當(dāng)于,而顯然等于。同理可以分析第三個(gè)賦值語(yǔ)句 有些東西很好用,但是你未必知道;有些東西你可能用過(guò),但是你未必知道原理。 實(shí)現(xiàn)一個(gè)目的有多種途徑,俗話說(shuō),條條大路通羅馬。很多內(nèi)容來(lái)自平時(shí)的一些收集以及過(guò)往博客文章底下的精彩評(píng)論,收集整理拓展一波,發(fā)...
摘要:優(yōu)點(diǎn)簡(jiǎn)單粗暴,直接調(diào)用缺點(diǎn)兼容性不太好,不過(guò)的話都支持你可能不知道的前端知識(shí)點(diǎn)對(duì)象和的方法。下面從深層次剖析一下對(duì)于開(kāi)始的兩個(gè)賦值語(yǔ)句,,,相當(dāng)于,而顯然等于。同理可以分析第三個(gè)賦值語(yǔ)句 有些東西很好用,但是你未必知道;有些東西你可能用過(guò),但是你未必知道原理。 實(shí)現(xiàn)一個(gè)目的有多種途徑,俗話說(shuō),條條大路通羅馬。很多內(nèi)容來(lái)自平時(shí)的一些收集以及過(guò)往博客文章底下的精彩評(píng)論,收集整理拓展一波,發(fā)...
閱讀 2809·2021-11-19 11:30
閱讀 3086·2021-11-15 11:39
閱讀 1835·2021-08-03 14:03
閱讀 2017·2019-08-30 14:18
閱讀 2071·2019-08-30 11:16
閱讀 2225·2019-08-29 17:23
閱讀 2631·2019-08-28 18:06
閱讀 2563·2019-08-26 12:22