本帖最后由 小刀葛小伦 于 2019-5-30 15:56 编辑
一. 值
1.按值比较对象
目前,JavaScript 只能对原始 值(value)进行比较,例如 字符串的值(通过查看其内容):
[JavaScript] 纯文本查看 复制代码 1> 'abc' === 'abc'
2true
相反,对象则通过身份ID(identity)进行比较(对象仅严格等于自身): [JavaScript] 纯文本查看 复制代码 1> {x: 1, y: 4} === {x: 1, y: 4}
2false 如果有一种能够创建按值进行比较对象的方法,那将是很不错的: [JavaScript] 纯文本查看 复制代码 1> #{x: 1, y: 4} === #{x: 1, y: 4}
2true 另一种可能性是引入一种新的类(确切的细节还有待确定): [JavaScript] 纯文本查看 复制代码 1@[ValueType]
2class Point {
3 // ···
4} 旁注:这种类似装饰器的将类标记为值类型的的语法基于草案提案。 2.将对象放入数据结构中 如果对象通过身份ID进行比较,将它们放入 ECMAScript 数据结构(如Maps)中并没有太大意义: [JavaScript] 纯文本查看 复制代码 1const m = new Map();
2m.set({x: 1, y: 4}, 1);
3m.set({x: 1, y: 4}, 2);
4assert.equal(m.size, 2); 可以通过自定义值类型修复此问题。 或者通过自定义 Set 元素和 Map keys 的管理。 例如: 3.大整数JavaScript 的数字总是64位的(双精度),它能为整数提供53位二进制宽度。这意味着如果超过53位,就不好使了:
[JavaScript] 纯文本查看 复制代码 1> 2 ** 53
29007199254740992
3> (2 ** 53) + 1 // can’t be represented
49007199254740992
5> (2 ** 53) + 2
69007199254740994
对于某些场景,这是一个相当大的限制。现在有[BigInts提案](http://2ality.com/2017/03/es-integer.html),这是真正的整数,其精度可以随着需要而增长:
[JavaScript] 纯文本查看 复制代码 1> 2n ** 53n
29007199254740992n
3> (2n ** 53n) + 1n
49007199254740993n
BigInts还支持 casting,它为你提供固定位数的值:
[JavaScript] 纯文本查看 复制代码 1const int64a = BigInt.asUintN(64, 12345n);
2const int64b = BigInt.asUintN(64, 67890n);
3const result = BigInt.asUintN(64, int64a * int64b);
4.小数计算
JavaScript 的数字是基于 IEEE 754 标准的64位浮点数(双精度数)。鉴于它们的表示形式是基于二进制的,在处理小数部分时可能会出现舍入误差:
[JavaScript] 纯文本查看 复制代码 1> 0.1 + 0.2
20.30000000000000004
这在科学计算和金融技术(金融科技)中尤其成问题。基于十进制运算的提案目前处于阶段0。它们可能最终被这样使用(注意十进制数的后缀 m):
[JavaScript] 纯文本查看 复制代码 1> 0.1m + 0.2m
20.3m
5.对值进行分类
目前,在 JavaScript 中对值进行分类非常麻烦:
[JavaScript] 纯文本查看 复制代码 1> typeof null
2'object'
3> typeof function () {}
4'function'
5> typeof []
6'object'
第三,instanceof不适用于来自其他realm(框架等)的对象。
也许可能通过库来解决这个问题(如果我有时间,就会实现一个概念性的验证)。
二、函数式编程
1.更多表达式
[JavaScript] 纯文本查看 复制代码 1// 条件表达式
2let str1 = someBool ? 'yes' : 'no';
3
4// 条件声明
5let str2;
6if (someBool) {
7 str2 = 'yes';
8} else {
9 str2 = 'no';
10}
特别是在函数式语言中,一切都是表达式。 Do-expressions 允许你在所有表达式上下文中使用语句:
[JavaScript] 纯文本查看 复制代码 1let str3 = do {
2 if (someBool) {
3 'yes'
4 } else {
5 'no'
6 }
7};
下面的代码是一个更加现实的例子。如果没有 do-expression,你需要一个立即调用的箭头函数来隐藏范围内的变量 result :
[JavaScript] 纯文本查看 复制代码 1const func = (() => {
2 let result; // cache
3 return () => {
4 if (result === undefined) {
5 result = someComputation();
6 }
7 return result;
8 }
9})();
使用 do-expression,你可以更优雅地编写这段代码:
[JavaScript] 纯文本查看 复制代码 1const func = do {
2 let result;
3 () => {
4 if (result === undefined) {
5 result = someComputation();
6 }
7 return result;
8 };
9};
2.匹配:结构switch
JavaScript 使直接使用对象变得容易。但是根据对象的结构,没有内置的切换 case 分支的方法。看起来是这样的(来自提案的例子):
[JavaScript] 纯文本查看 复制代码 1const resource = await fetch(jsonService);
2case (resource) {
3 when {status: 200, headers: {'Content-Length': s}} -> {
4 console.log(`size is ${s}`);
5 }
6 when {status: 404} -> {
7 console.log('JSON not found');
8 }
9 when {status} if (status >= 400) -> {
10 throw new RequestError(res);
11 }
12}
正如你所看到的那样,新的 case 语句在某些方面类似于 switch,不过它使用解构来挑选分支。当人们使用嵌套数据结构(例如在编译器中)时,这种功能非常有用。 模式匹配提案【https://github.com/tc39/proposal-pattern-matching】目前处于第1阶段。
3.管道操作
管道操作目前有两个竞争提案 。在本文,我们研究其中的 智能管道(另一个提议被称为 F# Pipelines)。 管道操作的基本思想如下。请考虑代码中的嵌套函数调用。 [JavaScript] 纯文本查看 复制代码 1const y = h(g(f(x))); 但是,这种表示方法通常不能体现我们对计算步骤的看法。在直觉上,我们将它们描述为: 从值 x 开始。 然后把 f() 作用在 x 上。 然后将 g() 作用于结果。 然后将 h() 应用于结果。 最后将结果赋值给 y。
管道运算符能让我们更好地表达这种直觉: [JavaScript] 纯文本查看 复制代码 1const y = x |> f |> g |> h; 换句话说,以下两个表达式是等价的。 [JavaScript] 纯文本查看 复制代码 1f(123)
2123 |> f 另外,管道运算符支持部分应用程序(类似函数的 .bind() 方法):以下两个表达式是等价的。 [JavaScript] 纯文本查看 复制代码 1123 |> f(#)
2123 |> (x => f(x))
使用管道运算符一个最大的好处是,你可以像使用方法一样使用函数——而无需更改任何原型: [JavaScript] 纯文本查看 复制代码 1import {map} from 'array-tools';
2const result = arr |> map(#, x => x * 2); 最后,让我们看一个长一点的例子(取自提案并稍作编辑): [JavaScript] 纯文本查看 复制代码 1promise
2|> await #
3|> # || throw new TypeError(
4 `Invalid value from ${promise}`)
5|> capitalize // function call
6|> # + '!'
7|> new User.Message(#)
8|> await stream.write(#)
9|> console.log // method call
10;
|