목적
- 함수편은 코드를 재사용 할 수 있는 요소인 함수에 중점을 둔 기본 팁을 모았습니다.
단락 또는 조건부 변수를 사용하는 대신 기본 인수를 사용합니다.
- 대부분의 프로그래밍 언어에서는 함수에 기본 인수를 정의할 수 있습니다.
- 이를 통해 코드 본문에서 조건부 또는 단락에 대한 로직을 처리할 필요가 없습니다.
function setName(name) {
const newName = name || ‘Juan Palomo’;
}
// function내 기본 인수 사용
function setName(name = ‘Juan Palomo’) {
// …
}
두개 또는 그 이상의 함수를 인수로 사용하는 경우
- 이 개념은 코드 품질을 향상시키는데 필수적인 항목입니다.
- 함수인수에 대한 수를 줄여야 합니다.
- 적절한 숫자는 2개 이하 이지만 특정 프로그래밍 언어에 따라 달라질 수 있으므로 숫자에 집착하지 않으셔도 됩니다.
- 보통 Function은 하나의 객체를 구성하여 그룹화 되기 때문에, 추상화 수준이 높은 객체를 사용하는 것이 좋습니다.
function newBurger(name, price, ingredients, vegan) {
// …
}
function newBurger(burger) {
// …
}
function newBurger({ name, price, ingredients, vegan }) {
// …
}
const burger = {
name: ‘Chicken’,
price: 1.25,
ingredients: [‘chicken’],
vegan: false,
};
newBurger(burger);
-
첫번째 function에서는 햄버거의 독창적인 기능을 위해서 네가지 매개 변수를 받습니다.
- 이때의 매개변수는 고정되어 있으며, 순서에 따라 많은 제한이 따릅니다.
- 이로써 Function내에 엄격한 기능으로 들어갑니다.
-
같은 내용을 사용하여 두번째 function에서 새로운 햄버거를 만드는 것이 상당히 개선되었습니다.
- 이와 같은 방식으로 속성을 단일 객체로 그룹화 할 수 잇습니다.
-
세번째 Function에서 우리는 전송된 객체에 대해 구조화를 사용 가능하며, 함수 본문의 속성에 엑세스가 가능해집니다.
- 단일 매개변수를 사용하기 때문에 유연성이 향상될 수 있습니다.
사이드 이펙트 방지하기 - 전역 변수 사용
- 사이드이펙트는 미래에 문제를 야기할 수 있는 원인이 됩니다.
let fruits = ‘Banana Apple’;
function splitFruits() {
fruits = fruits.split(‘ ‘);
}
splitFruits();
console.log(fruits); // [‘Banana’, ‘Apple’];
function splitFruits(fruits) {
return fruits.split(‘ ‘);
}
const fruits = ‘Banana Apple’;
const newFruits = splitFruits(fruits);
console.log(fruits); // ‘Banana Apple’;
console.log(newFruits); // [‘Banana’, ‘Apple’];
- 함수가 범위를 벗어난 변수 또는 객체를 수정하는 전형적인 사이드 이펙트 관련 예제입니다.
- 이 함수는 테스트할 인수가 없으므로 테스트가 불가능합니다.
- 실제로, 수정하는 변수의 상태는 함수에 의해 제어되거나 관리가 되지 않는 형태가 됩니다.
- 함수의 범위 내에 있는 변수 (시간이 지남에 따라 기억해야 하는 경우에는 분명하지 않은 변수)를 인수로 전달시키는 것입니다.
사이드 이펙트 방지하기 - Object 변이 (Mutable)
- 코드의 다른 부분을 동일한 객체를 사용하여 데이터를 수정할 때 더 많은 사이드 이펙트가 발생할 수 있습니다.
- Javascript에서 배열 데이터 구조와 함께 작동하는 메소드는 오브젝트를 변경하는 메소드와 그렇지 않은 메소드로 구분됩니다.
const addItemToCart = (cart, item) => {
cart.push({ item, date: Date.now() });
};
const addItemToCart = (cart, item) => {
return […cart, {
item,
date: Date.now(),
}];
};
함수는 한가지만 수행할 수 있어야 합니다.
- 프로그래밍의 기본적인 원칙 중 하나입니다.
- 각 기능은 하나의 개념적인 작업만 수행할 수 있어야 합니다.
- 일련의 작은 작업이 더 큰 작업을 수행하나 작업이 혼합되는 경우를 커플링이라고 합니다.
- 함수는 한가지 기능만 수행할 수 있어야 합니다.
function emailCustomers(customers) {
customers.forEach((customer) => {
const customerRecord = database.find(customer);
if (customerRecord.isActive()) {
email(client);
}
});
}
- 위의 예제는 클라이언트 목록을 수신해서 고객에게 이메일을 보내는 기능을 모델링 했습니다.
- 개념상 이는 단순한 비즈니스 규칙이지만, 이는 구현하는 과정에서 명확하게 차별화된 두 가지 작업을 지니고 있습니다.
- 우리는 활성 상태인 사용자를 필터링 해야하며, 이전 사용자와 독립적인 기능으로 작동됩니다.
- if 의 남용은 지양했으면 합니다. 활성화된 클라이언트를 필터링 한 후 각 클라이언트에게 메일을 전송하는 다른 기능이 필요합니다.
function emailActiveCustomers(customers) {
customers
.filter(isActiveCustomer)
.forEach(email);
}
function isActiveCustomer(customer) {
const customerRecord = database.find(customer);
return customerRecord.isActive();
}
함수는 추상화의 한 수준 이어야 합니다.
- 함수를 디자인 할 때 충족해야 할 또 다른 요구 사항은 각 함수에 단일 추상화 레벨만 있어야 한다는 것입니다.
function parseBetterJSAlternative(code) {
const REGEXES = [
// …
];
const statements = code.split(‘ ‘);
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
// …
});
});
const ast = [];
tokens.forEach((token) => {
// lex…
});
ast.forEach((node) => {
// parse…
});
}
- 상기 예제를 해결하는 내용은 간단합니다.
- 다양한 수준의 추상화를 식별하고, 전체에 설명된 요구사항을 충족하는 함수를 작성하면 됩니다.
- 리팩토링을 적용한 후의 기능은 다음과 같습니다.
const REGEXES = [ // …];
function tokenize(code) {
const statements = code.split(‘ ‘);
const tokens = [];
REGEXES.forEach((REGEX) => {
statements.forEach((statement) => {
tokens.push( /* … */ );
});
});
return tokens;
}
function lexer(tokens) {
const ast = [];
tokens.forEach((token) => ast.push( /* */ ));
return ast;
}
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const ast = lexer(tokens);
ast.forEach((node) => // parse…);
}
명령형 프로그래밍 보다 기능적 프로그래밍을 선호합니다.
- 패러다임의 경우에는 다양할 수 있으므로, 기능적 패러다임에 대한 간략한 설명만 짚고 넘어가겠습니다.
- Alvin Alexander의 블로그를 읽어보는 것이 좋습니다.
-
함수형 프로그램을 사용하는 주요 이점을 요약했습니다.
- 순수한 기능은 추론하기 더 쉽습니다.
- 테스트가 더 쉬우며 속성 기반 테스트와 같은 기술에 적합합니다.
- 디버깅이 더 쉽습니다.
- 프로그램이 더 높은 수준으로 작성되므로 이해하기 쉽습니다.
- 병렬 / 동시 프로그램이 더 쉽습니다.
- 함수형 프로그래밍과 명령형 프로그래밍의 또 다른 특징은 코드가 더 가독성이 좋다는 것입니다.
-
간단한 카운터 함수를 보겠습니다.
- 몇 가지 변수를 명심하세요:
total
,items
,i
,length
,price
- 기능 구현에서는 총계, 가격 및 품목만 있습니다.
- 몇 가지 변수를 명심하세요:
const items = [{
name: ‘Coffe’,
price: 500
}, {
name: ‘Ham’,
price: 1500
}, {
name: ‘Bread’,
price: 150
}, {
name: ‘Donuts’,
price: 1000
}
];
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].price;
}
// Good practice
const total = items
.map(({ price }) => price)
.reduce((total, price) => total + price);
메소드 체인을 사용합니다.
- 객체 또는 데이터 흐름에서 작동하는 기능을 설계할 때 일반적으로 단일 작업을 수행하는 기능입니다.
- 이러한 function들은 단일 추상화 수준을 가지며 부작용이 발생하지 않습니다.
- 복잡한 작업을 수행하기 위해서 여러 작업을 조합하게 됩니다.
- 더 많은 코드를 읽을 수 있는 체인 방식이 개발됩니다.
class Car {
constructor({ make, model, color } = car) {
/* */
}
setMake(make) {
this.make = make;
}
setModel(model) {
this.model = model;
}
setColor(color) {
this.color = color;
}
save() {
console.log(this.make, this.model, this.color);
}
}
const car = new Car(‘WV’,’Jetta’,’gray’);
car.setColor(‘red’);
car.save();
// method chain 사용
class Car {
constructor({ make, model, color } = car){}
setMake(make) {
this.make = make;
return this;
}
setModel(model) {
this.model = model;
return this;
}
setColor(color) {
this.color = color;
return this;
}
save() {
console.log(this.make, this.model, this.color);
return this;
}
}
const car = new Car(‘WV’,’Jetta’,’gray’)
.setColor(‘red’)
.save();