今天来给大家介绍 JavaScript 代码的一个新运算符:管道运算符 |>。
当我们在 JavaScript 中对一个值执行连续操作(例如函数调用)时,目前有两种基本方式:
在 2020 年 JS 状态调查中,“你认为 JavaScript 目前缺少什么?“ 问题中,希望拥有管道操作符 答案排行第四名。
看来大家当前对 JS 中连续操作的写法还是不太满意啊。
首先,如果是嵌套写法的话,简单的嵌套还好,但是当嵌套变得很深的时候就有点难以阅读了。嵌套的执行流程是从右到左移动的,而不是我们正常阅读代码从左到右的方向。另外,我们在很多括号之间找到一个位置添加一些参数也比较困难。比如下面的代码:
console.log(
chalk.dim(
`$ ${Object.keys(envars)
.map(envar =>
`${envar}=${envars[envar]}`)
.join(' ')
}`,
'node',
args.join(' ')));
对于链式调用,只有我们把方法指定为值的实例方法时才能用,这让它具有很大的局限性。当然,如果你的库设计的很好(比如 jquery) 还是挺好用的。
Unix 操作系统有一个管道机制,可以把前一个操作的值传给后一个操作。这个机制非常有用,使得简单的操作可以组合成为复杂的操作。许多语言都有管道的实现,举个简单的例子:
function capitalize (str) {
return str[0].toUpperCase() + str.substring(1);
}
function hello (str) {
return str + ' Hello!';
}
上面是两个简单的函数,想要嵌套执行,传统写法和管道写法分别如下:
// 传统的写法
exclaim(hello('conardli'))
// "Conardli Hello!"
// 管道的写法
'conardli'
|> capitalize
|> hello
// "Conardli Hello!"
关于管道运算符,目前在 ES 中有两个相互竞争的提案:
目前来看,Meta 提出的 Hack 应该更收社区的欢迎,Microsoft 提出的 F# 已经多次被 TC39 打回去了。不过不用担心,F# 的优势后续也可能会引入 Hack 中。
下面我们分别来看看两个提案的用法吧。
下面是一个 Hack 管道运算符 |> 的简单示例:
'ConardLi' |> console.log(%) // ConardLi
管道运算符 |> 的左侧是一个表达式,它被计算并成为特殊变量 % 的值。我们可以在右侧使用该变量。返回右侧的执行结果。前面的例子等价于:
console.log('ConardLi') // ConardLi
下面还有一些和其他写法配合的例子:
value |> someFunction(1, %, 3) // function calls
value |> %.someMethod() // method call
value |> % + 1 // operator
value |> [%, 'b', 'c'] // Array literal
value |> {someProp: %} // object literal
value |> await % // awaiting a Promise
value |> (yield %) // yielding a generator value
下面我们再来看个更复杂点的例子,一个嵌套函数调用:
const y = h(g(f(x)));
Hack pipe 操作符可以让我们更好地表达这段代码的意思:
const y = x |> f(%) |> g(%) |> h(%);
这段代码更符合我们常规的编码思想,代码从左到右依次执行:f、g、h。
F# 管道运算符与 Hack 管道运算符大致相似。但是,它没有特殊变量 %。相反,运算符右侧的函数并会直接应用于其左侧。因此,以下两个表达式是等价的:
'ConardLi' |> console.log
console.log('ConardLi')
因此 F# 管道运算符更适合单参数的函数,下面三个函数是等价的:
const y = h(g(f(x))); // no pipe
const y = x |> f(%) |> g(%) |> h(%); // Hack pipe
const y = x |> f |> g |> h; // F# pipe
在这种情况下,Hack pipe 比 F# pipe 更冗长。
但是,如果是多参数的情况下,F# pipe 的写法就要复杂一点了:
5 |> add2(1, %) // Hack pipe
5 |> $ => add2(1, $) // F# pipe
可以看到,F# pipe 还要多写一个匿名函数,这显然相对与 Hack pipe 来讲缺失了一些灵活性。这可能也是大家更倾向于 Hack pipe 的原因。
(1) 嵌套函数调用的扁平写法
JavaScript 标准库创建的所有迭代器都有一个共同的原型。这个原型是不能直接访问的,但我们可以像这样检索它:
const IteratorPrototype =
Object.getPrototypeOf(
Object.getPrototypeOf(
[][Symbol.iterator]()
)
)
;
使用管道运算符,代码会更容易理解一些:
const IteratorPrototype =
[][Symbol.iterator]()
|> Object.getPrototypeOf(%)
|> Object.getPrototypeOf(%)
;
(2) 后期处理
看看下面的代码:
function myFunc() {
// ···
return conardLi.someMethod();
}
如果现在我们想在函数返回前对返回值做一些其他的操作,我们应该怎么办呢?
在以前我们肯定要定义一个临时变量或者在函数外侧再包一个函数,使用管道运算符,我们可以这样做:
function myFunc() {
// ···
return theResult |> (console.log(%), %); // (A)
}
在下面的代码中,我们后处理的值是一个函数 — 我们可以向它添加一个属性:
const testPlus = () => {
assert.equal(3+4, 7);
} |> Object.assign(%, {
name: 'Test the plus operator',
});
前面的代码等价于:
const testPlus = () => {
assert.equal(3+4, 7);
}
Object.assign(testPlus, {
name: 'Testing +',
});
我们也可以像这样使用管道运算符:
const testPlus = () => {
assert.equal(3+4, 7);
}
|> (%.name = 'Test the plus operator', %)
;
我们可以用 Array 的一些方法例如 .filter()和 .map() 实现链式调用,但是这仅仅是内置在数组里的一些方法,我们没办法通过库引入更多的 Array 方法。
使用管道运算符,我们可以像数组本身的方法一样实现一些其他方法的链式调用:
import {Iterable} from '@rauschma/iterable/sync';
const {filter, map} = Iterable;
const resultSet = inputSet
|> filter(%, x => x >= 0)
|> map(%, x => x * 2)
|> new Set(%)
;
最后再回来看看标题的代码:
const regexOperators =
['*', '+', '[', ']']
.map(ch => escapeForRegExp(ch))
.join('')
|> '[' + % + ']'
|> new RegExp(%)
;
实际上就等价于:
let _ref;
const regexOperators =
(
(_ref = ['*', '+', '[', ']']
.map(ch => escapeForRegExp(ch))
.join('')),
new RegExp(`[${_ref}]`)
);
和引入中间变量相比,管道运算符是不是更易于阅读且简洁呢。
来源: code秘密花园
... 运算符, 是ES6里一个新引入的运算法, 也叫展开/收集 运算符, 我们每天都要和它打交道。这篇文章,我就带你系统的回顾下这个运算符, 介绍一些基础和进阶的用法。
JavaScript是一门了不起的语言。我喜欢它的灵活性:只需以你喜欢的方式做事:更改变量类型,动态的向对象添加方法或属性,对不同的变量类型使用运算符等等。然而动态是要付出代价的,开发人员需要知道怎样处理对于不同操作符的类型转换
TypeScript 2.1 增加了对 对象扩展运算和 rest 属性提案的支持,该提案在 ES2018 中标准化。可以以类型安全的方式使用 rest 和 spread 属性。对象 rest 属性假设已经定义了一个具有三个属性的简单字面量对象
我们可以使用展开操作符复制数组,不过要注意的是这是一个浅拷贝。这样我们就可以复制一个基本的数组,注意,它不适用于多级数组或带有日期或函数的数组。
JS 里的操作符大家每天都在使用,还有一些 ES2020、ES2021 新加的实用操作符,这些共同构成了 JS 灵活的语法生态
你有没有花一个下午的时间浏览过 Mozilla 文档?如果你有,你会很清楚网上有很多关于 JavaScript 的信息。这使得人们很容易忽略一些不同寻常的 JavaScript 操作符。
扩展运算符( spread )是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。array.push(...items)和add(...numbers)这两行,都是函数的调用,它们的都使用了扩展运算符。该运算符将一个数组,变为参数序列。
JavaScript使用符号三个点(...)作为剩余运算符和展开运算符,不过这两个运算符是有区别的。最主要的区别就是,剩余运算符将用户提供的某些特定值的其余部分放入JavaScript数组中
很多人都对双竖杠||非常熟悉,因为这个经常在项目中经常会用到。单竖杠|,却很少在项目开发中使用到。|是位运算符,||是逻辑运算符。平常,经常使用以下这个几个方法对数字进行处理。
Spread 和 Rest 是 ES6 Javascript 提供的两个功能,分别主要用于解构和函数参数处理。Spread 从可迭代对象(如数组、字符串或对象)中获取元素并将它们分散到各个部分。这就像将一副纸牌铺在桌子上一样。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!