[Javascript] 함수

앙버터·2023년 12월 5일

Web Programming

목록 보기
1/7

1. 사용자 정의 함수

함수 정의 방법

  1. function 명령으로 정의
  2. 함수 생성자 (constructor) 경유로 정의
  3. 함수 literal 표현으로 정의
  4. arrow 함수로 정의

1) function 명령으로 정의

function getTriangle(base, height) { // global 선언
 return base * height / 2;
};
  • 모든 datatype을 var로 선언하기 때문에 파라미터의 타입을 명시하지 않는다.
    getTriangle(base, height) 에서 base, heght의 타입과 getTriangle의 리턴타입이 없음
console.log(getTriangle(5, 2));
>> 5

2) 함수 생성자 (constructor) 경유로 정의

권장하지 않으므로 신경쓰지 말기!

3) 함수 literal 표현으로 정의

var getTriangle = function(base, height) {
	return base * height 
};
  • 함수도 DataType이기 때문에 literal로 선언 가능하다. ⭐⭐
  • var 로 선언하는 변수의 초기값이 함수
    따라서 변수 이름 = 함수 이름
  • function(base, height) 의 function에 함수 이름을 쓸 필요 없다 (쓴다고 해도 무시됨)
console.log(getTriangle(5, 2));
>> 5

4) Arrow 함수로 정의하기 ⭐⭐

(파라미터, ...) => { 함수의 본체 }

let getTriangle = (base, height) => {
	return base * height / 2;
};

console.log(getTriangle(5, 2));
>> 5
  • 함수의 기능이 단순한 경우 단순화하여 표기 가능
// return 생략
let getTriangle = (base, height) => base * height / 2; 

//  파라미터 1개인 경우 튜플의 괄호 생략
let getCircle = radius => radius * radius * Math.PI; 

// 파라미터 없는 경우
let show = () => console.log('안녕하세요, 자바스크립트!');

함수 정의시 주의사항

1) 함수는 데이터타입 중 하나이다.

함수로 지정된 변수를 출력하면, 함수 본문 내용이 출력

console.log(getTriangle);

getTriangle 변수값이 원래 함수로 지정되어 있다가, 숫자 0으로 변경

getTriangle = 0;
console.log(getTriangle);
>> 0

✅ getTriangle의 데이터타입이 number로 변경됨
✅ 해당 함수는 사라지며, error 발생X

2) hoisting effect : function 실행보다 선언이 나중에 되어도 정상 동작

  • function 을 사용한 함수 정의
console.log('사각형의 면적:' + getRectangle(5, 2));
function getRectangle(base, height) {
  return base * height;
}

✅ 정상적으로 5를 출력함

3) 함수 literal은 실행할 때 판단된다.

  • var로 선언하는 함수 리터럴 ➡ 변수선언
    따라서 함수 먼저 선언하지 않으면 에러 발생

변수를 먼저 사용하고 나중에 변수값이 결정되는 경우
변수 먼저 사용할 때 undefined의 값이 할당됨

console.log(getTriangle(5, 2));
var getTriangle = (base, height) {
	return base * height / 2;
};

💥 'getTriangle is not a function' 에러 발생

console.log('사각형의 면적:' + callFunctionLiteral());
callFunctionLiteral = function () {
	console.log('함수 리터럴을 선언한 후에 이 함수 리터럴을 사용해야만 에러 발생X');
}

💥 'callFunctionLiteral is not defined' 에러 발생


변수 Scope

  • 글로벌 scope : 함수 바깥에서(local scope 외 나머지) var로 선언된 변수, script 전체에서 참조가능
  • 로컬 scope : 함수의 안에서 var로 선언된 변수, 정의된 함수 안에서만 참조
  • Block scope : let으로 변수 선언 (ES6부터 사용가능)
    ▶ 블록 {...} 에 선언되고, 해당 블록 안에서만 사용 가능

글로벌/로컬 변수는 var로 선언하여 사용

var scope = 'Global Variable';
function getValue() {
  var scope = 'Local Variable';
  return scope;
}
console.log(getValue()); // 'Local Variable'
console.log(scope); // 'Global Variable'

특히 로컬 변수 선언에 var사용 필수
로컬변수를 var로 선언하지 않으면 글로벌 변수로 인식됨 ⭐⭐

scope1 = 'Global Variable'; // global 변수로 인식
function getValue1() {
  // scope1이 var로 선언되어 있지 않으면, global변수로 인식됨
  scope1 = 'Local Variable';  // 기존 global 변수인 scope1 값을 변경
  return scope1;
}
console.log(getValue1()); // 'Local Variable'
console.log(scope1); // 'Local Variable'

