ch10 에서는 일급 값, 일급 함수, 고차 함수에 대해 배울 수 있다.
Ch10
요약일급 값은 변수에 저장할 수 있고 인자로 전달하거나 함수의 리턴값으로 사용할 수 있다.
일급이 아닌 기능은 함수로 감싸 일급으로 만들 수 있다.
고차함수는 다른 함수에 인자로 넘기거나 리턴값으로 받을 수 있는 함수다. 고차 함수를 통해 다양한 동작을 추상화할 수 있다.
함수 이름의 암묵적 인자는 암묵적 인자를 드러내기 리팩토링을 통해 제거할 수 있다.
암묵적 인자 드러내기는 함수 이름 대신 일급 값인 인자로 바꾸는 리팩토링을 말한다.
동작을 추상화하기 위해 본문을 콜백으로 바꾸기 리팩토링을 사용할 수 있다.
함수 이름에 있는 암묵적 인자 냄새의 특징
1. 구현이 비슷하다.
2. 함수 이름에 다른 부분이 함수에서 사용된다.
냄새가 나는 코드
function setPriceByName(cart, name, price) {
var item = cart[name];
var newItem = objectSet(item, 'price', price);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
function setShippingByName(cart, name, ship) {
var item = cart[name];
var newItem = objectSet(item, 'shipping', ship);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
function setQuantityByName(cart, name, quant) {
var item = cart[name];
var newItem = objectSet(item, 'quantity', quant);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
function setTaxByName(cart, name, tax) {
var item = cart[name];
var newItem = objectSet(item, 'tax', tax);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
function objectSet(object, key, value) {
var copy = Object.assign({}, object);
copy[key] = value;
return copy;
}
리팩토링 이후
/// Before
function setPriceByName(cart, name, price) {
var item = cart[name];
var newItem = objectSet(item, 'price', price);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
cart = setPriceByName(cart, "shoe", 13);
cart = setQuantityByName(cart, "shoe", 3);
cart = setShippingByName(cart, "shoe", 0);
cart = setTaxByName(cart, "shoe", 2.34);
/// After
function setFieldByName(cart, name, field, value) {
var item = cart[name];
var newItem = objectSet(item, field, value);
var newCart = objectSet(cart, name, newItem);
return newCart;
}
cart = setFieldByName(cart, "shoe", 'price', 13);
cart = setFieldByName(cart, "shoe", 'quantity', 3);
cart = setFieldByName(cart, "shoe", 'shipping', 0);
cart = setFieldByName(cart, "shoe", 'tax', 2.34);
본문을 try/catch 로 감싸지 않고 함수로 감싼 이유는 코드를 바로 실행해서는 안되기 때문이며 감싼 함수를 호출하기 전까지 실행되지 않는다.
이 리팩토링은 중복을 제거할 수 있다.
리팩토링 이전의 코드
// Page 249
/// Preparing and eating
for(var i = 0; i < foods.length; i++) {
var food = foods[i];
cook(food);
eat(food);
}
/// Washing up
for(var i = 0; i < dishes.length; i++) {
var dish = dishes[i];
wash(dish);
dry(dish);
putAway(dish);
}
/// Give them names
function cookAndEatFoods() {
for(var i = 0; i < foods.length; i++) {
var food = foods[i];
cook(food);
eat(food);
}
}
cookAndEatFoods();
function cleanDishes() {
for(var i = 0; i < dishes.length; i++) {
var dish = dishes[i];
wash(dish);
dry(dish);
putAway(dish);
}
}
cleanDishes();
// Page 250
/// call both "item"
function cookAndEatFoods() {
for(var i = 0; i < foods.length; i++) {
var item = foods[i];
cook(item);
eat(item);
}
}
cookAndEatFoods();
function cleanDishes() {
for(var i = 0; i < dishes.length; i++) {
var item = dishes[i];
wash(item);
dry(item);
putAway(item);
}
}
cleanDishes();
/// change name to generic
function cookAndEatArray(array) {
for(var i = 0; i < array.length; i++) {
var item = array[i];
cook(item);
eat(item);
}
}
cookAndEatFoods(foods);
function cleanArray(array) {
for(var i = 0; i < array.length; i++) {
var item = array[i];
wash(item);
dry(item);
putAway(item);
}
}
cleanDishes(dishes);
// Page 251
/// extract out function
function cookAndEatArray(array) {
for(var i = 0; i < array.length; i++) {
var item = array[i];
cookAndEat(item);
}
}
function cookAndEat(food) {
cook(food);
eat(food);
}
cookAndEatFoods(foods);
function cleanArray(array) {
for(var i = 0; i < array.length; i++) {
var item = array[i];
clean(item);
}
}
function clean(dish) {
wash(dish);
dry(dish);
putAway(dish);
}
cleanDishes(dishes);
// Page 252
/// rename to something generic
function operateOnArray(array, f) {
for(var i = 0; i < array.length; i++) {
var item = array[i];
f(item);
}
}
function cookAndEat(food) {
cook(food);
eat(food);
}
cookAndEatFoods(foods, cookAndEat);
function operateOnArray(array, f) {
for(var i = 0; i < array.length; i++) {
var item = array[i];
f(item);
}
}
function clean(dish) {
wash(dish);
dry(dish);
putAway(dish);
}
cleanDishes(dishes, clean);
// Page 253
/// rename operateOnArray() to forEach()
function forEach(array, f) {
for(var i = 0; i < array.length; i++) {
var item = array[i];
f(item);
}
}
function cookAndEat(food) {
cook(food);
eat(food);
}
forEach(foods, cookAndEat);
function clean(dish) {
wash(dish);
dry(dish);
putAway(dish);
}
forEach(dishes, clean);
// Page 254
/// Using anonymous functions and forEach()
forEach(foods, function(food) {
cook(food);
eat(food);
});
forEach(dishes, function(dish) {
wash(dish);
dry(dish);
putAway(dish);
});
본문을 콜백으로 바꾸기 리팩토링을 적용한 예시 코드
// Page 255
try {
saveUserData(user);
} catch (error) {
logToSnapErrors(error);
}
// Page 256
try {
fetchProduct(productId);
} catch (error) {
logToSnapErrors(error);
}
// Page 257
/// After function extraction
function withLogging() {
try {
saveUserData(user);
} catch (error) {
logToSnapErrors(error);
}
}
withLogging();
/// After extracting callback
function withLogging(f) {
try {
f();
} catch (error) {
logToSnapErrors(error);
}
}
withLogging(function() {
saveUserData(user);
});
함수를 전역으로 정의하고 이름을 붙여 나중에 붙인 이름으로 프로그램 어디서나 쓸 수 있다.
function saveCurrentUserData() {
saveUserData(user);
}
withLogging(saveCurrentUserData);
함수를 지역 범위 내에서 정의하고 이름을 붙여 사용한다. 범위 바깥에서는 쓸 수 없다. 지역적으로 쓰고 싶고 이름이 필요할 때 유용하다.
function someFunction() {
var saveCurrentUserData = function() {
saveUserData(user);
}
withLogging(saveCurrentUserData);
}
함수를 사용하는 곳에서 바로 정의하니 이름이 없어 익명함수 anonymous function 이라고 부른다. 한 번만 쓰는 짧은 함수에 쓰면 좋다.
withLogging(function() { saveUserData(user) ;});
일급
first-class
은 인자로 전달할 수 있다는 말입니다. 그리고 고차higher-order
라는 말은 함수가 다른 함수를 인자로 받을 수 있다는 말입니다. 일급 함수가 없다면 고차 함수를 만들 수 없습니다. - 252p
고차 함수의 좋은 점은 코드를 추상화할 수 있다는 점이다. - 258p
함수를 정의할 때 변수에 저장해 이름을 붙이거나 배열이나 객체 같은 자료구조에 함수를 보관할 수도 있다. 또는 그냥 그대로 전달할 수도 있다. 이는 일급이기 때문에 할 수 있는 일이다. - 263p
함수 호출의 경우 선택적으로 호출하거나 나중에 호출하거나 새로운 문맥 안에서 호출될 수 있다. - 263p
이미지 출처 : https://product.kyobobook.co.kr/detail/S000001952246
⭐️ 왜 함수에 일반 데이터값으로 전달하지 않고 함수를 전달하나요? - 264p
함수로 감싸서 전달하지 않고 데이터로서 본문을 일급으로 전달하면 코드가 특정 문맥에서 실행되도록 할 수 있기 때문에 try/catch 라는 문맥에서 실행되도록 할 수 있다.
코드의 냄새 - 더 큰 문제를 가져올 수 있는 코드
일급 값 first-class value - 언어에 있는 다른 값처럼 쓸 수 있다.
고차 함수 highter-order funciton - 인자로 함수를 받거나 리턴 값으로 함수를 리턴할 수 있는 함수
데이터 지향 data orientation - 이벤트와 엔티티에 대한 사실을 표현하기 위해 일반 데이터 구조를 사용하는 프로그래밍 형식
정적 타입 statically typed - 컴파일할 때 타입을 검사하는 언어
동적 타입 dynamically typed - 런타임에 타입을 확인하는 언어
콜백 callback - 인자로 전달하는 함수, 핸들러 함수라고도 부른다.
인라인 함수 inline function - 쓰는 곳에서 바로 정의하는 함수를 말한다. 예를 들어 인자 목록에서 바로 정의하는 함수가 인라인 함수다.
익명함수 anonymous function - 이름이 없는 함수