Maybe 使 ECMAScript 更安全

栏目: JavaScript · 发布时间: 6年前

内容简介:ECMAScript 為 Dynamic Type Language,Function 的 Parameter 並不用指定 Type Hint,因此可以傳入任何 Type,理論上必須在 Runtime 使用VS Code 1.33.1Quokka 1.0.212

ECMAScript 為 Dynamic Type Language,Function 的 Parameter 並不用指定 Type Hint,因此可以傳入任何 Type,理論上必須在 Runtime 使用 typeof 做 Type Check,否則依賴 Type Coercion 很容易產生 Bug; Mabye 提供了另外一種方式:只會將正確 Type 進行運算,而不需使用 typeof 檢查。

Version

VS Code 1.33.1

Quokka 1.0.212

Croks 0.11.1

Number

let inc = x => x + 1;
let fn = n => inc(n);

console.log(fn(2));

很簡單的 inc() ,當輸入為 number 2 時,毫無懸念結果為 3

Maybe 使 ECMAScript 更安全

String

let inc = x => x + 1;
let fn = n => inc(n);

console.log(fn('8'));

但傳入改為 string '8' 時,結果為 81 ,且 81 為 string,並不是預期的 number

由於 ECMAScript 為 dynamic type language,因此可以傳入任何 type,而當 + 遇到 string 時,會將兩個 operand 都轉成 string+ 從原本的 add() 變成 concat() ,因此最後結果為 string 81

Maybe 使 ECMAScript 更安全

Undefined

let inc = x => x + 1;
let fn = n => inc(n);

console.log(fn(undefined));

當傳入為 undefined 時,結果為 NaN

81NaN 都不是我們預期結果,要避免這些情形發生,唯一的方法就是確認輸入只能是 number ,其他 type 都不可輸入

Maybe 使 ECMAScript 更安全

typeof

let inc = x => x + 1;
let fn = n => typeof n === 'number' ? inc(n) : 0;

console.log(fn(2));
console.log(fn('8'));
console.log(fn(undefined));

為了確保輸入一定是 number ,我們當然可以在 fn() 加上 typeof 判斷,確定是 number 才執行 inc() ,否則傳回 default 值 0

如此結果就不會是 stringNaN 這些不是我們預期結果。

但這種寫法只能算是 short term solution:

  • 每個 function 都必須檢查其 parameter type,弄髒了原本邏輯
  • 無法在一般情況就發現錯誤
  • 必須考驗 coder 的細心程度,只要不夠細心,unit test 一樣測不到

Maybe 使 ECMAScript 更安全

Wrap into Maybe

import { Maybe } from 'crocks';

let inc = x => x + 1;
let fn = n => n.map(inc);

console.log(fn(Maybe.Just(2)));

引進了 Maybe type,它只有兩種值: JustNothing

Just
Nothing

以本例而言,我們預期 type 是 number ,是故為 Just ;而 stringundefined 都不是我們預期 type,是故為 Nothing

import { Maybe } from 'crocks';

crocks import 進 Maybe object,它提供了我們使用 Maybe 所需要的 function。

第 4 行

let fn = n => n.map(inc);

Argument nMaybe ,而 Maybe 自帶 map() ,可傳入 function, map() 會幫我們將 Maybe 內的 value 透過傳入的 inc() 改變之,最後回傳 Maybe

Maybe 使 ECMAScript 更安全

由於 fn() 回傳為 Maybe ,因此印出 Just 3 ,而非原本的 3

目前我們已經將 number 包進 Maybe ,但印出也是 Maybe ,而非原本的 number 3 ,稍後會從 Maybe 萃取出 number

Just

import { Maybe } from 'crocks';

let inc = x => x + 1;
let fn = n => n.map(x => console.log('calling inc()') || inc(x));

console.log(fn(Maybe.Just(2)));

我們雖然將 inc() 傳入 Maybe.map() ,但 inc() 真的有被執行嗎 ?

第 4 行

let fn = n => n.map(x => console.log('calling inc()') || inc(x));

特別在 Maybe.map() 的 callback 加上 console.log() ,測試 inc() 是否有被執行。

由於 console.log() 會回傳 undefined ,視為 falsy value,因此一定會執行 || 右側的 inc(x)

console.log() 配合 || 為 ECMAScript 常用測試 callback 手法

Maybe 使 ECMAScript 更安全

印出了 calling inc() ,顯示了若輸入為 Just , 真的有執行 Mapbe.map() 的 callback。

Nothing

import { Maybe } from 'crocks';

let inc = x => x + 1;
let fn = n => n.map(x => console.log('calling inc()') || inc(x));

console.log(fn(Maybe.Nothing()));

inc()fn() 完全不變,只是改將 Nothing 傳入 fn()

Maybe 使 ECMAScript 更安全

