다시 처음부터 JavaScript || 함수 (2) ⭐️

문규찬·2021년 11월 10일
0
post-thumbnail

✅ 출처 : 다시 처음부터 자바스크립트 게시글은 이웅모(님) '모던 자바스크립트 Deep Dive' 를 기록합니다

•참조에 의한 전달과 외부 상태의 변경 ⭐️

이 부분은 내용이 많지는 않았지만 개인적으로 조금 중요한 부분이라고 생각했습니다!
(어쩌면...중요한 부분 일수도..?⭐️)

매개변수도 함수 몸체 내부에서 변수와 동일하게 취급되므로 매개변수 또한 타입에 따라 값에 의한 전달, 참조에 의한 전달 방식을 그대로 따릅니다.

// 매개변수 primitive는 원시 값을 전달받고, 매개변수 obj는 객체를 전달받는다.

function changeVal(primitive, obj){
primitive += 100;
obj.name = 'kim';
}

//외부 상태
var num=100;
var person= {name: 'moon'}

console.log(num) // 100
console.log(person) // {name:'moon'}

changeVal(num, person) 

//원시값은 원본이 훼손되지 않습니다.
console.log(num) ----> 100

//객체는 원본이 훼손됩니다.
console.log(person) ----> {name:'kim'}

원시 타입 인수 (primitive)

원시타입 인수를 전달받은 매개변수 primitive는 변경 불가능한 값이므로 직접 변경할 수 없기 때문에 재할당을 통해 할당된 원시 값을 새로운 원시 값으로 교체했고
외부상태, 즉 함수 외부에서 몸체 내부로 전달한 원시 값의 원본을 변경하는 어떠한 부수 효과도 발생하지 않았습니다!!

객체 타입 인수 (obj)

객체 타입 인수는 참조값이 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼!!손!! 됩니다.
복잡한 코드에서 여러 변수가 참조에 의한 전달 방식을 통해 참조 값을 공유하고 있다면 의도치 않은 객체 변경이 일어날 수 있고 추적 또한 어려울 수 있습니다.

문제 해결 방법 중 하나는 객체를 불변 객체 (Immutable object) 로 만들어 사용하는 것입니다. 객체의 복사본을 새롭게 생성하는 비용은 들지만 객체를 마치 원시 값처럼!!!! 변경 불가능한 값으로... 원본 객체를 완전히 복제, 즉 깊은 복사 (deep copy) 를 통해 새로운 객체를 생성하고 재할당을 통해 교체합니다.

🟣 외부 상태를 변경하지 않고 외부 상태에 의존하지도 않는 함수를 순수 함수라 합니다. 순수 함수를 통해 부수 효과를 최대한 억제하여 오류를 피하고 안정성을 높이려는 프로그래밍 패러다임을 함수형 프로그래밍이라 합니다.

• 다양한 함수의 형태

1. 즉시 실행 함수

함수 정의와 동시에 즉시 호출되는 함수를 즉시 실행 함수라고 합니다.
즉시 실행 함수는 단 한번만!!! 호출되며 다시 호출할 수 없음을 기억해보자
즉시 실행 함수는 반드시 그룹 연산자 (...) 로 감싸야 합니다.

var res = (function() {
var a= 3;
var b= 5;
return a*b;
}());

console.log(res) // 15

//즉시 실행 함수에도 인수 전달 가능!
res = (20 + function(a,b){
return a*b
}(3,5))

console.log(res) /// 35

2. 재귀함수

함수가 자기 자신을 호출하는 것을 재귀 호출이라 합니다. 재귀 함수는 자기 자신을 호출하는 행위, 즉 재귀 호출을 수행하는 함수입니다.

function countdown(n){
if(n>0) return;
console.log(n)
countdown(n-1) // 재귀호출
}

countdown(5) // 5 4 3 2 1 

사실 반복문으로 쉽게 구현이 가능한데.....어떠한 상황에 재귀함수를 사용해야 하는지 이유를 딱히 모르겠다...

2. 중첩함수

함수 내부에 정의된 함수를 '중첩함수', '내부함수'라 합니다. 그리고 중첩함수를 포함한 함수는
'외부함수'라 부릅니다. 중첩함수는 외부 함수 내부에서만 호출할 수 있습니다. 일반적으로 중첩함수는 자신을 포함하는 외부함수를 돕는 헬퍼 함수의 역할을 합니다

function outer(){
var x=1;

//중첩함수
	function inner(){
    	var y=2;
        //외부함수의 변수를 참조할 수 있습니다.
        console.log(x+y) //3
        }
 inner();
 }
 
 outer();

3. 콜백함수 ⭐️

어떤 일을 반복 수행하는 repeat 함수를 정의해보자

function repeat(n){
	for(var i=0; i<n; i++) console.log(i)
    }
repeat(5); // 0 1 2 3 4

-----------------------------------------------

function repeat2(n) {
	for(var i=0; i<n; i++){
    		if(i%2) console.log(i)
    }
}

repeat2(5) // 1 3

위의 예제는 함수들을 반복하는 일은 공통적으로 수행하지만 반복하면서 하는 일의 내용은 다릅니다.
즉 함수의 일부분만이 다르기 때문에 매번 함수를 새롭게 정의해야 합니다. 이 문제는 함수를 합성! 하는 것으로 해결할 수 있습니다.

function repeat(n,f){
	for(var i=0; i<n; i++){
    f(i); // i를 전달하면서 f를 호출!
    }
}

-------------------------------

var logAll = function(i) {
console.log(i);
}

repeat(5,logAll); // 0 1 2 3 4 

-------------------------------

var logOdds = function(i) {
	if(i%2) console.log(i);
    }

repeat(5,logOdds) // 1 3

repeat함수는 더 이상 내부로직에 강력히 의존하지 않고 외부에서 로직의 일부분을 함수로 전달받아 수행하므로 더욱 유연한 구조를 가지게 되었습니다.

이처럼 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜!백!함수 라고 매개 변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수라고 합니다.

콜백함수도 고차 함수에 전달되어 헬퍼 함수의 역할을 합니다. 중첩함수는 고정되어 있기 때문에 교체하기 곤란하지만 콜백 함수는 외부에서 고차 함수 내부로 주입하기 때문에 자유롭게 교체가 가능하다는 장점!!

*콜백함수는 비동기 처리 (ex: setTimeout() ) 뿐 아니라 배열 고차 함수 (ex : map(), flter(), reduce()) 에서도 사용됩니다.

4. 순수함수와 비순수함수

함수형 프로그래밍에서는 어떤 외부 상태에 의존하지도 않고 변경하지도 않는, 즉 부수 효과가 없는 함수를 '순수 함수' 라 합니다. '비순수 함수' 는 반대! ㅎ.ㅎ

var count=0;

function increase(n){
return n++;
}

//순수함수가 반환한 결과값을 변수에 재할당해서 상태를 변경
count = increase(count);
console.log(count) // 1

----------------------------------------

function increase2(){
return ++count
}

// 비순수함수는 외부상태(count)를 변경하므로 상태 변화를 추적하기 어려워진다.
increase();
console.log(count); // 1

0개의 댓글