在JavaScript中是否可以创建自定义运算符?

45

在数学课上,我们学习了如何定义新的运算符。例如:

(ℝ, ∘),x ∘ y = x + 2y

这定义了 运算。对于任意实数 xyx ∘ y 等于 x + 2y

例如:2 ∘ 2 = 2 + 4 = 6


在 JavaScript 中是否可以定义这样的运算符呢?我知道一个函数可以完成这个任务:

function foo (x, y) { return x + 2 * y; }

但我希望具有以下语法:

var y = 2 ∘ 2; // returns 6

不要这样:

var y = foo(2, 2);

哪个是最接近这个问题的解决方案?


4
另一个名称是中缀函数符号表示法——答案是否定的,你不能。 - Eric
你可以尝试使用现有的运算符来利用JavaScript中的valueOf,但这就是全部了。 - elclanrs
2
我认为想要定义一个不在我的键盘上的符号作为运算符是一个非常糟糕的想法。 - Eric
1
不,你不能在JavaScript中这样做。但是,在Haskell中可以。第一行:infixl 6 ∘。第二行:x ∘ y = x + 2 * y - Aadit M Shah
从技术上讲,你可以通过编写自己的词法解析器并在<script>块中定义自己的脚本类型名称来实现。这种技术被广泛使用。一些知名的例子包括Google Traceur - Derek 朕會功夫
11个回答

30
翻译如下:

简短的回答是不行。ECMAScript(JS所基于的标准)不支持运算符重载。

顺便提一下,ECMAScript 7中,你将能够在设计自定义值类型时重载一部分标准运算符。这里有一篇由语言创造者和Mozilla CTO Brendan Eich撰写的幻灯片关于这个主题。然而,这不会允许任意的运算符,并且重载的含义只会应用于值类型。 <- 哈哈,这最终没有发生。

可以使用第三方工具,如sweet.js添加自定义运算符,但这需要额外的编译步骤。

我已经给出了一个来自JavaScript之外的解决方案,使用了esprima-这是改变和扩展JavaScript,而不是本地支持。


你能添加一个引用吗?在它实现后,我该如何定义这样的操作符? - Ionică Bizău
我怀疑任何版本的ECMAScript都没有定义新符号运算符的能力 - 这通常是保留给像Haskell这样的语言。我猜它只会带有操作符_重载_。 - Eric
@Eric,Haskell所做的并不是魔法。在JS中引入反引号作为中缀符号并没有什么问题 :) 但是,当前的提案确实讨论了运算符重载。 - Benjamin Gruenbaum
@BenjaminGruenbaum:当然,这不是魔法,但定义符号操作符是一个滑坡到perl的方式。 - Eric
1
我在ECMAScript值对象页面上没有看到任何提及用户代码可以声明新值类型的内容。 - Eric
显示剩余4条评论

8

在ES6中新增的标记模板字面量功能可以创建自定义DSL来处理嵌入式表达式,包括各种代数符号。

例如(在stackblitz中运行):

function math(strings, x, y) {
  // NOTE: Naive approach as demonstration

  const operator = strings[1].replace(/\s/gi, "");

  if (operator == "∘") {
    return x + 2 * y;
  }
  else if (operator == "^") {
    return Math.pow(x, y);
  }
  else {
    return `Unknown operator '${operator}'`;
  }
}

console.log(math`${2}${2}`)

请注意,由于标记模板文字并不一定返回字符串作为结果,它们可以返回更复杂的中间AST结构来构建表达式,然后可以进一步细化并保持与手头的特定领域语言紧密联系,然后进行解释。我还没有找到任何现有的Javascript库可以做到这一点,但从我对标记模板文字和在诸如lit-html等地方使用的了解来看,这应该是一个有趣且相当易于实现的尝试。

5

不行,在JS中你做不到那个。

我认为最接近的方法是实现你自己的对象,具有可链接的接口,即"流畅"语法。这样你就可以像按顺序说话一样操作。

