본문 바로가기
JavaScript/스타일가이드 정리하기

스타일 가이드 복습하기 03 (Functions, ArrowFunctions)

by zzinLee 2023. 8. 22.

# Function 개념

1. 독립적으로 분리된 로직. 프로그램 수준에서 이미 정의된, 혹은 사용자가 정의한 실행 단위.

 

2. 일급객체(First Class Object)

  • 객체를 변수나 데이터구조 안에 담을 수 있다
  • 인자 전달 및 반환값으로 전달 가능하다
  • 런타임에 생성될 수 있다

3. 함수선언(Function Declaration) VS 함수 표현(Function Expression)

 

  • 함수 선언 Function Declaration ( ≒ Function Statement
    • 실행 가능한 코드블럭이 아닌 함수의 정의
    • statement 코드 해석에 따른 수행결과가 없음
    • function declarations always need names
    • 이렇게 선언된 함수는 언제나 호이스팅 가능함
  • 함수 표현 Function Expression ( ≒ Function Literal )
    • 실행 가능한 코드블럭 - 변수, 데이터 구조 등에 할당되고 있음을 의미함 (즉시 실행 가능)
    • 해석과 동시에 실행
    • anonymous function expression
    • named function expression ★ 해당 함수의 이름은 함수밖에서 사용할 수 없다
    • self invoking function expression

 

★ 해당 함수의 이름은 함수밖에서 사용할 수 없다 named function expression

 

const foo = function bar() {
	bar();
};

bar(); //ReferenceError: bar is not defined

 

함수 표현식 3가지

//anonymous function expression
const foo = function() { console.log('foo'); }

//named function expression
const foo = function foo() {
	console.log('foo');
}

//self invoking function expression(IIFE)
(function foo(){ console.log('foo');})();

 

# Hoisting 호이스팅

인터프리터가 JavaScript 코드를 해석하는데에 있어서

Global 영역에 선언된 코드 블럭최상의 스코프scope 로 끌어올리는 것

 

Global 영역에 선언된 변수 또는 함수는

자바스크립트 엔진이 가장 우선적으로 해석

(선언문은 항시 최우선 해결, 실행 코드는 뒤로 밀린다)

 

단, 할당구문은 런타임 중 이루어지므로 호이스팅 되지 않음.

※ 코드 가독성이 떨어지므로 호이스팅을 이용한 코드작성은 비추천

 

function statement(함수선언문)은 호이스팅 되고, 할당은 호이스팅 되지 않음.

//함수 선언문. 호이스팅 됨.
function foo() {//...Global 객체에 등록됨
}

//함수리터럴, 할당구문. 호이스팅 되지 않음.
const foo = function() {//...
}

 

Quiz about Hoisting

호이스팅과 관련한 퀴즈는 아래 제시된 출처 블로그에서 가져왔다.

좋은 내용이라 여기에 한번 내 답안을 써보려고 한다.

정확한 내용이 궁금하다면 아래의 출처를 통해 원문을 볼 것!

//Question 1:
function foo(){
    function bar() {
        console.log('hello');
    }

    return bar();

    function bar() {
        console.log('world');
    }
}
foo();

//Question 2:
function foo(){
    var bar= function() {
        console.log('hello');
    };
    return bar();
    var bar = function() {
         console.log('world');
    };
}
foo();

 

Question 1 :

global 영역으로 function foo() { ...foo... } 가 함수선언문이므로 함수를 호이스팅한다.

블럭 내부{ ...foo...}도 함께 호이스팅 된다.

...foo... :  function bar(){} 은 함수선언문으로, Global 영역에 호이스팅 된다.

//Question 1:
function foo(){
    function bar() {
        console.log('hello');
    }
    function bar() {
        console.log('world');
    }
    return bar(); 
}
foo();

호이스팅 되므로 bar는 함수가 덮어 씌워지며

bar() 호출 시 console.log('world') 코드블럭을 실행하게 된다.

따라서 콘솔창에는 'world' 만 찍힌다.

실행문 return bar(); 은 런타임 중 가장 마지막에 실행된다.

 

Question 2 :

global 영역으로 function foo() { ...foo... } 가 함수선언문이므로 함수를 호이스팅한다.

블럭 내부{ ...foo...}도 함께 호이스팅 된다.

...foo... : var bar 에는 함수 리터럴을 할당하므로, 할당은 실행문과 함께 런타임 시점에서 이루어진다.

런타임 도중 return을 만나면 반환하므로, 콘솔창에는 'hello' 가 찍힌다. 

function foo(){
	var bar = undefined;
	var bar = undefined;
    
	bar = function() { console.log('hello'); }
	return bar; 
    
	//bar = function() { console.log('world'); } 실행되지 않음
}

foo();

 

Function 의 개념에 대한 대부분의 내용은 아래 출처에서 공부하여 정리했다.

출처: http://insanehong.kr/post/javascript-function/


07 Functions

1. 함수식보다는 함수선언을 지향

 

  • 이름이 부여된 함수는 콜스택에서 쉽게 확인
  • 호이스팅이 가능
  • arrow function에서 손쉽게 사용가능

 

//bad
const func = function() {...};

//good
function func() {...}

 

2. 함수 이외의 블록(if, while, ... )에서 함수를 선언하지 않는 편이 좋다.

 

//bad
if(currentUser){
	function test() { console.log('Nope.'); }
}

//good
let test;
if(currentuser){
	test = () => { console.log('Yes!'); };
}

 

나쁜 예 ) if 의 블록내에 새로운 함수를 선언한다.

