JavaScript 模擬向量運算符重載
來自: http://www.w3ctech.com/topic/1695
寫在前面, 本文中實現的運算符重載有很多局限, 完全不能實用, 僅供燒腦和娛樂.
</div>
很久以前做過比較完善的 函數重載實現 , 然后自從知道 valueOf 這個東西, 就一直有想搞運算符重載的念頭. 搜了下居然搜到自己 11 年發的 帖子 , 對 valueOf 進行了簡單的利用.
前些時候在公司做分享, 主題是 WAT JavaScript Explained, 最后是以 JavaScript 嘗試重載向量的 +-*/ 運算符結束的, 下文則是在分享中設計的效果與方法.
效果
第一種實現
Vector.createVector('a', 1, 2); Vector.createVector('b', 4, 6); Vector.createVector('c');c = a + b; c = a - b; c = a * 2; c = b / 4;</pre>
第二種實現
Vector.createVector('a', 1, 2); Vector.createVector('b', 4, 6); Vector.createVector('c', 2, 1); Vector.createVector('d');d = a + b - c; d = -a - b + c;</pre>
文中的技巧還可以實現類似的效果:
let a = new Vector(1, 2); let b = new Vector(4, 6); let c = new Vector(4, 6); let d = calc(a + b - c);方法
細心的同學可能已經發現, 上面提到的例子中, 要么使用了全局變量, 要么需要包裹一個函數 calc . 通過重寫 valueOf 方法, 我們可以知道哪些向量參與了運算. 但僅僅如此, 還無法知道參與運算的運算符, 也無法知道第一種實現中類似于 a * 2 和 b / 4 中的 2 和 4 . 但如果我們可以獲得整個表達式的值, 就可以從某種程度窺探發生了怎樣的運算.
兩個向量的 + 和 -
前面我們提到了重寫 valueOf 方法獲取參與了運算的向量, 但為了知道發生了怎樣的運算, 我們還需要為 valueOf 構造特別的返回值. 比如第一個參與運算的向量 a 對應的值為 1 , 第二個向量 b 對應的值為 2 , 那么 a + b 的值則為 3 , a - b 則為 -1 . 這樣一來, 通過 setter 或者 calc 函數我們就可以根據表達式的值得知發生了什么運算.
雖然只提到了 + 和 - , 同樣的方法我們還可以實現向量的叉乘.
一個向量一個標量的 * 和 /
對于四則運算中的原始值, JavaScript 并不會調用對應的 valueOf . 這樣一來, 我們則需要爭取通過獲得的表達式的值獲取運算符和標量. 或者, 或許我們并不需要知道到底是哪一個運算符, 只需要知道這個向量需要縮放的比例. 上面我們提到了第一個參與運算的向量 valueOf 的值是 1 , 這自然而然地帶來了一個好處: 表達式 a * n ( a 為向量, n 為標量) 的值是 n , b / m ( b 為向量, m 為標量) 的值則是 1 / m . 向量需要縮放的比例正好是表達式最后的值! 現在知道了參與運算的向量和它需要被縮放的比例, 也就自然可以計算出結果.
多個向量的 + 和 -
上面提到的方式中, 構造 valueOf 的值相對簡單, 只要不為零都是可以的. 然而很顯然, 如果向量數量大于 2, 這種構造的方式得出的表達式的值很難再推出發生了哪些運算. 不過僅僅是針對 + 和 - 兩種運算, 我們還是有辦法區分一定數量的向量參的運算.
思考表達式 a + b - c + d - e , 怎樣可以使它的值包含所有的運算符信息? 可能有的同學也會立即把它和 flags 或者 enums 之類的聯系到一起:
let flagA = 0b0001; let flagB = 0b0010;let flagAB = flagA | flagB; // 0b0011</pre>
如果我們可以把類似的性質應用到加減上, 就可以保留一定數量的運算符信息了. 考慮三進制數的加減: 1111 + 1 - 10 + 100 - 1000 = 0202. 和表達式 a + b - c + d - e 聯系起來, 0202 中的四個數字分別對應了 - ( e ), + ( d ), - ( c ) 和 + ( b ).
這樣一來, 我們為第一個參與運算的向量構建足夠大的每一位都為 1 的三進制數作為 valueOf 的返回值, 為隨后參與運算的向量分別構建 1, 10, 100 這樣的三進制數, 就可以通過表達式的值獲知幾十個參與運算的向量對應的符號了. 對于首個元素前面有負號的情況, 則可以通過整個表達式的正負來做區分.
實現
上面提到的兩種效果的實現都可以在這個倉庫找到: vilic/js-operator-overloading .
![]()
掃碼關注w3ctech微信公眾號