var eq = new YourEquationObject();

// Then operate like 1 - 2 * 3
eq.add(1).sub(2).mul(3);

具体细节由您决定。我只是提供一个想法。


5
您可以通过在Number.prototype方法中添加伪运算符来实现:
Object.defineProperty(Number.prototype, 'myOp', {
    value: function(that) {
        return this + 2 * that;
    }
});

然后所有这些语法都可以正常工作。
alert( (2).myOp(2) )
alert( 2 .myOp(2) )
alert( 2..myOp(2) )
alert( 2.0.myOp(2) )

2.myOp(2)不能工作,因为句点被视为小数点。


3
不,JavaScript不支持操作符重载。但是你可以创建一个类方法来完成这个功能。
var mathClass = function(value){
   this.value = value;
}

mathClass.prototype.toLaw = function(){
   return 2 * this.value;
}

var y = new mathClass(2)
2 + y.toLaw(); //2 + 2 * y

你可以更进一步,将它们添加到Number.prototype中,就像我的答案中所示。 - Eric

2

请阅读答案下面的评论。

显然你不能这样做。这里有一个类似的方法:

function exec(input) {
    return Function(
        'return ' + input.replace(/∘( *[\d.]+)/g, '+ 2 * $1') + ';'
    )();
}

exec('2 ∘ 2'); // 6

8
"(1 + 1) ∘ (1 + 1)" 会毁掉你的一天。 - Eric
1
Paperscript非常彻底地完成了这个任务。 - Eric
@Eric 的方法不错,但是 '2 ∘ 2' 是一个字符串。我希望它不是一个字符串。 - Ionică Bizău
1
@イオニカビザウ:这个想法是你将一个脚本标记为 language="myjavascript",然后有一个小脚本在运行时将你的代码转换成原始的JavaScript。 - Eric
要做到正确,您需要像acornesprima这样的JavaScript AST库。如果您想添加新的符号运算符,则可能需要稍微调整解析器。 - Eric

2

一些编译成JS的语言支持自定义操作符。

我想强调 ReasonML (ocaml-采用JS人可读的语法) 和 Bucklescript (ocaml-to-js-编译器),这使得自定义操作符看起来很整洁:

例如,一个用于连接字符串的操作符可以像这样:

let (>|<) = (list, seperator) => Belt.List.reduce(list, "", (a, b) => a ++ seperator ++ b);

然后可以像这样使用:

[Styles.button, Media.Classes.atLeastTablet] >|< " "

所有这些的不足之处是,它必须用编译为js的语言编写,并且它有很多优点和缺点,但通常这些语言具有大量漂亮的语法糖,而这些在js/ts中无法得到。

1
略长于短句的答案是可以,但与数学课上所做的有些不同。要使用类似http://esprima.org/的转换器来扩展语言并定义您的语法,编写解析器以解析语句,最后添加实际代码以执行数学部分。当这些部分就位时,您已经创建了一个新语言,它的工作方式与JavaScript相同,但增加了运算符的支持。添加新语法真的不是太难,以下是Facebook添加=>箭头函数语法的示例。

https://github.com/facebook/jstransform/blob/master/visitors/es6-arrow-function-visitors.js


0

我也曾经问过这个问题,以下是我的答案:

function myCustomOperation(string){
var res = String.fromCharCode(9762);
var thing = string.replace(/☢/g," + 10 + ");
thing=thing.replace(/☣/g," * 2 + ");
thing=thing.replace(/☠/g," / 3 * ");
return [eval(thing),string+" = "+eval(thing)];
};
var thing = myCustomOperation("

3 ☠ 4

");
document.write(thing[1]);

0

有几个工具、编译器或语言扩展提供了这样的可能性,甚至使用表情符号和其他在 JavaScript 中不常见的东西。我能想起来最接近的是 sweet.js,还有另一个JavaScript编译工具可能也很有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接