브라우저에 따라 이 함수를 해석하는 방식이 다르기 때문에 유의해야한다.

좋은 예 ) 재할당 예정인 변수 test  를 선언하여, if문 안에 함수식을 할당한다.

 

 

3. 파라미터에 절대 arguments 를 지정하지 말 것

함수 스코프에는 자동적으로 arguments 객체가 주어진다.

이 arguments 객체는 유사배열객체로 스프레드 문법이 나오기 전,

함수의 인수 전체를 얻어오는 유일한 방법이었다.

 

인수전체를 얻어오기에 필요한 인자의 일부만 받아오는 것이 불가능하였고,

배열형태가 아니므로 불편했다.

...args 와 같이 나머지매개변수(rest syntax, spread) 를 이용하는 것을 권장
→ 몇 개의 인자를 원하는지 explicit하게 얻어내고 Array형태로 받아낸다.

 

function recommended(name, options, ...args){...}
//args는 주어진 인자에서 앞의 두 인자를 제외한 나머지 인수들의 배열
//typeof(args) 는 object([[Prototype]]): Array(0)

 

함수에 전달된 인자를 까다로운 방법으로 array형태로 변경했던 기존의 방식과 비교해보자.

 

//old
function factorsArray(){
	const args = Array.prototype.slice.call(arguments);
	return args.join('');
}

//new
function factorsArray(...args){
	return args.join('');
}

 

 

4. 함수에 전달되는 파라미터를 변경하는 것보다 default 파라미터를 적극 이용하는 것이 좋다. 

 

//bad
function handleThings(opt){
	opt = opt || {} ;
}

//bad
function handleThings(opt){
	if(opt === void 0) {opt = {};}
}


//good
function handleThings(opt = {}){
	....
}

 

5. 부작용이 있을 법한 default 값 설정은 피하기  (ex) 전위 증감, 후위증감 등

 

//never do this
let i=1;
function count(k=i++){
	console.log(k);
}

 

6. default 값 사용 시, 파라미터 중 가장 마지막에 둔다. 

 

function person(name, age, opt = {} ){
	console.log(`${name} ${age}`);
}

 

7. Function Constructor 사용을 금지한다. eval()과 같은 보안 취약점이 있다.

문자열을 평가시켜 새 함수를 작성하는 것은 매우 위험한 일이다.

 

const add = new Function('a','b', 'return a+b;');

 


08 Arrow Functions

1. anonymous function expression 인 경우, arrow function 을 사용하는 것이 낫다.

 

//bad
const foo = function(){...}

//good
const foo = () => {...}

 

2. 함수 body 부분이 하나의 식으로 구성된 경우 중괄호 {} 생략 가능  암시적 return 을 사용한다.
    그 외에는 반드시 중괄호를 사용하고, return 명시해준다.

 

[1,2,3].map( number => number+1 );

[1,2,3].map( number => { 
const addNumber = number + 1;
return addNumber;
});

 

3. 식은 하나이지만 복수행에 걸쳐있는 경우, 가독성을 위하여 소괄호로 감싸는 것이 좋다.

  코드의 시작과 끝을 알기 쉽도록 표기

 

[1,2,3].map( number => (
`this is askdjflkasjdkfljalksdjfklajsdfkljalsjfdksjadklja` +
`Finally, number is ${number}`
));

 

4. 함수의 인수가 하나인 경우, 소괄호를 생략하는 게 가능하다.

 

//bad
[1,2,3,4].map((x)=> x*(x+1));

//good
[1,2,3,4].map(x => x*(x+1));

//good
[{x: 1, y: 2},{ x: 3, y: 3},{x: 2, y:1}].map(({x,y})=> x*y);