沒顯示 calling inc() ,表示 inc() 根本沒執行,直接回傳 Nothing

只有 Just 才會執行我們 map() 的 function, Nothing 完全不執行,這確保了只有正確 type 才會執行 function,不需要我們自己做 typeof 檢查,也不會產生不是預期結果

safeNum()

import { Maybe } from 'crocks';

let inc = x => x + 1
let safeNum = v => typeof v === 'number' ? Maybe.Just(v) : Maybe.Nothing();
let fn = n => safeNum(n).map(inc);

console.log(fn(2));
console.log(fn('8'));
console.log(fn(undefined));

第 5 行

let fn = n => safeNum(n).map(inc);

JustNothing 我們可發現,只要我們能將 value 包進 Maybe ,儘管不做 typeof 檢查,也能確保不會有不預期結果,所以我們需要 safeNum() 將任何值包進 Maybe

第 4 行

let safeNum = v => typeof v === 'number' ? Maybe.Just(v) : Maybe.Nothing();

使用 typeof 判斷是否為 number ,若是 number ,則包成 Just ,否則包成 Nothing

Q:到底還是使用了 typeof ?

底層雖然會使用 typeof ,但即將包成 higher order function,請繼續看下去。

Maybe 使 ECMAScript 更安全

Higher Order Function

import { Maybe } from 'crocks';

let inc = x => x + 1
let isNumber = v => typeof v === 'number';
let safe = pred => v => pred(v) ? Maybe.Just(v) : Maybe.Nothing();

let fn = n => safe(isNumber)(n).map(inc);

console.log(fn(2));
console.log(fn('8'));
console.log(fn(undefined));

第 4 行

let isNumber = v => typeof v === 'number';

number 寫死在 safeNum() 當然不好,可將此部分抽成 higher order function,將來可透過 function compostion 組合出 safeNum() ,重複使用程度更高。

第 5 行

let safe = pred => v => pred(v) ? Maybe.Just(v) : Maybe.Nothing();

safeNum() 重構成更一般性的 safe() higher order function,可透過傳入 predicate 組合出能將各種 type 包進 Maybe 的 function。

第 7 行

let fn = n => safe(isNumber)(n).map(inc);

safe(isNumber) 組合出 safeNum() ,如此可安全將任何值包進 Maybe

Maybe 使 ECMAScript 更安全

Helper Function

import { inc } from 'ramda';
import { Maybe, safe, isNumber } from 'crocks';

let fn = n => safe(isNumber)(n).map(inc);

console.log(fn(2));
console.log(fn('8'));
console.log(fn(undefined));

事實上 safe()isNumber() ,Crocks 都提供了,可直接使用。

inc() 以順便使用 Ramda 版本。

目前只剩下最後一哩路: fn() 回傳為 Maybe 需要解決

Maybe 使 ECMAScript 更安全

Unwrap from Maybe

import { inc } from 'ramda';
import { Maybe, safe, isNumber } from 'crocks';

let fn = n => safe(isNumber)(n).map(inc).option(0);

console.log(fn(2));
console.log(fn('8'));
console.log(fn(undefined));

Crocks 提供了 Maybe.options() ,讓我們提供當 MaybeNothing 時該如何處理,因為 Nothing 完全不會經過 inc() 運算。

Maybe 使 ECMAScript 更安全

fn() 的 signature 完全沒變,結果也完全沒變。

Comparison

比較這兩種寫法,我們得到了什麼 ?

Imperative

let fn = v => typeof v === 'number' ? inc(v) : 0;

我們必須時時刻刻想著要使用 typeof 檢查,但儘管粗心沒檢查, 一般情況 也不會錯,但特殊狀況就會錯,這就是 bug 來源。

Functional

let fn = n => safe(isNumber)(n).map(inc).option(0);

若使用 FP 的 Maybe ,思維就不一樣了:

  • 先組合出能包進 Maybe 的 function,因此會先寫出 safe(isNumber) ,就算你很粗心也被逼的要寫出來,否則無法繼續
  • 接下來要使 Maybe 能經過運算,一定得使用 map() 傳入 function
  • 若很粗心忘記寫 option() ,一般情況顯示 Maybe 就是錯的,一定會回來補上 option(0)

也就是每個步驟都被逼著走,且在 一般情況 就會發現錯誤,不像 imperative 的 typeof 在一般情況不會錯,只有在特殊狀況才會出錯,這就是 bug 來源。

Maybe 還可讓你在寫 unit test 時不用想很奇怪的 test case,因為 Maybe 會確保不會產生不預期結果。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTTP Essentials

HTTP Essentials

Stephen A. Thomas、Stephen Thomas / Wiley / 2001-03-08 / USD 34.99

The first complete reference guide to the essential Web protocol As applications and services converge and Web technologies not only assume HTTP but require developers to manipulate it, it is be......一起来看看 《HTTP Essentials》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器