함수는
-변수에 저장할 수 있어야 한다.
-함수의 파라미터로 전달할 수 있어야한다. (함수의 파라미터로 함수를 받을수도있다.)
-함수의 반환값으로 사용할수 있어야한다. (리턴 , 함수를 리턴값으로 줄 수 있다)
-자료 구조에 저장할 수 있어야한다. (배열,객체...)
함수는 자바스트립트에서 객체라고도 부른다.
===============================
자바스크립트 함수는 크게 3가지 작성법이 있다.
1.함수 선언문
function 함수명 (){
};
2.함수 표현식(변수에 담는 방식)
var func = function(){
};
3.함수 생성자 (이거 자체로도 함수를 생성한 상태이다.)
var multi = new Function();
===================================
-함수 선언식
function foo(){
console.log('call foo');
}
foo();
-함수 표현식
var bar = function(){
console.log('call bar');
}
bar();
*함수 선언식과 ,함수 표현식 의 큰 차이점은 호출시점에 따라 함수가 실행이 되고, 안되고 차이가 있다.
그게 이제 함수 호이스팅과 관련이 있는데~
*함수 호이스팅>
저렇게 위의 코드 처럼
함수 실행을 밑에서 하면 정상적으로 실행이 다된다.
그러나 함수실행을 그함수 위에다 해버리면, 어떨까?
함수 호이스팅이 나타나겠지.
그리고
선언식에서 호이스팅이랑
표현식에서 호이스팅이랑 차이가 날것이다.
foo(); // 위쪽에서 호출~!
-함수 선언식
function foo(){
console.log('call foo');
}
bar(); // 위쪽에서 호출!~
-함수 표현식
var bar = function(){
console.log('call bar');
}
이렇게 다 위쪽에서 호출을 한결과~
함수선언식은 정상적으로 실행이된다.
그러나
함수 표현식에서는 정상적으로 실행이 안된다.
*여기서 호이스팅이 왜 일어나냐면?
자바스크립트에서는 변수의 생성과 초기화 작업이 분리가되서 진행이 되기때문이다.
그래서
보통 함수 표현식(변수로 저장된 함수)으로 함수를 사용하기를 권장하는데
그 이유는 , 선언식으로 사용하게되면 위쪽에서도 호출해도 호이스팅때문에 실행이 되기때문에 그런 잠재적 오류가 될 수 있기때문에,
함수에서
파라미터는 전달된 값을 받아들이는 변수이다.
아규먼트는 어떤 함수를 호출시 전달되는 값이다.
function callRandom(upper){ //여기 upper는 파라미터
var randomNumber = Math.floor(Math.random() * upper) + 1; //여기도 파라미터~
return randomNumber;
}
console.log(callRandom(10)); //여기 10 은 아규먼트
===========================================
<함수의 종류>
1.내장함수(중첩함수)
-함수안에 함수가 존재하는 형태
//여기서는 inner함수가 내장함수다~!
function outter (){
var test1 = 1;
var test2 = 2;
function inner(){
var test2 = 100;
console.log(test1);
console.log(test2);
}
inner();
}
outter();
출력결과<
1
100
여기서 내장함수inner(중첩함수)의 큰 특징은 2개있는데~
1.(클로져 = 내부함수가 외부함수안에있는 변수를 접근할수있는형태를 클로져라고 한다.)
즉, 내장함수는 외장함수 안 에있는 변수에 접근을 할 수가있다.!!
그러나
2.inner함수(내장함수) 호출을 외부에 할 경우,
에러가 난다. inner함수는 실행이 안된다.
왜냐면!
아니 외부함수 호출을 했으니 내부도 호출을 바깥에서 해도 되는거 아니냐고 물을 수 있는데, 아니다, 안된다!
무조건 내부함수(내장함수)는 외부함수 안에서만 놀 수 있다.
즉, 내장함수는 외장함수 안에서만 호출 할 수 있다.
즉, outter함수 바깥에서는 inner함수를 접근할수 없다.!!!!
2.콜백함수(고차함수 , Higher Order Function)
-파라미터안에 함수가 들어간다.
-고차함수는(콜백함수는) 순수함수를 통해 불변성을 지향하도록하고, 보조함수를 통해 조건문 반복문을 최대한 제거하여 , 애플리케이션의 복잡성을 낮추려는데 목적성이 있다.
-콜백함수의 필요성은 하나의 함수를 만들어넣고,
그 함수의 쓰임새는 특정 기능을 정의해(로직 담당) 처리만 하는 역할을 하는 함수라고
만들어두고,
또 다른 여러 함수를 만들어서, 이 여러함수는 값만 가지고있는 함수들로 구성되어져있고,
그러면,
이 값을 지닌 함수들을 사용하려면, 기능담당의 함수에다가 패스해서 넣으면 된다.
그럼 그 로직담당함수는 그 기능대로만 값들만 처리하고 결과를 출력하는 역할을 한다.
그래서 역할별로 정의해놓고 값, 기능으로 나누어서 하는 역할이 다르게 구성된 함수로 만들어놓으면 일처리가 간편해지는 장점이 있다.
(즉,모듈화 하는거다.)
왜냐면 , 이 함수는 맡겨진 이일만하면되고, 다른함수는 그일만하면되니까.
각자 역할담당별로 나누어 처리하면 효율적인거다.
콜백함수 사용법은 특정함수의 파라미터에 함수를 패스하거나 함수를 넣으면 되고,
그함수는 값일테고, 그럼 그걸 토대로 특정함수는 일을 처리하면되는거다.
var arr =['Apple' , 'Banana' , 'Tomato'];
console.log('forEach 문');
var result = "";
arr.forEach(function (element , index , arr){ // 파라미터 최대3개 // 이형태를 콜백함수
result += index + ' : ' + element + " = " '\n';
});
console.log(result);
// 보면 forEach문 의 파라미터로 함수가 들어가있다.
// 그래서 그 함수 안을 보면
// 첫 인덱스를 찾고, 그안에 어떤내용이 들어가있는지 알려준다.
// 그래서 첫번째 반복을 돌고 1차적으로 종료가 된다
//그다음
// 아직 남은 인덱스가 남아있다. (방금0 했으니까 1 , 2가 남아 있다.)
// 그래서 0번째 인덱스 값을 다 찾은 다음에 , 종료(스탑을 시키고)를 시키고,
// 다시 1번 인덱스로 움직이게 된다.
// 그니까 순서는 forEach 에서 파라미터안의 인덱스에 맞게,함수 안의 내용을 찍고, 다시 forEach 파라미터 안의 익명함수가
다시 실행되는거다.
// 그리고 또 forEach파라미터에 인덱스값을 넣어주고, 함수안의 내용이 찍히고,
이런식으로 배열의 인덱스가 다 끝날때까지 돌고 끝이나는데, 이 반복문이 계속 반복하면서, 그와 동시에 익명 함수가 계속 다시 호출되고 종료하고를 반복하는거다.
즉, 반복적으로 파라미터안의 익명함수를 호출을 하는거다.
그리고나서 최종적으로 forEach함수가 종료가된다.
즉, 뭔가 다시 함수를 재 호출하는 과정 이 자체를 보통 이런 콜백함수를 이런식으로 자주 사용하는 식이다.
그래서
이런 구조자체를 forEach 문으로 해서 콜백함수를 익혀두면 좋은 예일것같다.
즉, forEach함수안의 파라미터로 사용하는 익명함수, 그 익명함수가 콜백함수이다.
(외부함수에서 어떤함수를 파라미터안에서 함수를 호출을 하는...)
즉, 파라미터안의 함수가 어떤방식으로든 호출이되어 실행되는 형태가 콜백함수이다.
var button = document.getElementById('my-button')
button.onclick = function onButtonClick () {
console.log('버튼 눌림')
}
... // 잡다한 작업들
이 코드는 웹 페이지가 잡다한 작업들을 하고 있는 와중에도 버튼이 클릭되면 onButtonClick 함수를 다시 불러서 실행할 것이다. 이게 바로 이런 함수를 Callback 함수라고 부르는 이유다.
****콜백함수의 또다른 예제>>>>>>>>>>
let x = function() {
console.log('im called from inside a function');
}
let y = function (callback){
console.log('do something');
callback();
}
y(x);
이 콜백함수 코드는
y 함수 안의 파라미터로 함수를 불러와서 y함수 내부에서 x함수를 실행시키는 형태이다.
그래서 y를 호출시키면, x도 호출되는 형태이다.
***콜백함수의 또다른 예제>>>>>>>>>>
//이건 리팩토링 전 코드!!!(이건 콜백함수 아님!!!!!!!!!!!!!!!!!!!)
let calc = function(num1 , num2, calcType){
if(calcType === "add"){
return num1 + num2;
}else if(calcType === "multiply"){
return num1 * num2;
}
};
console.log(calc(2,3,'add'));
이 코드를 콜백으로 바꾸면!
let add = function(a,b){ //콜백함수
return a+b;
};
let multifly = function(a,b){ //콜백함수
return a*b;
};
let doWhatever = function(a,b){ //콜백함수
console.log(here are your two numbers back ${a} , ${b});
}
let calc = function(num1, num2, callback){
return callback(num1 , num2);
};
console.log(calc(2,3, add));
console.log(calc(2,3, doWhatever));
console.log(calc(2,3, function(a,b){
return a-b;
}));
// 이 마지막 거는 아규먼트에서 바로 익명함수를 선언해서 넘겨주는거다, 그러면 calc함수를 호출하면 2 랑 3그리고 이 익명함수를 가지고 전달해주면 리턴값으로 가면 이 익명함수가 또 실행이 되고, 리턴값이 또 실행되서 a -b가 되면서 결과값이 출력된다.
그래서 즉, 내부에서 선언해서 써도 된다는 이야기다.
내부에서 쓰는경우는 그냥 딱 한번만 써도 될때 이렇게 쓰면 된다는,,
이 코드를 다시 수정해보면
let add = function(a,b){ //콜백함수
return a+b;
};
let multifly = function(a,b){ //콜백함수
return a*b;
};
let doWhatever = function(a,b){ //콜백함수
console.log(here are your two numbers back ${a} , ${b});
}
let calc = function(num1, num2, callback){
if(typeof callback === "function"){
return callback(num1 , num2);
}
};
console.log(calc(2,3, add));
console.log(calc(2,3, doWhatever));
console.log(calc(2,3, function(a,b){
return a-b;
}));
// 이거는 타입이 함수형태만 일치할경우 콜백함수를 실행시키는조건을 건거다.
// 누군가 쓰레기 코드를 전달했을때(함수가 아닌걸 전달했을떄) 실행이 되는걸 방지하는거다.
***콜백함수의 또다른 예제>>>>>>>>>>
var myArr = [{
num:5,
str:'apple'
},{
num:7,
str:'cabbage'
},{
num:1,
str:'ban'
}];
myArr.sort(function(val1 ,val2){
if(val1.str > val2.str){
return -1;
}else{
return 1;
}
});
console.log(myArr);
// 이코드는 배열이 하나 있는데,
// 배열안에 숫자와 문자가 있고,
// 내용을 순서대로 정렬하고싶을때의 코드이다.
// sort함수를 이용해서 정렬을하는데,
// 문자열비교시 문자열 알파벳 순서대로 비교하는거고,
// 숫자는 숫자순서대로 비교한다.
// 조건을 주는데, 둘중 숫자 크기가 크면 -1 아니면1
// 참일떄는 -1 , 거짓이면 1 이다.
// 즉 , -1 은 자리를 변경하지않고, 1 은 자리를 바꾸는거다.
//예를들어 5 , 7 ,1 이 있으면
// 5 와 7 을 변경한다고 했을떄,
// -1 (참) 일경우 변경하지않고 그대로 둔다
// 근데 1(거짓)일경우 5 와 7 둘자리를 바꾼다. 그럼 7 ,5 이렇게 자리가 변경된다.
//이런식으로 자리를 병경하면서 순서를 바꾸면서 정렬이되는 원리이다.
// if(val1.str > val2.str){ 이부분을 수정하면서 조건을 다양하게 변경할수있다.
// if(val1.num > val2.num){ // 이렇게 숫자를 비교하는거고
// if(val1.str < val2.str){ 크기가 큰순서대로 , 작은순서대로 바꿀수있다.
3.재귀함수
재귀함수는 함수안에서 함수가 자신을 다시 리턴값으로 반환하는 형태이다.
(한마디로 좀 복잡하다)
function factorial (num){
if(num <= 1){
return 1;
}else{
return num * factorial(num - 1);
}
}
console.log(factorial(3));
이코드는
팩토리얼 함수를 호출했을때 3을 넘겨주면,
function factorial (3){
if(num <= 1){
return 1;
}else{
return 3 factorial(3 - 1);
// 3 3 - 1 하고 여기서
}
}
console.log(factorial(3));
function factorial (3){
if(num <= 1){
return 1;
}else{
return 3 factorial((3 - 1) - 1); // num은 이렇게 바뀐다 그리고 또 호출!
// 3 3 - 1
}
}
console.log(factorial(3));
function factorial ((3 - 1) - 1){
if(num <= 1){
return 1;
}else{
return (3 - 1) - 1 factorial((3 - 1) - 1); // num은 이렇게 바뀐다 그리고 또 호출!
// 3 (3 - 1) * (3 - 1)- 1
}
}
console.log(factorial(3));
function factorial ((3 - 1) - 1){
if(num <= 1){
return 1;
}else{
return (3 - 1) - 1 factorial((3 - 1) - 1); // num은 이렇게 바뀐다 그리고 또 호출!
// 3 (3 - 1) (3 - 1)- 1 (3 - 1) - 1
}
}
console.log(factorial(3));
이렇게 된다.
// 3 (3 - 1) (3 - 1)- 1 * (3 - 1) - 1
이부분을 계산하게되면
3 2 2 - 1 * 2 - 1
6 1 1
6 * 1
6
6이 나오게 된다.