<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【內(nèi)部分享】看向未來(lái) - 近期 TC39 提案匯總

          共 18715字,需瀏覽 38分鐘

           ·

          2021-11-26 20:22

          來(lái)自某不愿具名同學(xué)的投稿,匯總近期 TC39 比較關(guān)鍵的提案,是一篇非常通俗易懂又有料的文章,建議 PC 端觀看~


          最近翻看 TC39,一些期待已久的提案居然已經(jīng)落地了,一些阻塞了很久的提案也有了新的進(jìn)展,這次分享我們就來(lái)匯總介紹,包含近半年全部已經(jīng)落地的提案,以及近期有進(jìn)展、比較有趣或者與開發(fā)相關(guān)的尚未落地的提案。

          TC39:Ecma International's TC39 is a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the definition of JavaScript.

          一、TC39 提案過(guò)程

          回顧一下,TC39 提案過(guò)程分為 Stage 0 ~ 4,共 5 個(gè)階段,分別代表 「起草、提案、草案、候選、完成?!?/strong>

          「Stage 0」

          所有人都可以向 ECMA 262 提出提案,但是必須滿足:

          1. 有 champion (目前中文叫 責(zé)編) 支持
          2. 或者由 來(lái)自 TC39 會(huì)員或已注冊(cè)為 TC39 撰稿人的非會(huì)員提交給 TC39 大會(huì)評(píng)審,且沒有被明確拒絕

          才能進(jìn)入 Stage 0,并收錄到 TC39 倉(cāng)庫(kù):

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposals/blob/master/stage-0-proposals.md

          ? ? ? ? ?

          「Stage 1」

          要想進(jìn)入 Stage 1 必須要滿足 6 個(gè)條件:

          1. 一個(gè) TC39 成員作為 champion
          2. 有一個(gè)完整的當(dāng)前遇到什么問(wèn)題、如何解決的文章
          3. 有使用例
          4. 有 API 設(shè)計(jì)
          5. 有潛在的與其他提案沖突、或是實(shí)現(xiàn)復(fù)雜性、可能遇到的問(wèn)題的考慮
          6. 有一個(gè) repository 來(lái)存放上述這些內(nèi)容 (如果 stage 0 的時(shí)候這個(gè) repo 不在 tc39 組織下,需要 transfer 給 tc39)

          同時(shí)在這個(gè)階段也應(yīng)當(dāng)有 polyfill 或者 demo 用于演示

          「Stage 2」

          Stage 1 這些復(fù)雜要求順利通過(guò),且滿足

          1. 有符合 ECMAScript 的語(yǔ)法、API (此時(shí)可以有 TODO)
          2. 描述盡可能完整 且可供實(shí)現(xiàn)

          后,經(jīng)過(guò) TC39 會(huì)議評(píng)審,就可以推動(dòng)到 Stage 2 了。進(jìn)入這個(gè)階段,意味著這個(gè)提案非常有可能被最終通過(guò)。提案的內(nèi)容也不能大改,只能增量修改。

          「Stage 3」

          當(dāng) Stage 2 留下的 TODO 已經(jīng)補(bǔ)完,所有的語(yǔ)法、API 都完整實(shí)現(xiàn)、reviewer (TC39 指定) 以及全部 ECMAScript 編輯都簽署完成后,TC39 會(huì)議評(píng)審?fù)ㄟ^(guò)就可以推動(dòng)進(jìn)入 Stage 3。此時(shí)瀏覽器會(huì)開始實(shí)現(xiàn)這個(gè)標(biāo)準(zhǔn),并接收用戶反饋。這個(gè)階段的提案文本只能針對(duì)用戶反饋發(fā)現(xiàn)的關(guān)鍵問(wèn)題進(jìn)行修改。

          「Stage 4」

          當(dāng)滿足:

          1. Test262 測(cè)試已經(jīng)準(zhǔn)備就緒并合入測(cè)試集
          2. 至少有 2 個(gè)實(shí)現(xiàn)通過(guò)了 Test262 測(cè)試
          3. 該實(shí)現(xiàn)已經(jīng)有了顯著的實(shí)踐應(yīng)用,比如有 2 個(gè) VM(比如 Node、瀏覽器)已經(jīng)不需要設(shè)置 flag 就能執(zhí)行了
          4. 包含文本的 Merge Request 已經(jīng)提給了 ecma262 倉(cāng)庫(kù)
          5. 所有的 ECMAScript 編輯都批準(zhǔn)了這個(gè) Merge Request

          進(jìn)入 Stage 4 的提案基本已經(jīng)認(rèn)為完成了。會(huì)在下一次發(fā)布時(shí)成為 JavaScript 標(biāo)準(zhǔn)的一部分。

          二、近期關(guān)鍵提案匯總

          下面我們就來(lái)介紹近期的提案。

          2.1 Stage 4 (已落地)

          Stage 4 的提案已經(jīng)確認(rèn)寫入 ECMAScript 標(biāo)準(zhǔn)中,且至少有 2 個(gè)瀏覽器已經(jīng)實(shí)現(xiàn)并通過(guò)測(cè)試。

          2.1.1 .at() (proposal-relative-indexing-method)

          提案鏈接

          https://github.com/tc39/proposal-relative-indexing-method


          ECMAScript

          TypeScript

          Chrome

          Firefox

          Safari

          落地時(shí)間

          版本號(hào)

          August 2021

          2022

          4.6.0

          92

          90

          TP

          取出數(shù)組中的倒數(shù)某個(gè)值,或者字符串中的后數(shù)幾個(gè)字符,在 JavaScript 中一直是比較麻煩的事情,比如 arr[arr.length - 1] 或者 arr.slice(-1)[0]?

          現(xiàn)在,不用這么麻煩了,我們只要用 arr.at(-1) 就好了。

          同時(shí),這個(gè)也給 string、TypedArray 添加了相同的功能。

          Array.prototype.at

          String.prototype.at

          同類提案:

          • proposal-array-last「Stage 1」,為 Array 添加了 lastItem 和 lastIndex

          2.1.2 Error Cause (proposal-error-cause)

          提案鏈接

          https://github.com/tc39/proposal-error-cause


          ECMAScript

          TypeScript

          Chrome

          Firefox

          Safari

          落地時(shí)間

          版本號(hào)

          October 2021

          2022

          No

          93

          91

          15

          我們正在實(shí)現(xiàn)一個(gè)方法。在這個(gè)方法中,調(diào)用的某個(gè)外部方法拋出了異常,而且:

          1. 我們的方法無(wú)法處理它;
          2. 我們和調(diào)用方約定了會(huì)拋出的異常,不能拋出未約定的異常;
          3. 我們也不能直接 new 一個(gè)新的 Error,因?yàn)闀?huì)丟失原始 Error 的上下文。

          如果 Error 能串起來(lái),那將極大地方便我們?nèi)フ{(diào)試并定位問(wèn)題。而 Error Cause 的出現(xiàn),在語(yǔ)言層面上解決了這個(gè)進(jìn)退兩難的問(wèn)題。

          在這之前,我們的應(yīng)用可能是這樣解決的:

          class?BusinessError?extends?Error?{
          ????constructor(code,?message,?{?cause?})?{
          ????????super(message);
          ????????this.code?=?code;
          ????????this.cause?=?cause;
          ????}?
          }

          async?function?addName()?{
          ????const?{?ctx?}?=?this;
          ????try?{
          ????????const?res?=?await?ctx.fetch('http://examples.com/getxxx');
          ????????return?res.json();
          ????}?catch?(e)?{
          ????????throw?new?BusinessError(500,?'request?failed',?{?cause:?e?});
          ????}
          }

          //?production?response:?{?code:?500,?message:?'request?failed'?}
          //?boe?response:?{?code,?message,?error:?{?stack,?cause:?{?...?}??}?}

          那么,為什么不用這種繼承方法創(chuàng)建自定義的 Error Class,而還需要在語(yǔ)言層面做修改呢?

          提案者本身也回答了這個(gè)問(wèn)題,他說(shuō):確實(shí)有很多辦法能夠?qū)崿F(xiàn)這一提案想要的行為,但是,如果在語(yǔ)言層面上定義了 cause,調(diào)試工具就可以更可靠地用這個(gè)信息,而不是去要求開發(fā)者以某種方式拋出 Error 了。

          While there are lots of ways to achieve the behavior of the proposal, if the cause property is explicitly defined by the language, debug tooling can reliably use this info rather than contracting with developers to construct an error properly.

          如提案者所說(shuō),F(xiàn)ireFox 的調(diào)試工具實(shí)現(xiàn)了這個(gè) cause 將 Error 串起來(lái)的功能,而 chrome 目前似乎還沒有跟上。


          2.1.3 Accessible Object.prototype.hasOwnProperty

          提案鏈接

          https://github.com/tc39/proposal-accessible-object-hasownproperty


          ECMAScript

          TypeScript

          Chrome

          Firefox

          Safari

          落地時(shí)間

          版本號(hào)

          August 2021

          2022

          4.6.0

          93

          92

          TP

          判斷某個(gè)字段是否存在于對(duì)象中,該怎么做?xxx in obj ?不對(duì),如果對(duì)象的原型上有這個(gè)字段,會(huì)錯(cuò)誤判斷。

          當(dāng)有這種需求時(shí),就必須用到 Object.prototype.hasOwnProperty 了。但這一方法使用起來(lái),還是非常的迷惑:

          let?hasOwnProperty?=?Object.prototype.hasOwnProperty
          if?(hasOwnProperty.call(obj,?"foo"))?{
          ??console.log("has?property?foo")
          }

          為什么不是 obj.hasOwnProperty() ?因?yàn)閷?duì)于 Object.create(null) 等魔法產(chǎn)生的對(duì)象,并不含 Object 原型鏈上的方法。

          這個(gè)提案為 Object 添加了新的靜態(tài)方法 Object.hasOwn,用這一靜態(tài)方法可以直接判斷。

          let?object?=?{?foo:?false?}
          Object.hasOwn(object,?"foo")?//?true

          let?object2?=?Object.create({?foo:?true?})
          Object.hasOwn(object2,?"foo")?//?false

          let?object3?=?Object.create(null)
          Object.hasOwn(object3,?"foo")?//?false

          2.1.4 Class Fields

          提案鏈接

          https://github.com/tc39/proposal-class-fields

          https://github.com/tc39/proposal-private-methods

          https://github.com/tc39/proposal-static-class-features


          ECMAScript

          TypeScript

          Chrome

          Firefox

          Safari

          落地時(shí)間

          版本號(hào)

          April 2021

          2022

          4

          74

          90

          14.1

          這 3 個(gè)提案 TypeScript 早已實(shí)現(xiàn),且已經(jīng)在上半年正式落地了,而且我們已經(jīng)在項(xiàng)目中用了很久,本不該在本文的討論范圍,但因?yàn)橄旅?2 個(gè)提案都與 class 有關(guān),我們回顧一下。

          1. Field declarations - 字段可以直接定義在 class 類中
          2. Private fields/methods - 可以定義私有字段、方法。私有成員只能在類中定義、只有當(dāng)前類可以訪問(wèn)、沒有后門
          3. Private getter/setters - 可以定義私有 getter / setter
          4. Static public fields/methods - 公共方法、字段可以定義為靜態(tài)
          5. Static private fields/methods - 私有方法、字段可以定義為靜態(tài)

          比如提案中提到,自定義一個(gè)點(diǎn)擊就可以 +1 的 Element:

          class?Counter?extends?HTMLElement?{
          ??#count?=?0;?

          ??get?#x()?{?return?#count;?}
          ??set?#x(value)?{
          ????this.#count?=?value;
          ????window.requestAnimationFrame(this.#render.bind(this));
          ??}

          ??#clicked()?{
          ????this.#x++;
          ??}

          ??constructor()?{
          ????super();
          ????this.onclick?=?this.#clicked.bind(this);
          ??}

          ??connectedCallback()?{?this.#render();?}

          ??#render()?{
          ????this.textContent?=?this.#x.toString();
          ??}
          }
          window.customElements.define('num-counter',?Counter);

          這個(gè)內(nèi)部的 count 值是沒有任何辦法獲取的(devtools 協(xié)議的某些手段除外)。


          2.1.5 Ergonomic brand checks for Private Fields

          提案鏈接

          https://github.com/tc39/proposal-private-fields-in-in


          ECMAScript

          TypeScript

          Chrome

          Firefox

          Safari

          落地時(shí)間

          版本號(hào)

          July 2021

          2022

          4.5.0

          91

          90

          15

          這一提案是私有字段/方法的擴(kuò)展。

          假如,我們需要在公有方法中設(shè)置某個(gè)私有字段,但不知道傳參是不是我們的對(duì)象,該怎么辦呢?

          如果沒有 in,以前,我們可能需要用到 try 和 catch 這么做:

          class?C?{
          ??#a;

          ??static?isC(obj)?{
          ????try?{
          ??????obj.#a;
          ??????return?true;
          ????}?catch?{
          ??????return?false;
          ????}
          ??}
          }

          但這一操作有個(gè)問(wèn)題,如果這個(gè) #a 是個(gè)可能會(huì) throw Error 的 getter,怎么辦呢?

          class?C?{
          ??get?#a()?{
          ????throw?new?Error('you?cannot?get?me!');
          ??}
          }

          好像沒辦法,所以,我們需要用這一提案的 #a in obj (注意,與公有方法不同,這個(gè)判斷左側(cè)不是字符串,公有方法的判斷是 'a' in obj

          這是一個(gè)應(yīng)用的例子:

          代碼

          應(yīng)用

          class?C?{
          ??#a;?
          ??//?如果我們認(rèn)可這個(gè)對(duì)象
          ??//?就將它設(shè)成?verified
          ??static?verifyAndSet(obj)?{
          ????if?(#a?in?obj)?{
          ??????obj.#a?=?'verified!'?
          ????}
          ??}
          }

          對(duì)于 C 對(duì)象,我們能正確設(shè)置 #a

          class?D?{
          ??#a;
          ??readA()?{
          ????return?this.#a;
          ??}
          }

          D 不是我們的對(duì)象,因此不會(huì)設(shè)置 #a (也沒辦法設(shè)置)

          ?class?E?extends?C?{
          ??#a;
          ??readA()?{
          ????return?this.#a;
          ??}
          }

          E 雖然 extends C,也會(huì)設(shè)置 #a,但這個(gè) #a 不是 E 的 #a,私有成員只能自己訪問(wèn),E 不能訪問(wèn) C 的成員。

          那么,為什么不是 instanceof ?提案者:安全,安全,還是安全。instanceof 是可以被欺騙的。而 private fields是沒有任何辦法從外部訪問(wèn)的,可以確保這個(gè)對(duì)象就是自己類的實(shí)例,就是我們想要的對(duì)象。這也是 brand check 的核心。


          2.1.6 Class Static Block (proposal-class-static-block)

          提案鏈接

          https://github.com/tc39/proposal-class-static-block


          ECMAScript

          TypeScript

          Chrome

          Firefox

          Safari

          落地時(shí)間

          版本號(hào)

          August 2021

          2022

          4

          94

          93

          x

          如果 class 的一些 static fields 需要運(yùn)算一個(gè)表達(dá)式才能設(shè)置,之前的 class fields 提案似乎沒辦法實(shí)現(xiàn),因此,這個(gè)提案可以作為補(bǔ)充。

          例如,同時(shí)設(shè)置 y 和 z 的值...

          class?C?{
          ??static?x?=?...;
          ??static?y;
          ??static?z;
          ??static?{
          ????try?{
          ??????const?obj?=?doSomethingWith(this.x);
          ??????this.y?=?obj.y;
          ??????this.z?=?obj.z;
          ????}
          ????catch?{
          ??????this.y?=?...;
          ??????this.z?=?...;
          ????}
          ??}
          }

          static 代碼塊是在 ClassDefinitionEvaluation 階段執(zhí)行的,也就是說(shuō),是在創(chuàng)建 class 的時(shí)候執(zhí)行的,這也讓它有能力訪問(wèn)并設(shè)置 private fields。它還有能力暴露特權(quán)函數(shù)給外部,理論上,外部函數(shù)沒辦法訪問(wèn)類的私有成員,但如果是 static 代碼塊賦給外部的,就可以突破這個(gè)限制。

          let?getX;

          class?C?{
          ??#x;
          ??constructor(x)?{
          ????this.#x?=?{?data:?x?};
          ??}
          ??static?{
          ????getX?=?(obj)?=>?obj.#x;
          ??}
          }

          getX(new?C('private?data'));
          //?{?data:?'private?data'?}

          2.2 Stage 3 (已確定,實(shí)現(xiàn)中)

          Stage 3 的提案基本上已經(jīng)確定并交由內(nèi)核實(shí)現(xiàn),甚至部分瀏覽器已經(jīng)實(shí)現(xiàn)。這一階段的提案只能針對(duì)關(guān)鍵問(wèn)題進(jìn)行修改。

          2.2.1 Array find from last

          indexOflastIndexOf,有 findfindIndex,那為什么沒有 findLastfindLastIndex 呢?現(xiàn)在它終于快來(lái)了...


          2.2.2 Import Assertions

          我們經(jīng)常會(huì)用到類似

          import?Component?from?'./component';
          import?data?from?'./data.json';
          import?styles?from?'./index.module.css';

          這樣的引入,但是它們都是 webpack 等打包工具幫我們處理的。隨著 json modules 和 css modules 加入 Web 標(biāo)準(zhǔn),原生 JavaScript 也要考慮引入對(duì)它們的支持。

          但不能就這樣引入!因?yàn)?..假如,我們?cè)跒g覽器中執(zhí)行

          import?sheet?from?'./styles.css';

          而后端給我們返回了

          Content-Type:?application/javascript;?charset=utf8;

          alert('you?are?rickrolled!');

          emmm... 這可不好。

          為什么不用擴(kuò)展名來(lái)區(qū)分呢?因?yàn)閿U(kuò)展名不是資源的一切,我們有太多資源沒有擴(kuò)展名了。Content-Type 由后端掌控,不夠安全,因此,提案中設(shè)計(jì)了 import assertion 的方式。

          //?同步的
          import?json?from?"./foo.json"?assert?{?type:?"json"?};
          //?異步的
          const?cssModule?=?await?import('./style.css',?{??assert:?{?type:?'css'?}});

          2.2.3 proposal-temporal

          JavaScript 的 Date 有多難用,不支持 format、很難用的 setDate、... 就不用說(shuō)了,更不用說(shuō) month 還是從 0 開始的。

          大概 tc39 的成員們也覺得 Date 沒有改造價(jià)值了,因此干脆寫個(gè)新的 Temporal 來(lái)代替它。整個(gè) Temporal 對(duì)象重新實(shí)現(xiàn)了 Date,可以參考以下文檔(中文):

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://tc39.es/proposal-temporal/docs/zh_CN/index.html

          ? ? ? ? ?

          簡(jiǎn)單說(shuō)來(lái),它將時(shí)間分為 「壁鐘時(shí)間」(本地時(shí)間) 和 「確切時(shí)間」(UTC時(shí)間) 兩種。壁鐘時(shí)間受當(dāng)?shù)氐臅r(shí)區(qū)、歷法或是夏令時(shí)之類的影響,而確切時(shí)間則表示的就是對(duì)應(yīng)的 UTC 時(shí)間點(diǎn)。

          Temporal 有以下幾種核心方法:

          Temporal.Instant:確切時(shí)間,無(wú)時(shí)區(qū)、歷法等信息

          Temporal.PlainDate
          Temporal.PlainTime
          Temporal.PlainDateTime
          Temporal.PlainYearMonth
          Temporal.PlainMonthDay:壁鐘時(shí)間,只表示某一時(shí)刻,不包含時(shí)區(qū)歷法

          Temporal.ZonedDateTime:表示包含當(dāng)前的時(shí)區(qū)歷法的某一時(shí)刻
          Temporal.Now:當(dāng)前時(shí)刻,包含當(dāng)前的時(shí)區(qū)、歷法信息

          Temporal.TimeZone:只表示時(shí)區(qū)
          Temporal.Calendar:?只表示歷法
          Temporal.Duration:只表示時(shí)間間隔,不包含其他任何信息

          小測(cè)驗(yàn),下面這兩條語(yǔ)句的輸出是什么?

          const?timeZone?=?Temporal.TimeZone.from('Asia/Shanghai');
          timeZone.getInstantFor('2000-01-01T00:00');
          timeZone.getPlainDateTimeFor('2000-01-01T00:00Z');
          1. 上海時(shí)區(qū)和北京時(shí)區(qū)一樣,都是 UTC+8,無(wú)夏令時(shí)
          2. getInstantFor 想要得到的是確切時(shí)間,需要用 壁鐘時(shí)間 與上海時(shí)區(qū) 換算。那么,在上??吹界娚巷@示的時(shí)間是 2000-01-01 00:00,確切時(shí)間應(yīng)該就是 1999-12-31T16:00:00Z。
          3. getPlainDateTimeFor 想要得到的是壁鐘時(shí)間,我們知道確切時(shí)間是 2000-01-01T00:00Z,那么上海的鐘上應(yīng)該應(yīng)該是 2000-01-01T08:00:00。

          Temporal 有對(duì)應(yīng)的字符串表示,由這幾部分組成Not Final

          一些例子??:

          「構(gòu)造、修改、比較、運(yùn)算:」

          dt?=?Temporal.PlainDateTime.from({
          ??year:?1995,
          ??month:?12,
          ??day:?7,
          ??hour:?3,
          ??minute:?24,
          ??second:?300,
          ??millisecond:?0,
          ??microsecond:?3,
          ??nanosecond:?500
          });
          //?Temporal.PlainDateTime?<1995-12-07T03:24:59.0000035>

          dt.with({?year:?2000,?day:?30?})
          //?Temporal.PlainDateTime?<2000-12-30T03:24:59.0000035>

          dt.add?/?substract(Temporal.Duration)?=?Temporal.PlainDateTime

          dt.until?/?since(Temporal.PlainDateTime)?=>?Temporal.Duration

          「duration.round」:比如,我們有時(shí)候會(huì)需要把 134 秒 轉(zhuǎn)換為 x 分 x 秒的形式,Duration 就非常適合我們

          a?=?Temporal.Duration.from({?seconds:?134?});
          b?=?a.round({?largestUnit:?'day'?});
          b.toLocaleString();?
          //?我們期待的是?2?分?14?秒,但實(shí)際上暫時(shí)不能這么做,因?yàn)?
          //?提供國(guó)際化格式化?Duration?的?Intl.DurationFormat?還在實(shí)現(xiàn)中

          //?largestUnit?是必須的,不然它不會(huì)自動(dòng)進(jìn)位
          //?如果?largestUnit?是月或者年,就必須提供起點(diǎn),比如某月的天數(shù)不同,某年的天數(shù)不同
          //?考慮到夏令時(shí)等因素,甚至還需要提供時(shí)區(qū)和歷法

          2.2.4 ShadowRealm

          (這個(gè)提案以前叫 Realm)

          設(shè)想一些場(chǎng)景:

          1. 我們需要執(zhí)行一些代碼,但這個(gè)代碼需要我們剛才講的 Temporal 的 Polyfill,于是它直接 window.Temporal = TemporalPolyfill。執(zhí)行它污染了我的作用域。僅僅一個(gè)變量可能還可控一些,假如它引入了個(gè) core-js,那就不得了了。我們想保護(hù)好自己
          2. 我想讓我的代碼執(zhí)行在原生且安全的環(huán)境中,不希望受到別人影響,比如,外面的腳本可能劫持了我的一些方法,比如 garfish 就劫持了我的 localStorageinsertElement 等方法,我想要原生對(duì)象做一些操作。
          3. 我是個(gè)子應(yīng)用,我想讓我的應(yīng)用不受其他應(yīng)用干擾,也不要干擾到其他應(yīng)用(garfish 通過(guò)給子應(yīng)用的 context 塞假的 globalThis 對(duì)象來(lái)實(shí)現(xiàn))。

          ShadowRealm 就是來(lái)解決這些問(wèn)題的!它讓每一段可信的 JavaScript 都有能力執(zhí)行在隔離的領(lǐng)域中,獲得干凈的原生環(huán)境,類似于新的 context,不受任何影響,也不影響其他人。

          declare?class?ShadowRealm?{
          ????constructor();
          ????importValue(specifier:?string,?bindingName:?string):?Promise;
          ????evaluate(sourceText:?string):?PrimitiveValueOrCallable;
          }

          這也相當(dāng)于給 JavaScript 添加了新的隔離環(huán)境的方式。以前我們只有 context,現(xiàn)在還有 realm 了。

          詳細(xì)了解,可以參考以下文檔:

          https://github.com/tc39/proposal-shadowrealm/blob/main/explainer.md


          2.3 Stage 2 (待確定,很可能實(shí)現(xiàn))

          這一部分的提案不管講得多詳細(xì),看起來(lái)多么 promising,也不得不貼上 Not Final 的標(biāo)簽。反復(fù)橫跳的事情時(shí)有發(fā)生,對(duì)于它們,可以先了解一下大致思路。

          2.3.1 Decorators

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-decorators

          ? ? ? ? ?

          曾經(jīng)非常有潛力的裝飾器提案,花了幾年的時(shí)間大改才進(jìn)展到 stage 2,被放棄后又被重新提出,目前又進(jìn)展到 stage 2 了。

          這一版本的 Decorators 目標(biāo)有 3 個(gè)主要能力:

          1. 將被裝飾的值替換為同類值,比如,字段替換為另一個(gè)字段,方法替換為另一個(gè)方法,類替換為另一個(gè)類...等等
          2. 為被裝飾的值添加 「元數(shù)據(jù)」,支持 元編程
          3. 通過(guò)元數(shù)據(jù)訪問(wèn)被裝飾的值,不管是公有還是私有

          目前它的定義大概是這樣的,還在不斷變化中:

          type?Decorator?=?(value:?Input,?context:?{
          ??//?要裝飾的是什么
          ??kind:?'class'?|?'method'?|?'getter'?|?'setter'?|?'field'?|?'auto-accessor';
          ??//?目標(biāo)的名稱,或者私有元素的可讀名稱
          ??name:?string?|?symbol;
          ??//?訪問(wèn)方法,僅私有元素存在,提供訪問(wèn)和修改私有元素的方式
          ??access?:?{
          ????get?():?unknown;
          ????set?(value:?unknown):?void;
          ??};
          ??//?是否為私有
          ??isPrivate?:?boolean;
          ??//?是否為靜態(tài)
          ??isStatic?:?boolean;
          ??//?如果是?@init:?的裝飾器,提供初始化方法
          ??addInitializer?(initializer:?()?=>?void):?void;
          ??//?獲取和設(shè)置元數(shù)據(jù)
          ??getMetadata(key:?symbol);
          ??setMetadata(key:?symbol,?value:?unknown);
          })?=>
          ?Output?|?void;

          2.3.1.1 簡(jiǎn)單示例

          以簡(jiǎn)單的 @logged 為例:

          function?logged(value,?{?kind,?name?})?{
          ??if?(kind?===?"method")?{
          ????return?function?(...args)?{
          ??????console.log(`starting?${name}?with?arguments?${args.join(",?")}`);
          ??????const?ret?=?value.call(this,?...args);
          ??????console.log(`ending?${name}`);
          ??????return?ret;
          ????};
          ??}
          }
          class?C?{
          ??@logged
          ??m(arg)?{}
          }
          new?C().m(1);
          //?starting?m?with?arguments?1
          //?ending?m
          • value: 什么也不做的 m 函數(shù)
          • kind: 'method'
          • name: 'm'

          同理 'class' | 'method' | 'getter' | 'setter' | 'field' 都是這樣的。

          2.3.1.2 Auto accessor

          再來(lái)說(shuō)說(shuō)這個(gè) auto accessor,這個(gè)提案給類字段加了個(gè) accessor 關(guān)鍵字,大概是自動(dòng)生成一個(gè)私有字段的 getter 和 setter:

          class?C?{
          ??accessor?x?=?1;
          }

          //?約等于這樣:
          class?C?{
          ??#x?=?1;

          ??get?x()?{
          ????return?this.#x;
          ??}

          ??set?x(val)?{
          ????this.#x?=?val;
          ??}
          }

          //?當(dāng)然這個(gè)東西也能生成私有的或者靜態(tài)的
          class?C?{
          ??static?accessor?x?=?1;
          ??accessor?#y?=?2;
          }

          這個(gè)有什么用?@decorated 就有用了:

          function?logged(value,?{?kind,?name?})?{
          ??if?(kind?===?"auto-accessor")?{
          ????let?{?get,?set?}?=?value;

          ????return?{
          ??????get()?{
          ????????console.log(`getting?${name}`);

          ????????return?get.call(this);
          ??????},

          ??????set(val)?{
          ????????console.log(`setting?${name}?to?${val}`);

          ????????return?set.call(this,?val);
          ??????},

          ??????initialize(initialValue)?{
          ????????console.log(`initializing?${name}?with?value?${initialValue}`);
          ????????return?initialValue;
          ??????}
          ????};
          ??}

          ??//?...
          }
          class?C?{
          ??@logged?accessor?x?=?1;
          }
          let?c?=?new?C();
          //?initializing?x?with?value?1
          c.x;
          //?getting?x
          c.x?=?123;
          //?setting?x?to?123

          2.3.1.3 @init:

          如果 decorator 是 @init: 開頭,那么它會(huì)額外獲得一個(gè) addInitializer 方法,會(huì)在被裝飾的目標(biāo)已經(jīng)初始化完畢后執(zhí)行。

          • 對(duì)于類,會(huì)在類定義完成后執(zhí)行。
          • 對(duì)于類字段和類方法,會(huì)在類實(shí)例初始化時(shí),對(duì)應(yīng)字段和方法初始化完成后執(zhí)行。
          • 對(duì)于靜態(tài)類字段和靜態(tài)類方法,會(huì)在類定義時(shí),對(duì)應(yīng)字段和方法初始化完成后執(zhí)行。

          @init 一個(gè)自定義元素

          function?customElement(name)?{
          ??(value,?{?addInitializer?})?=>?{
          ????addInitializer?.(function()?{
          ??????customElements.define(name,?this);
          ????});
          ??}
          }

          @init:customElement('my-element')
          class?MyElement?extends?HTMLElement?{
          ??static?get?observedAttributes()?{
          ????return?['some',?'attrs'];
          ??}
          }

          或者生成一個(gè)不管怎樣都 bind this 的方法

          function?bound(value,?{?name,?addInitializer?})?{
          ??addInitializer(function?()?{
          ????this[name]?=?this[name].bind(this);
          ??});
          }
          class?C?{
          ??message?=?"hello!";

          ??@init:bound
          ??m()?{
          ????console.log(this.message);
          ??}
          }
          let?{?m?}?=?new?C();
          m();?//?hello!

          2.3.1.4 metadata

          Decorators 提案還添加了一個(gè)新的 Symbol,Symbol.metadata,可以用來(lái)訪問(wèn) setMetaData 中設(shè)置的元數(shù)據(jù)。

          而 metadata 分為 constructorpublicprivate,例如:

          const?MY_META?=?Symbol();
          function?myMeta(value,?context)?{
          ??context.setMetadata(MY_META,?'metadata');
          }

          @myMeta
          class?C?{
          ??@myMeta?a?=?123
          ??@myMeta?b()?{}
          ??@myMeta?#c?=?456;

          ??@myMeta?static?x?=?123;
          ??@myMeta?static?y()?{}
          ??@myMeta?static?#z?=?456;
          }

          C.prototype[Symbol.metadata][MY_META];
          //?{
          //???public:?{
          //?????a:?'metadata',
          //?????b:?'metadata',
          //???},
          //???private:?['metadata']
          //?}
          C[Symbol.metadata][MY_META];
          //?{
          //???constructor:?'metadata',
          //???public:?{
          //?????x:?'metadata',
          //?????y:?'metadata',
          //???},
          //???private:?['metadata']
          //?}

          這一部分的設(shè)計(jì)思路是讓裝飾同一個(gè)成員的 decorator 間有能力互相協(xié)調(diào),不至于沖突(目前我還沒看出有什么用處)。


          2.3.2 Pipeline Operator

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-pipeline-operator

          ? ? ? ? ?

          這個(gè)提案在進(jìn)入 stage 2 之前,有3 個(gè)關(guān)于 pipeline 的競(jìng)爭(zhēng)提案在 stage 1 徘徊了很久,分別是 minimal、fsharp、~~smart(已廢棄)~~ 。但是... 真正進(jìn)入 stage 2 的卻是目前的 hack-style pipline operator。

          這個(gè)是我非常期待的提案之一,因?yàn)闀?huì)極大提升我們的代碼可讀性。想象這樣一個(gè)場(chǎng)景:

          const?obj?=?{?
          ????'1':?{?id:?'1',?name:?'小明'?},?
          ????'2':?{?id:?'2',?name:?'小剛'?},
          }

          _.union(_.values(_.mapValues(obj,?i?=>?i.name)),?['小張',?'小剛'])
          //??['小明',?'小剛',?'小張']

          出現(xiàn)了某個(gè)函數(shù)參數(shù)是另一個(gè)函數(shù)的結(jié)果,又作為參數(shù)傳給另一個(gè)函數(shù)的情況。如果我們用 Pipeline Operator (hack-style) 的話,看起來(lái)就比較清晰了:

          obj
          ????|>?_.mapValues(%,?i?=>?i.name)?//?{?'1':?'小明',?'2':?'小剛'?}
          ????|>?_.values(%)?//?['小明',?'小剛']
          ????|>?_.union(%,?['小張',?'小剛'])?//
          ;
          //??['小明',?'小剛',?'小張']

          這樣是不是清晰多了。它非常適合解決數(shù)據(jù)需要串行處理的情況。

          hack style 這一提案與其他提案不同的是,對(duì)于 % 占位符有強(qiáng)要求,即使是作為函數(shù)的唯一參數(shù),也必須使用。相比其他方式,它可以更全面地涵蓋 函數(shù)調(diào)用 %.()、 await %(yield %) 等場(chǎng)景。


          2.3.3 Destructuring Private

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-destructuring-private

          ? ? ? ? ?

          小小改動(dòng),作為 class fields 提案的補(bǔ)全。

          private fields 也可以 destruct 了,但必須提供 alias。

          class?Foo?{
          ??#x?=?1;

          ??constructor()?{
          ????const?{?#x:?x?}?=?this;
          ????console.log(x);?//?=>?1
          ??}
          }

          2.3.4 Array Grouping

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-array-grouping

          ? ? ? ? ?

          大概就是將 lodash 的 groupBy 方法引入 JavaScript 中。

          const?array?=?[1,?2,?3,?4,?5];
          array.groupBy(i?=>?{
          ??return?i?%?2?===?0???'even':?'odd';
          });
          //?=>??{?odd:?[1,?3,?5],?even:?[2,?4]?}

          2.3.5 Change Array by Copy

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-change-array-by-copy

          ? ? ? ? ?

          數(shù)組里面有些方法是原地操作的,比如 reverse sort splice ...

          拷貝一份再操作又很麻煩,能不能直接返回給我一個(gè)新的數(shù)組呢?

          Array.prototype.withReversed()?->?Array
          Array.prototype.withSorted(compareFn)?->?Array
          Array.prototype.withSpliced(start,?deleteCount,?...items)?->?Array
          //?[3,?3,?3].withAt(1,?1)?=>?[1,?3,?3]
          Array.prototype.withAt(index,?value)?->?Array

          2.3.6 Module Blocks

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-js-module-blocks

          ? ? ? ? ?

          一個(gè) Module 一定要放在獨(dú)立的文件里?目前是,但,這個(gè)提案試圖改變這一點(diǎn)...

          是不是可以把所有的模塊打包到一個(gè)文件里?

          let?moduleBlock?=?module?{
          ??import?{?add?}?from?'lodash-es';
          ??export?let?y?=?add(1,?2);
          };
          let?moduleExports?=?await?import(moduleBlock);
          moduleExports.y?===?3;

          2.3.7 Record & Tuple

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-record-tuple

          ? ? ? ? ?

          不可修改的對(duì)象和數(shù)組,提案目前的想法是:

          1. 創(chuàng)建一個(gè)不能修改的記錄、元組,其中只能包含基本類型、記錄和元組 和 封裝??的類型
          2. 相等比較時(shí),基本類型對(duì)值進(jìn)行比較,封裝類型根據(jù)引用進(jìn)行比較
          3. 提供一些修改、封裝、解除封裝的方法
          4. 提供除更新外其他對(duì) object / array 的操作方法
          const?record?=?#{
          ??a:?#{
          ????foo:?"string",
          ??},
          ??b:?#{
          ????bar:?123,
          ??},
          ??c:?#{
          ????baz:?#{
          ??????hello:?#[
          ????????1,
          ????????2,
          ????????3,
          ??????],
          ????},
          ??},
          };

          //
          const?ship1?=?#[1,?2];
          const?ship2?=?[-1,?3];
          function?move(start,?deltaX,?deltaY)?{
          ??return?#[
          ????start[0]?+?deltaX,
          ????start[1]?+?deltaY,
          ??];
          }
          const?ship1Moved?=?move(ship1,?1,?0);
          const?ship2Moved?=?move(ship2,?3,?-1);
          console.log(ship1Moved?===?ship2Moved);?//?true

          //
          const?myObject?=?{?x:?2?};
          const?record?=?#{
          ??name:?"rec",
          ??data:?Box(myObject)
          };
          console.log(record.data.unbox().x);
          record.data.unbox().x?=?3;
          console.log(myObject.x);?//?3
          console.log(record?===?#{?name:?"rec",?data:?Box(myObject)?});?//?true

          2.4 Stage 1 (新功能)

          提案通常會(huì)在 Stage 1 停留幾年,可能會(huì)有很多變化,也可能會(huì)隨時(shí)被廢棄,因此這里只選取兩個(gè)我比較期待的,也只會(huì)大概說(shuō)明它的思路。

          2.4.1 Partial Application

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-partial-application

          ? ? ? ? ?

          本質(zhì)上就是 「curry」 ,我們有時(shí)候,需要?jiǎng)?chuàng)建某些參數(shù)已經(jīng)確定的函數(shù),這一提案可以解決這種需求。

          const?add?=?(x,?y)?=>?x?+?y;

          //?apply?from?the?left:
          const?addOne?=?add~(1,??);
          addOne(2);?//?3

          //?apply?from?the?right:
          const?addTen?=?add~(?,?10);
          addTen(2);?//?12

          2.4.2 Do Expressions

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposal-do-expressions

          ? ? ? ? ?

          在寫代碼的時(shí)候出現(xiàn)這種情況...

          type?Type?=?'cat'?|?'dog'?|?'human';
          const?itemType:?Type?=?'...';

          let?Component;
          if?(itemType?===?'cat')?{
          ????Component?=?Cat;
          }?else?if?(itemType?===?'dog')?{
          ????Component?=?Dog;
          }?else?if?(itemType?===?'human')?{
          ????Component?=?Human;
          }
          ;

          這樣用三目非常麻煩,代碼可讀性很差,而用這種方式或者 switch 還是怎樣都沒辦法在一個(gè)語(yǔ)句內(nèi)完成,因此,引入 do expression 可以很好的解決這個(gè)問(wèn)題。

          let?Component?=?do?{
          ????if?(itemType?===?'cat')?Cat;
          ????else?if?(itemType?===?'dog')?Dog;
          ????else?if?(itemType?===?'human')?Human;
          };
          return?<Component?/>;

          三、了解更多

          提案?jìng)儯?/p>

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/proposals

          ? ? ? ? ?

          TC39 中文站:

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://jscig.github.io/

          ? ? ? ? ?

          TC39 會(huì)議記錄:

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/tc39/notes/tree/master/meetings

          ? ? ? ? ?

          參與中文討論:

          「長(zhǎng)按識(shí)別二維碼查看原文」? ?

          https://github.com/JSCIG/es-discuss/discussions

          ? ? ? ? ?
          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人妻精品无码偷拍 | 黄色视频观看免费 | 日本黄色电影网站 | 色色五月丁香婷婷 | 激情乱伦毛片 |