로컬변수의 유효 범위
➡ 함수 중간에 로컬 변수를 선언하는 경우

var scope = 'Global Variable';
function getValue() {
// 아래의 scope : local variable
// hoist에 의해 에러는 발생하지 않으나
// 실제 scope의 값이 뒤에 결정되어 undefined 출력
console.log(scope);
var scope = 'Local Variable';
return scope;
}

✅ 오류의 많은 부분을 차지함
✅ 결론 : 가급적이면 로컬변수는 함수 첫 부분에 선언한다.

  • 함수 전달 매개변수(파라미터)의 유효 범위

1) 매개변수가 기본형
: 로컬변수로 처리되고, 글로벌 변수에 영향을 미치지 않음

var value = 10;

function decrementValue(value) {
  value--;
  return value;
}

console.log(decrementValue(100)); // 99
console.log(value); // 10

2) 매개변수가 참조형
: 로컬변수로 처리되나, 글로벌 변수에 영향 미침

var value = [1, 2, 4, 8, 16]; // 주소=200 이라고 가정
function deleteElement(value) { // 주소=200을 참조
  value.pop();
  return value; // 주소=200의 값이 변함
}

console.log(deleteElement(value)); // [1,2,4,8]
console.log(value); // [1,2,4,8]

3) 블록 레벨 scope : 원래 자바스크립트에서 지원 안함 (ES5)

if (true) {
  // 함수가 아닌 if, while문 등 
  // 제어문에서 var로 선언한 변수는 global 변수로 인식됨
  var i = 5;
}

console.log(i); // 5

4) 즉시실행함수 (IIFE) ⭐⭐

  • immediately invoked function expression
  • 블록레벨 scope의 효과를 낼 수 있음

( function() {...} ) (); <- 함수 선언 + 즉시 실행

(function() { // 무명 함수 선언
  var a = 10; // 블록레벨
  console.log(a); // 10
})(); // 즉시 실행

console.log(a); // a는 스코프 밖이므로 에러 발생
(function() { // 무명 함수 선언
  var a = 5; // 블록레벨
  console.log(a); // 10
}).call(this); // 즉시 실행

console.log(a); // a는 스코프 밖이므로 에러 발생

5) let 명령으로 블록레벨 스코프 지원 (ES6)

if (true) {
  let i = 5; // 블록레벨
}
console.log(i); // 에러 발생

{
  let a = 5;
  console.log(a); // 5
}
console.log(i); // 에러 발생
// switch문의 각 case마다 똑같은 이름의 변수를 let으로 선언하면 error 발생
// switch(x) {
//   case 0:
//     let value = 'x:0';
//   case 1:
//     let value = 'x:1';
// }

// switch문 이전에 let으로 변수 선언하여 사용할 것
let value='';
switch(value) {
  case 0:
    value = 'x:0';
  case 1:
    value = 'x:1';
}

함수 매개변수의 다양한 표기법

1) 자바스크립트는 기본적으로 매개변수의 수를 체크하지 않음
2) 자바스크립트는 매개변수 정보를 argument 객체에 제공함

✅ argument라는 function 객체의 field를 호출할 때, argument data들이 자동 저장됨
✅ argument 객체에는 건네진 모든 인수 값이 보존됨

function showMessage(value) {
  console.log(value);
}

showMessage(); // arg[0] = undefined, arg[1] = undefined
// arg.length = 0
showMessage('철수'); // arg[0] = '철수', arg[1] = undefined
// arg.length = 1
showMessage('철수', '영희'); // arg[0] = '철수', arg[1] = '영희'
// arg.length = 2

3) 매개변수의 수를 체크할 경우 추가 코딩 필요

  • throw 객체 사용하여 error 발생시키기
// 인수 갯수가 맞지 않을 때 error 처리
function showMessage1(value) {
  if (arguments.length !== 1) { // 예외처리
    throw new Error('인수의 수가 서로 다릅니다:' + arguments.length);
  } // Error : 함수명
  console.log(value);
}

try {
  showMessage1(' 철수', ' 영희');
} catch(e) {
  window.alert(e.message);
}
  • 함수 내에서 매개변수가 정의되어있지 않은 경우 default 값을 설정
function getTriangle(base, height) {
  if (base === undefined) { base = 1; }
  if (height === undefined) { height = 1; }
  return base * height / 2;
}

console.log(getTriangle(5)); // 2.5

4) 가변길이 매개변수 함수 정의하기

  • 매개변수 대신 arguments 객체를 이용하여 구현
    arguments 객체
    • 매개변수에 들어온 값들이 모두 들어있음
    • 배열처럼 보이지만 array 데이터타입은 아님
