摘要:例如通過哈希表映射需要一個操作來檢查值是否相等,另一個操作用于創(chuàng)建哈希碼。如果使用哈希碼,則對象應該是不可變的。模式匹配提案目前處于第階段。在本文,我們研究其中的智能管道另一個提議被稱為。更強大,更重量級,并附帶自己的數(shù)據(jù)結構。
翻譯:瘋狂的技術宅
原文:http://2ality.com/2019/01/fut...
本文首發(fā)微信公眾號:jingchengyideng
歡迎關注,每天都給你推送新鮮的前端技術文章
近年來,JavaScript 的功能得到了大幅度的增加,本文探討了其仍然缺失的東西。
說明:
我只列出了我所發(fā)現(xiàn)的最重要的功能缺失。當然還有很多其它有用的功能,但同時也會增加太多的風險。
我的選擇是主觀的。
本文中提及的幾乎所有內(nèi)容都在 TC39 的技術雷達上。 也就是說,它還可以作為未來可能的 JavaScript 的預覽。
有關前兩個問題的更多想法,請參閱本文第8節(jié):語言設計部分。
1. 值 1.1 按值比較對象目前,JavaScript 只能對原始值(value)進行比較,例如字符串的值(通過查看其內(nèi)容):
> "abc" === "abc" true
相反,對象則通過身份ID(identity)進行比較(對象僅嚴格等于自身):
> {x: 1, y: 4} === {x: 1, y: 4} false
如果有一種能夠創(chuàng)建按值進行比較對象的方法,那將是很不錯的:
> #{x: 1, y: 4} === #{x: 1, y: 4} true
另一種可能性是引入一種新的類(確切的細節(jié)還有待確定):
@[ValueType] class Point { // ··· }
旁注:這種類似裝飾器的將類標記為值類型的的語法基于草案提案。
1.2 將對象放入數(shù)據(jù)結構中如果對象通過身份ID進行比較,將它們放入 ECMAScript 數(shù)據(jù)結構(如Maps)中并沒有太大意義:
const m = new Map(); m.set({x: 1, y: 4}, 1); m.set({x: 1, y: 4}, 2); assert.equal(m.size, 2);
可以通過自定義值類型修復此問題。 或者通過自定義 Set 元素和 Map keys 的管理。 例如:
通過哈希表映射:需要一個操作來檢查值是否相等,另一個操作用于創(chuàng)建哈希碼。 如果使用哈希碼,則對象應該是不可變的。 否則破壞數(shù)據(jù)結構就太容易了。
通過排序樹映射:需要一個比較值的操作用來管理它存儲的值。
1.3 大整數(shù)JavaScript 的數(shù)字總是64位的(雙精度),它能為整數(shù)提供53位二進制寬度。這意味著如果超過53位,就不好使了:
> 2 ** 53 9007199254740992 > (2 ** 53) + 1 // can’t be represented 9007199254740992 > (2 ** 53) + 2 9007199254740994
對于某些場景,這是一個相當大的限制?,F(xiàn)在有[BigInts提案](http://2ality.com/2017/03/es-...),這是真正的整數(shù),其精度可以隨著需要而增長:
> 2n ** 53n 9007199254740992n > (2n ** 53n) + 1n 9007199254740993n
BigInts還支持 casting,它為你提供固定位數(shù)的值:
const int64a = BigInt.asUintN(64, 12345n); const int64b = BigInt.asUintN(64, 67890n); const result = BigInt.asUintN(64, int64a * int64b);1.4 小數(shù)計算
JavaScript 的數(shù)字是基于 IEEE 754 標準的64位浮點數(shù)(雙精度數(shù))。鑒于它們的表示形式是基于二進制的,在處理小數(shù)部分時可能會出現(xiàn)舍入誤差:
> 0.1 + 0.2 0.30000000000000004
這在科學計算和金融技術(金融科技)中尤其成問題。基于十進制運算的提案目前處于階段0。它們可能最終被這樣使用(注意十進制數(shù)的后綴 m):
> 0.1m + 0.2m 0.3m1.5 對值進行分類
目前,在 JavaScript 中對值進行分類非常麻煩:
首先,你必須決定是否使用 typeof 或 instanceof。
其次,typeof 有一個眾所周知的的怪癖,就是把 null 歸類為“對象”。我還認為函數(shù)被歸類為 "function" 同樣是奇怪的。
> typeof null "object" > typeof function () {} "function" > typeof [] "object"
第三,instanceof 不適用于來自其他realm(框架等)的對象。
也許可能通過庫來解決這個問題(如果我有時間,就會實現(xiàn)一個概念性的驗證)。
2. 函數(shù)式編程 2.1 更多表達式不幸的是C風格的語言在表達式和語句之間做出了區(qū)分:
// 條件表達式 let str1 = someBool ? "yes" : "no"; // 條件聲明 let str2; if (someBool) { str2 = "yes"; } else { str2 = "no"; }
特別是在函數(shù)式語言中,一切都是表達式。 Do-expressions 允許你在所有表達式上下文中使用語句:
let str3 = do { if (someBool) { "yes" } else { "no" } };
下面的代碼是一個更加現(xiàn)實的例子。如果沒有 do-expression,你需要一個立即調(diào)用的箭頭函數(shù)來隱藏范圍內(nèi)的變量 result :
const func = (() => { let result; // cache return () => { if (result === undefined) { result = someComputation(); } return result; } })();
使用 do-expression,你可以更優(yōu)雅地編寫這段代碼:
const func = do { let result; () => { if (result === undefined) { result = someComputation(); } return result; }; };2.2 匹配:解構 switch
JavaScript 使直接使用對象變得容易。但是根據(jù)對象的結構,沒有內(nèi)置的切換 case 分支的方法??雌饋硎沁@樣的(來自提案的例子):
const resource = await fetch(jsonService); case (resource) { when {status: 200, headers: {"Content-Length": s}} -> { console.log(`size is ${s}`); } when {status: 404} -> { console.log("JSON not found"); } when {status} if (status >= 400) -> { throw new RequestError(res); } }
正如你所看到的那樣,新的 case 語句在某些方面類似于 switch,不過它使用解構來挑選分支。當人們使用嵌套數(shù)據(jù)結構(例如在編譯器中)時,這種功能非常有用。 模式匹配提案目前處于第1階段。
2.3 管道操作管道操作目前有兩個競爭提案 。在本文,我們研究其中的 智能管道(另一個提議被稱為 F# Pipelines)。
管道操作的基本思想如下。請考慮代碼中的嵌套函數(shù)調(diào)用。
const y = h(g(f(x)));
但是,這種表示方法通常不能體現(xiàn)我們對計算步驟的看法。在直覺上,我們將它們描述為:
從值 x 開始。
然后把 f() 作用在 x 上。
然后將 g() 作用于結果。
然后將 h() 應用于結果。
最后將結果賦值給 y。
管道運算符能讓我們更好地表達這種直覺:
const y = x |> f |> g |> h;
換句話說,以下兩個表達式是等價的。
f(123) 123 |> f
另外,管道運算符支持部分應用程序(類似函數(shù)的 .bind() 方法):以下兩個表達式是等價的。
123 |> f(#) 123 |> (x => f(x))
使用管道運算符一個最大的好處是,你可以像使用方法一樣使用函數(shù)——而無需更改任何原型:
import {map} from "array-tools"; const result = arr |> map(#, x => x * 2);
最后,讓我們看一個長一點的例子(取自提案并稍作編輯):
promise |> await # |> # || throw new TypeError( `Invalid value from ${promise}`) |> capitalize // function call |> # + "!" |> new User.Message(#) |> await stream.write(#) |> console.log // method call ;3 并發(fā)
一直以來 JavaScript 對并發(fā)性的支持很有限。并發(fā)進程的事實標準是 Worker API,可以在 web browsers 和 Node.js (在 v11.7 及更高版本中沒有標記)中找到。
在Node.js中的使用方法它如下所示:
const { Worker, isMainThread, parentPort, workerData } = require("worker_threads"); if (isMainThread) { const worker = new Worker(__filename, { workerData: "the-data.json" }); worker.on("message", result => console.log(result)); worker.on("error", err => console.error(err)); worker.on("exit", code => { if (code !== 0) { console.error("ERROR: " + code); } }); } else { const {readFileSync} = require("fs"); const fileName = workerData; const text = readFileSync(fileName, {encoding: "utf8"}); const json = JSON.parse(text); parentPort.postMessage(json); }
唉,相對來說 Workers 是重量級的 —— 每個都有自己的 realm(全局變量等)。我想在未來看到一個更加輕量級的構造。
4. 標準庫JavaScript 仍然明顯落后于其他語言的一個領域是它的標準庫。當然保持最小化是有意義的,因為外部庫更容易進化和適應。但是有一些核心功能也是有必要的。
4.1 用模塊替代命名空間對象JavaScript 標準庫是在其語言具有模塊之前創(chuàng)建的。因此函數(shù)被放在命名空間對象中,例如Object,Reflect,Math和JSON:
Object.keys()
Reflect.ownKeys()
Math.sign()
JSON.parse()
如果將這個功能放在模塊中會更好。它必須通過特殊的URL訪問,例如使用偽協(xié)議 std:
// Old: assert.deepEqual( Object.keys({a: 1, b: 2}), ["a", "b"]); // New: import {keys} from "std:object"; assert.deepEqual( keys({a: 1, b: 2}), ["a", "b"]);
好處是:
JavaScript 將變得更加模塊化(這可以加快啟動時間并減少內(nèi)存消耗)。
調(diào)用導入的函數(shù)比調(diào)用存儲在對象中的函數(shù)更快。
4.2 可迭代工具 (sync 與 async)迭代 的好處包括按需計算和支持許多數(shù)據(jù)源。但是目前 JavaScript 只提供了很少的工具來處理 iterables。例如,如果要 過濾、映射或消除重復,則必須將其轉換為數(shù)組:
const iterable = new Set([-1, 0, -2, 3]); const filteredArray = [...iterable].filter(x => x >= 0); assert.deepEqual(filteredArray, [0, 3]);
如果 JavaScript 具有可迭代的工具函數(shù),你可以直接過濾迭代:
const filteredIterable = filter(iterable, x => x >= 0); assert.deepEqual( // We only convert the iterable to an Array, so we can // check what’s in it: [...filteredIterable], [0, 3]);
以下是迭代工具函數(shù)的一些示例:
// Count elements in an iterable assert.equal(count(iterable), 4); // Create an iterable over a part of an existing iterable assert.deepEqual( [...slice(iterable, 2)], [-1, 0]); // Number the elements of an iterable // (producing another – possibly infinite – iterable) for (const [i,x] of zip(range(0), iterable)) { console.log(i, x); } // Output: // 0, -1 // 1, 0 // 2, -2 // 3, 3
筆記:
有關迭代器的工具函數(shù)示例,請參閱 Python 的 itertools (https://docs.python.org/3/lib...)。
對于 JavaScript,迭代的每個工具函數(shù)應該有兩個版本:一個用于同步迭代,一個用于異步迭代。
4.3 不可變數(shù)據(jù)很高興能看到對數(shù)據(jù)的非破壞性轉換有更多的支持。兩個相關的庫是:
Immer 相對輕量,適用于普通對象和數(shù)組。
Immutable.js 更強大,更重量級,并附帶自己的數(shù)據(jù)結構。
4.4 更好地支持日期和時間JavaScript 對日期和時間的內(nèi)置支持有許多奇怪的地方。這就是為什么目前建議用庫來完成除了最基本任務之外的其它所有工作。
值得慶幸的是 temporal 是一個更好的時間 API:
const dateTime = new CivilDateTime(2000, 12, 31, 23, 59); const instantInChicago = dateTime.withZone("America/Chicago");5. 可能不需要的功能 5.1 optional chaining 的優(yōu)缺點
一個相對流行的提議功能是 optional chaining。以下兩個表達式是等效的。
obj?.prop (obj === undefined || obj === null) ? undefined : obj.prop
此功能對于屬性鏈特別方便:
obj?.foo?.bar?.baz
但是,仍然存在缺點:
深層嵌套的結構更難管理。
由于在訪問數(shù)據(jù)時過于寬容,隱藏了以后可能會出現(xiàn)的問題,更難以調(diào)試。
optional chaining 的替代方法是在單個位置提取一次信息:
你可以編寫一個提取數(shù)據(jù)的輔助函數(shù)。
或者你可以編寫一個函數(shù),使其輸入是深層嵌套數(shù)據(jù),但是輸出更簡單、標準化的數(shù)據(jù)。
無論采用哪種方法,都可以執(zhí)行檢查并在出現(xiàn)問題時盡早拋出異常。
進一步閱讀:
“Overly defensive programming” by Carl Vitullo
Thread on Twitter by Cory House
5.2 我們需要運算符重載嗎?目前正在為 運算符重載 進行早期工作,但是 infix 函數(shù)可能就足夠了(目前還沒有提案):
import {BigDecimal, plus} from "big-decimal"; const bd1 = new BigDecimal("0.1"); const bd2 = new BigDecimal("0.2"); const bd3 = bd1 @plus bd2; // plus(bd1, bd2)
infix 函數(shù)的好處是:
你可以創(chuàng)建 JavaScript 以外的運算符。
與普通函數(shù)相比,嵌套表達式的可讀性仍然很好。
下面是嵌套表達式的例子:
a @plus b @minus c @times d times(minus(plus(a, b), c), d)
有趣的是,管道操作符還有助于提高可讀性:
plus(a, b) |> minus(#, c) |> times(#, d)6. 各種小東西
以下是我偶爾會遺漏的一些東西,但我認為不如前面提到的那些重要:
鏈式異常:使你能夠捕獲錯誤,能夠包含其他信息并再次拋出。
new ChainedError(msg, origError)
可組合的正則表達式:
re`/^${RE_YEAR}-${RE_MONTH}-${RE_DAY}$/u`
正則表達式的轉義文本(對于.replace()很重要):
> const re = new RegExp(RegExp.escape(":-)"), "ug"); > ":-) :-) :-)".replace(re, "
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101983.html
摘要:編程中經(jīng)常定義一些短小的匿名函數(shù),使用箭頭函數(shù)語法可使得這類函數(shù)的定義更加簡潔。外部函數(shù)的,等價于定義個局部變量修改內(nèi)部的所以這樣,,也就無法修改箭頭函數(shù)的值的。拋異常即箭頭函數(shù)不能作為構造函數(shù),其也不具有屬性。 一、概述 箭頭函數(shù)是指通過=>語法定義的函數(shù)。JS編程中經(jīng)常定義一些短小的匿名函數(shù),使用箭頭函數(shù)語法可使得這類函數(shù)的定義更加簡潔。 // ES3/5方式 var fun1 =...
摘要:接下來,我會檢查每個頁面以確保它使用有用的標簽,包括標題標簽。這個問題讓面試官有機會了解潛在員工對工作難以勝任的部分。面試官可能需要考慮這種弱點對團隊的影響。面試官可能會發(fā)現(xiàn)自己公司的計劃與未來員工的職業(yè)目標是否保持一致。 翻譯:瘋狂的技術宅原文:https://www.indeed.com/hire/i... 本文首發(fā)微信公眾號:jingchengyideng歡迎關注,每天都給你...
摘要:在整個年,看到發(fā)布版增加了許多功能,包括新的生命周期方法新的上下文指針事件延遲函數(shù)和。它在等待渲染異步響應時數(shù)據(jù),是延遲函數(shù)背后用來管理組件的代碼分割的。發(fā)布自第版開始將近年后,于年發(fā)布。 前端發(fā)展發(fā)展迅速,非常的快。 本文將回顧2018年一些重要的前端新聞,事件和 JavaScript 趨勢。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! showImg(ht...
摘要:并且于年月日,四個主要的瀏覽器一致同意宣布的版本已經(jīng)完成,即將推出一個瀏覽器可以搭載的穩(wěn)定版本。因此本文著重介紹為什么比更快。本文主要表達的是為什么應該是更快的。則不同,它是由幾大主要的瀏覽器廠商共同設計的。 作者:Alon Zakai 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId=58ce80d2a6d8a0...
閱讀 1688·2021-10-13 09:39
閱讀 3160·2021-10-12 10:11
閱讀 554·2021-09-28 09:36
閱讀 2637·2019-08-30 15:55
閱讀 1390·2019-08-30 13:04
閱讀 631·2019-08-29 17:08
閱讀 1909·2019-08-29 14:14
閱讀 3403·2019-08-28 18:23