Maybe 使 ECMAScript 更安全

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

内容简介: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 會確保不會產生不預期結果。


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

查看所有标签

猜你喜欢:

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

深入理解 Flask

深入理解 Flask

[美]Jack Stouffer / 苏丹 / 电子工业出版社 / 2016-7-1 / 79.00

Flask 是一种具有平缓学习曲线和庞大社区支持的微框架,利用它可以构建大规模的web应用。学习上手Flask非常轻松,但要深入理解却并不容易。 本书从一个简单的Flask应用开始,通过解决若干实战中的问题,对一系列进阶的话题进行了探讨。书中使用MVC(模型-视图-控制器)架构对示例应用进行了转化重构,以演示如何正确地组织应用代码结构。有了可扩展性强的应用结构之后,接下来的章节使用Flask......一起来看看 《深入理解 Flask》 这本书的介绍吧!

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

在线压缩/解压 CSS 代码

html转js在线工具
html转js在线工具

html转js在线工具