function sum() { // 인수 지정X
  var result = 0;
  console.log(arguments.length);
  // 주어진 인수를 순서대로 취득하여 차례로 더하는 처리
  for (var i = 0, len = arguments.length; i < len; i++) {
    console.log(arguments[i]);
    var tmp = arguments[i];
    if (typeof tmp !== 'number') {
      throw new Error('인수값이 숫자가 아닙니다.:' + tmp);
    }
    result += tmp;
  }
  return result;
}

try {
  // 파라미터 수를 원하는 대로 줄이고 늘릴 수 있다는 장점
  console.log(sum(1, 3, 5, 7, 9, 11));
} catch(e) {
  window.alert(e.message);
}

5) 명명된 매개변수 사용 : { } 객체 표기 사용
파라미터를 Object로 변환

  • 매개변수가 많아져도 코드의 의미를 알기 쉬움
  • 생략된 매개변수 쉽게 인지 가능
  • 매개변수의 순서 인지 가능
// args : {} 객체 데이터타입
function getRectangle(args) { // default 값 정의
  if (args.base === undefined) { args.base = 1; }
  if (args.height === undefined) { args.height = 1; }
  return args.base * args.height;
}

console.log(getRectangle({ base:5, height:4 })); // 20
console.log(getRectangle({ height:5 })); // 5
console.log(getRectangle({ height:6, base:2 })); // 12

ES6 매개변수 표기법

1) 매개변수 default 값 선언

function getTriangle(base = 1, height = 1) {
  return base * height / 2;
}

console.log(getTriangle(5)); // 2.5
function multi(a, b = a) {
  return a * b; 
}
console.log(multi(10, 5)); // 50
console.log(multi(3)); // 3*3 = 9

default 값 선언할 때 뒤에 있는 변수 참조하면 에러 발생 가능

// a = b를 인수의 처음에 사용하면 안됨
// 왜냐하면 나중에 선언된 인수 b를 사용하기 때문
function multi(a = b, b = 5) {
  return a * b; 
}
console.log(multi());

default 값 사용시 주의점

  • 함수를 부를 때 매개변수가 명시적으로 건네지지 않은 경우에만 default 값이 사용됨 (매개변수 값이 undefined도 포함)
    ✅ 함수를 부를 때 null/false/0/빈 문자열 '' 등 의미상 비어 있음을 나타내는 값이라도 명시적으로 건네지면 default 값 사용X
function getTriangle(base = 1, height = 1) {
  return base * height / 2;
}
console.log(getTriangle(5, null)); // 0
console.log(getTriangle(5, undefined)); // 2.5
  • default 값을 갖는 매개변수는 매개변수 리스트의 끝에 선언
    ✅ default 값을 먼저 선언하면 코드의 의도가 명확하지 않음
function getRectangle(base=1, height) {
  return base * height;
}
// 10이 base의 값으로 넘겨지고, height는 undefined가 됨
console.log(getRectangle(10));

2) 가변길이 매개변수 함수 정의

  • 매개변수 앞에 ... 을 부여 -> 가변길이 인수를 배열(Array)로 처리
    (arguments 는 array가 아님)
function sum(...nums) { // nums를 array 데이터타입으로 자동 형변환
  let result = 0;
  console.log(nums);    // nums : array
  for (let num of nums) {
    if (typeof num !== 'number') {
      throw new Error('지정값이 숫자가 아닙니다:' + num);
    }
    result += num;
  }
  return result;
}

try {
  console.log(sum(1, 3, 5, 7, 9, 11));
} catch(e) {
  window.alert(e.message);
}
  • '...' 연산자에 의한 배열 매개변수의 전개
    ✅ Math.max는 단순 Array [] 형태로 매개변수를 넘길 경우 인식못함
// ...의 다른 사용 예
console.log(Math.max(15, -3, 78, 1));
console.log(Math.max([15, -3, 78, 1])); // error 발생 => NaN (argument가 1개인것으로 인식)

// 해결책
console.log(Math.max.apply(null, [15, -3, 78, 1])); // ES5 방식
console.log(Math.max(...[15, -3, 78, 1]));          // ES6 방식
// ...[15, -3, 78, 1] => 배열을 배열이 아닌 형태로 변환

3) 분할대입 방법을 통한 명명된 매개변수 전달

  • 매개변수 default 값 선언할 때 {} 사용
    -> object에 대해서도 default 선언 가능
function getTriangle({ base = 1, height = 1 }) {
  return base * height / 2;
}
console.log(getTriangle({ base:5, height:4 })); // 10
  • 함수에서 매개변수를 선언할 때, 전달 받은 매개변수 객체 전체에서 특정 property만 선택하는 것이 가능
