花了鐵人賽 1/3 篇數都在講 pure function,這一篇就帶到實務來玩一下選擇題之哪些是 pure/impure function 吧
var xs = [1,2,3,4,5]
/* 1) -------*/
xs.slice(0,3) //=> [1,2,3]
xs.slice(0,3) //=> [1,2,3]
xs.slice(0,3) //=> [1,2,3]
/* 2)-------- */
xs.splice(0,3) //=> [1,2,3]
xs.splice(0,3) //=> [4,5]
xs.splice(0,3) //=> []
1 ) ✅ Pure
2 ) ❌ Impure ,xs 會改變 (mutable) 而導致 side Effect
/* 1) -------*/
const toSlug = (title) => {
const urlFriendly = title.replace(/\W+/ig, '-')
if(urlFriendly.length < 1) {
throw new Error('is bad')
}
return urlFriendly
}
/* 2)-------- */
const toSlug = (title) => {
return new Promise((res, rej) => {
const urlFriendly = title.replace(/\W+/ig, '-')
if(urlFriendly.length < 1) {
rej(new Error('is bad'))
}
return res(urlFriendly)
})
}
1 ) ❌ Impure , throw
new Error 會有 Side Effect
2 ) ✅ Pure: return error as a value
/* 1) -------*/
const signUp = (attrs, fn) => {
return () => {
let user = saveUser(attrs)
fn(user)
}
}
/* 2) -------*/
const signUp = (attrs) => {
let user = saveUser(attrs)
welcomeUser(user)
}
1 ) ✅ Pure
2 ) ❌ Impure ,沒有回傳 return
/* 1) -------*/
const birthday = user => {
user.age += 1;
return user;
}
/* 2) -------*/
const shout = word =>
word.toUpperCase().concat("!")
/* 3) -------*/
const headerText = header_selector =>
querySelector(header_selector).text()
/* 4) -------*/
const parseQuery = () =>
location.search.substring(1).split('&').map(x => x.split('='))
/* 5) -------*/
var parseQueryString = function(queryString) {
var params = {}, queries, temp, i, l;
queries = queryString.split("&");
for ( i = 0, l = queries.length; i < l; i++ ) {
temp = queries[i].split('=');
params[temp[0]] = temp[1];
}
return params;
};
1 ) ❌ Impure: 改了 user.age
2 ) ✅ Pure
3 ) ❌ Impure : 因為他有讀取到外部,若有天外部資料不同就會有不同 output
4 ) ❌ Impure : 有用到 location.search
5 ) ✅ Pure: 雖然裡面做了很多 mutation 的事,但他還是秉持 same input, same output 原則
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。
這個範例:
const signUp = (attrs) => {
return () => {
let user = saveUser(attrs)
welcomeUser(user)
}
}
並不因為它有傳回一個函式,就令 signUp
是純函式,從 saveUser
、welComeUser
來看,應該是有與外界溝通,就是有副作用。
這個範例:
var parseQueryString = function(queryString) {
var params = {}, queries, temp, i, l;
queries = queryString.split("&");
for ( i = 0, l = queries.length; i < l; i++ ) {
temp = queries[i].split('=');
params[temp[0]] = temp[1];
}
return params;
};
並不能說是很好的示範,因為單純只照相同輸入就有相同輸出來說,就算是 Imperative 的語言,也有很多這種例子。
確實地,在非 FP 的語言中,有很多地方會做些權衡,而有類似的作法,然而問題在於,這種作法分寸很難拿捏,因為函式內部實作,很有可能增長到數十甚至數百行以上,充滿邏輯泥塊。
在非 FP 的語言中,有很多地方會做些權衡,然而是不得已而為之,如果真的要這麼做,最好是為每個變數加上 const
,至少這會強加不少限制,某些程度上可以讓函式內部實作不至於過於冗長、也可以減少邏輯泥塊的尺寸。
(為每個變數加上 const
,就不會有使用迴圈的機會了,如果你在程式碼中看到迴圈,基本上就是不純的了…這時候遞迴是你的好朋友…XD)
謝謝你的建議,我修改一下第一個範例,然後第二個可能我要寫一些備註會清楚一些