function show({name}) { // 'name' property만 선택하여 가져옴
  console.log(name);
};

let member = {
  mid: 'Y0001',
  name: '심우진',
  address: 'shim_me_shim_Me_ya@example.com'
};

show(member); // 심우진

함수 호출과 return 값

1) 복수의 return 값을 개별 변수에 대입

function getMaxMin(...nums) {
  // return 값 여러개 -> 배열로 return 가능
  // 별도의 배열 생성, 대입 과정 필요X
  return [Math.max(...nums), Math.min(...nums)];
}

let result = getMaxMin(10, 35, -5, 78, 0);
console.log(result); // [78, -5]

let [max, min] = getMaxMin(10, 35, -5, 78, 0); // 분할대입
//let [,min] = getMaxMin(10, 35, -5, 78, 0);
console.log(max); // 78
console.log(min); // -5

2) 재귀 함수

function factorial(n) {
  if (n != 0) { return n * factorial(n - 1); }
  return 1;
}
console.log(factorial(5));

3) 함수 매개변수가 함수인 경우 : callback 함수 ⭐⭐

  • 고차 함수 (higher function) : 함수의 매개변수가 함수이거나 return 값이 함수인 경우
function arrayWalk(data, f) { // 함수를 파라미터로 받음 = callback 함수
  for (var key in data) {
    f(data[key], key); // 내부에서 함수 실행
  }
}

function showElement(value, key) { // 사용자 정의 함수
  console.log(key + ':' + value);
}
var ary = [1, 2, 4, 8, 16];
arrayWalk(ary, showElement); // 함수를 파라미터로 부름
  • 고차 함수를 이용함으로써 베이스가 되는 기능 (여기서는 배열의 내용을 순서대로 추출하는 것)은 그대로 두고, 구체적인 처리 내용만을 자유롭게 바꿀 수 있다.
var result1= 0; // 글로벌 변수
function sumElement(value, key) {
 result1 += value;
}

arrayWalk(ary, sumElement);
console.log('합계:' + result1); // 31

4) 일회용 함수는 익명함수(anonymous function)로 표현 가능

arrayWalk(
  ary,
  function (value, key) {
    console.log(key + ':' + value);
  }
);

높은 수준의 함수 테마

1) 변수 scope chain

  • scope chain : 글로벌 객체, call 객체를 생성 순서대로 연결한 리스트
    • 글로벌 변수나 함수는 글로벌 객체의 property나 method
    • 함수 내 로컬 변수는 Activate 객체 (Call 객체)의 property
      (함수 arguments 객체도 Call 객체의 property)
// Global scope 객체 : property : var y = 'Global', method = outerFunc()
var y = 'Global';
function outerFunc() {
  // call scope 객체 : property : var y = 'Local Outer', arguments, method = innerFunc()
  var y = 'Local Outer';

  function innerFunc() {
    // call scope 객체 : property : var z = 'Local Inner', arguments, method(없음)
    var z = 'Local Inner';
    console.log(z); // Local Inner
    console.log(y); // Local Outer
    console.log(x); // 에러 (미정의)
  }
  innerFunc();
}
outerFunc();

2) 클로저 (Closure) ⭐⭐

  • local 변수를 참조하고 있는 함수 내의 함수
  • 특히, return 값이 local 변수를 참조하는 함수를 말한다.
  • 참조된 로컬 변수가 메모리에서 보존됨
    ✅ 고차함수, 함수를 return
  • 하나의 Class 역할 가능 (자바스크립트 library의 형태 = function을 return 하는 형태)
// closure함수 정의 : 특정 함수의 return값이 함수이면서, 
//                    return한 함수안에 부모 local 변수를 사용한 경우
function closure(init) {
  // counter : local variable
  var counter = init;

  return function() {
    return ++counter; // 부모 함수인 closure의 로컬변수를 사용
  }
}

// myClousure = function() { counter=1,    return ++counter;   };
var myClosure = closure(1);
console.log(myClosure()); // 2
console.log(myClosure()); // 3
console.log(myClosure()); // 4
  • function : 변수의 스코프에 대한 정보를 별도 object로 관리함
  • function을 return하는 경우 그 내부의 로컬변수를 계속 사용 가능
var myClosure1 = closure(1);
var myClosure2 = closure(100);

console.log(myClosure1()); // 2
console.log(myClosure2()); // 101
console.log(myClosure1()); // 3
console.log(myClosure2()); // 102

profile
그래도 해야지 어떡해

0개의 댓글