4장 함수와 스코프

moono·2023년 1월 29일
1

Do it

목록 보기
1/3

🏁 함수(function)

특별한 목적의 동작 여러 개의 명령을 묶은 것
⇒ 입력 받아 블록 내부의 코드 실행한 후 실행 결과를 반환 하는 과정의 묶음

➰ 함수를 왜 사용할까?

  • 재사용성: 같은 작업을 여러 곳에서 반복해야할 때마다 호출하여 사용하면 편리하다.
  • 가독성: 함수의 이름만 보고도 어떤 기능을 수행하는지 유추 가능
  • 모듈화: 유지보수나 버그 발생 시 편리
  • 문제가 복잡해지거나 기능이 많아진다면? ⇒ 기능별로 나누는 것이 좋다

➰ 함수 선언하기

  1. 함수를 선언할 때 function 예약어 사용
  2. 함수 이름 적기 ⇒ 나중에 그 이름 사용해서 함수 실행
  3. 중괄호 {} 안에 실행할 여러 개의 명령 묶기
function 함수명() {
  명령()
}

➰ 함수 호출하기

함수를 호출할 때는 함수 이름 뒤에 꼭 소괄호 () 붙이기

함수명()

🤔 호출 먼저? 선언 먼저?

함수 선언 소스는 어디에 넣어도 상관 없지만 웹브라우저에서 js 소스를 해석할 때
변수 선언이나 함수 선언 부분을 가장 먼저 해석하므로
보통 함수를 선언하는 소스를 실행 소스보다 앞에 넣는다
(함수 선언 소스를 실행 소스보다 뒤에 두면 함수에서도 호이스팅이 발생해 선언 부분을 앞으로 끌어당겨 해석함)
⇒ 🤔 문제가 발생하나????
⇒ 순서 상관없이 잘 작동함, 보통 한 파일 안에 여러 함수 선언했을 때 소스 앞부분이나 뒷부분에 함수 선언 부분을 모아두고 필요시 호출해서 사용함

// 함수 호출 먼저
a();
function a() {
}

// 함수 선언 먼저
function a() {
}
a();


➰ 매개변수(parameter), 인수(argument), return문

함수를 선언, 호출할 때 각각 변수를 사용해야 하는데 이걸 매개변수 , 인수 라고 하고,
두개를 통틀어 인자 라고 부르기도 함

➰ 매개변수

함수 선언할 때 외부에서 값을 받는 변수
함수 이름 옆의 소괄호 () 안에 매개변수 이름을 넣어준다.
매개변수는 선언된 함수에서만 사용하고,
여러 개의 매개변수가 필요할 때는 사이에 , 를 넣어 나열한다.

➰ 인수

매개변수가 있는 함수를 호출할 때 실제 값을 넣어 넘겨주는 것

➰ return문

함수의 실행 결과를 함수 실행한 시점으로 넘겨주는 것 ⇒ 함숫값을 반환(return)한다
⇒ 결과값을 반환할 때는 return 다음에 넘겨줄 값이나 변수 지정
⇒ 결과값을 함수 밖으로 넘겨주면 그 결과값은 자유롭게 사용이 가능하다.

function sum(a, b) { // a, b 매개변수, 를 가진 함수 선언
  return a+b // sum 값을 원래 호출했던 document.write 자리로 반환
}

// 10, 20 인수를 넣어 함수 호출
document.write(`두 수의 합 : ${sum(10, 20)}`)

➰ 기본 매개변수

매개변수의 갯수가 정해진 함수를 선언하고
함수를 호출할 때 그 갯수만큼의 인자를 전달받지 못한다면
전달받지 못한 매개변수의 값은 undefined 가 되어 결과값은 NaN이 된다.
⇒ 이때 매개변수의 기본값을 지정해서 사용할 수 있음

function multi(a, b=5, c=10) { // b와, c의 default 값 지정
	return a+b+c;
}

console.log(multi(1,2,3)) // a=1, b=2, c=3
console.log(multi(5,6)) // a=5, b=6, c=10(default)
console.log(multi(10)) // a=10, b=5(default) , c=10(default)


✔️ 함수선언식과 함수표현식 ⇒ 더 공부할 필요 있음!!!

➰ 함수선언식(Function Declartion)

함수명이 정의되어 있고, 별도의 할당 명령이 없다.
⇒ 함수 전체를 호이스팅한다.
⇒ 정의된 범위의 맨 위로 호이스팅돼서 함수 선언 전에 함수를 사용할 수 있다.

function sum(a, b) {
  return a + b;
}

➰ 함수표현식(Function Expression)

정의한 function을 별도의 변수에 할당한다.
⇒ 호이스팅에 영향을 받지 않는다.
⇒ 선언과 호출 순서에 따라서 정상적으로 함수가 실행되지 않을 수 있다.

const sum = function(a, b) {
  return a + b;
}

🤔 hoisting?

이 뭔지 더 찾아보고 정리해야겠다.



🏁 변수의 유효 범위(scope)

함수 안에서 변수를 사용할 때 선언한 변수가 어디까지 적용되는지에 대한 적용 범위
⇒ 어느 위치에서 변수를 접근할 수 있는지를 가르킴

➰ 전역 변수(global)

프로그램 시작 부분에서 변수를 선언하면 프로그램 전체에서 사용할 수 있는 변수
제일 바깥 범위(함수 안에 포함되지 않은) 에 변수를 만드는 것

  • var 로 만든 전역변수 : Window 객체에 할당됨
    ⇒ Window 객체는 되게 무거워서 이 변수값 하나를 사용하기 위해 Window 객체를 불러오는건 비효율적
    ⇒ 문제점: 함수 내에 var 키워드를 빼먹으면 전역변수화 되는 것 조심해야 함
// var 함수 레벨 스코프
function sum(a,b) {
  var result = a+b // var로 선언한 result 변수는 sum 함수 안에서만 유효하다.
  // console.log(result) //30
} 

sum(10,20);
console.log(result) // result is not defined
  • let , const 로 만든 전역변수 : 범위가 스크립트로 나옴 스크립트 스코프
    ⇒ 스크립트 스코프 : js 파일 전체에서 사용하는 변수다 라는 뜻으로 무거운 Window 객체 가져오지 않아도 됨
// let const 블록 레벨 스코프
const factor = 5; // 전역 변수(프로그램 안에서 전부 사용 가능)

function calc() { 
  return num * factor; // num 변수에 접근할 수 없음
}
{
  const num = 10; // 이 num 은 지금 있는 블록에서만 사용 가능, 선언한 블록 벗어나면 사용 불가능
  let result = calc(); 
  console.log(`result : ${result}`);
}
// 위의 블록 레벨 스코프를 문제없이 동작하게 바꿔보자
const factor = 5; // 전역 변수(프로그램 안에서 전부 사용 가능)

function calc(n) { // num이라는 값이 n으로 사용이 됨
  return n * factor; 
}
{
  const num = 10;
  let result = calc(num) // num을 인자로 넘겨주고
  console.log(`result : ${result}`);
}

➰ 지역 변수(local)

함수 내부에서 선언된 변수(함수 내부에서만 사용 가능)
함수가 종료되면 메모리에서 사라진다.
⇒ 함수를 실행하는 동안에만 변수들이 사용되다가 함수 실행이 끝나고 반환하게 되면 변수의 값은 사라지게 됨

🤔 전역 변수, 지역 변수 예시

let x = 'global'; // 전역변수

function ex() {
  let x = 'local'; // 지역변수
  x = 'change';
}

ex(); // ex 함수로 x바꾸려해도 ex 밖의 x는 전역변수
console.log(x); // 여전히 'global'

****
  
let x = 'global';

function ex() {
  x = 'change';
}

ex();
console.log(x); // 'change'
// 함수 ex 범위 내에 x가 없기 때문에 더 넓은 범위의 전역 스코프에서 찾아서 바꿈
// 함수 내에서 변수 선언하지 않아 재할당


➰ 키워드에 따른 스코프

  • 함수 레벨 스코프 : var 키워드 사용한 변수(ES5)

  • 블록 레벨 스코프 : let , const 키워드 사용한 변수(ES6)
    자바스크립트에서 얘기하는 블록은 {} 로 둘러싸인 영역을 의미하는데,
    블록별로 변수의 유효 범위가 결정되는 것을 블록 스코프라고 함
    ⇒ 선언한 블록을 벗어나서는 사용할 수 없다.


➰ 스코프 체인(scope chain)

전역 변수와 지역 변수의 관계에서 나오는 개념으로
내부 함수에서는 외부 함수의 변수에 접근이 가능하지만, 외부 함수에서는 내부 함수의 변수에 접근할 수 없는데
내부에서 범위를 넓히면서 찾는 관계를 스코프 체인이라고 부른다.

let name = 'zero'
function outer() {
  console.log('외부', name);
  function inner() {
    let enemy = 'nero';
    console.log('내부', name);
  }
  inner();
}
outer();
console.log(enemy) // enemy is not defined // 외부에서 내부 함수의 변수에 접근 불가능

⇒ inner 함수는 name 이라는 변수를 찾기 위해 본인 스코프를 뒤지고,
없으면 한단계 위로 올라가 outer를 뒤지고 없으면 다시 올라가 결국 전역 스코프에서 뒤진다.
⇒ 전역 스코프에 name 이 있어서 'zero' 값을 얻었는데, 만약 전역 스코프에도 없다면 변수를 찾지 못해 에러 발생


➰ 렉시컬 스코핑(lexical scoping) === 정적 스코프

스코프는 함수를 호출할 때가 아니라 선언할 때 생김 : 정적 스코프

let name = 'zero';
function log() {
    console.log(name)
}

function wrapper() {
    name = 'nero';
    log();
}

wrapper() // 'nero'

****
  
let name = 'zero';
function log() {
    console.log(name)
}

function wrapper() {
    let name = 'nero';
    log();
}

wrapper() // 'zero'

스코프는 함수를 선언할 때 생긴다!
log 안의 name 은 wrapper 안의 지역 변수 name 이 아니라 전역 변수 name 을 가리키고 있는데
이걸 렉시컬 스코핑 이라고 함

함수를 처음 선언하는 순간,
함수 내부의 변수는 자기 스코프로부터 가장 가까운 곳(상위 범위에서)에 있는 변수를 계속 참조하게 되는데,
위 예시에서 log 함수 안에 있는 name 변수는
선언 시 가장 가까운 전역변수 name을 참조하게 된다.
그래서 wrapper 함수 안에 있는 log 함수를 호출해도 지역변수 name='nero'를 참조하는게 아니라
전역변수 name='zero'를 참조하는 것
⇒ 무슨 짓을 해도 log 함수가 한 번 선언된 이상 전역 변수인 name 변수가 다른 걸 가리키게 할 수 없는데,
유일한 방법은 위에 처럼 전역변수를 다른 값으로 바꾸는 것 뿐!


➰ 자바스크립트 변수, 이렇게 사용하자!

var 변수 보다 let, const 변수를 사용하자
⇒ var 변수는 다시 선언 할 수 있어 실수로 같은 변수를 다시 선언해도 오류가 발생하지 않아
여러 사람이 함께 진행하는 프로젝트라면 호이스팅이 없는 let 이나 const 변수 사용하는 것이 안전

전역 변수는 최소한으로 사용하자
⇒ 프로그램의 모든 곳에서 접근할 수 있어 편리하지만 어디에서든 값을 변경할 수 있기때문에
예상치 못한 곳에서 오류 발생할 확률이 높다.

객체 선언은 const를 사용하자
⇒ 프로그래밍 도중 객체가 바뀌지 않도록 const를 사용해 선언하자
const로 선언해도 객체 안에 있는 프로퍼티는 얼마든지 수정할 수 있다.

함수 내에 변수 선언 키워드를 꼭 붙이자
⇒ 실수로 키워드를 붙이지 않으면 전역 변수로 인식해 어디서든 수정이 가능해지니 꼭 확인하자



➰ 전역 변수를 지역 변수로 만드는 방법 ⇒ 네임스페이스 알아보기

전역 변수 대신 한 번 함수 안에 넣어 지역변수로 만들기 또는 객체 안의 속성으로 만들수도 있음

let obj = {
  x: 'local',
  y: function() {
    alert(this.x)
  }
}

⇒ 이렇게 하면 obj.x , obj.y() 이렇게 접근해야 하기 때문에 obj를 통째로 덮어쓰지 않는 이상 다른 사람과 섞일 염려가 없다. 전역변수를 하나로 최소화해서 변수가 겹칠 우려도 최소화할 수 있음!
⇒ 하지만 누군가 고의적으로 코드 밑에 스크립트를 추가해서 x,y를 바꿀 수 있음



🏁 이름 없이 사용하는 함수 표현식

➰ 익명 함수

함수 이름이 없는 함수, 변수에 할당해서 사용한다.
⇒ 함수 표현식이라고도 한다.
⇒ 주로 다른 함수에 매개변수로 사용할 때 많이 사용하며(콜백 함수), 변수처럼 사용하기 위해 사용한다.

let sum = function(a, b) {
  return a + b;
}
console.log(`함수 실행 결과 : ${sum(10,20)}`);

****
  
let greeting = function () {
  console.log("안녕하세요?");
}
greeting();


➰ 즉시 실행 함수

한 번만 실행하는 함수일 경우 함수를 정의함과 동시에 실행할 수 있는 함수
⇒ 함수를 식 형태로 선언하므로 마지막에 세미콜론 ;을 붙인다.

// 즉시 실행 함수 기본 형식
(function() {
  ...
} ());
  
(function(매개변수) {
  ...
}(인수));
// 즉시 실행 함수 예제
(function(a, b) {
  let sum = a + b;
  console.log(`함수 실행 결과 : ${sum}`)
} (100, 200))


➰ 화살표 함수

ES6 이후에 함수를 좀 더 간결하고 읽기 쉽게 만들기 위해 만들어진 것으로
표현식을 사용해 함수를 정의할 경우에만 사용할 수 있다.

(매개변수) => {함수 내용}

매개변수를 먼저 적어주고, function 대신 => 를 넣어주기
⇒ 함수에서 실행하는 문장이 하나인 경우 중괄호{}return 을 생략할 수 있다.
⇒ 만약 매개변수가 없다면 소괄호 비워둔다. + 매개변수가 1개라면 소괄호를 생략할 수 있다.

let sum = function(a,b) {
  return a+b;
}
sum(10, 20)

****

let sum = (a, b) => {return a + b}

****

let sum = (a, b) => a + b;


➰ 콜백 함수

함수 안에 매개변수로 들어가는 함수로,
매개변수를 통해 전달되고 전달받은 함수의 내부에서 특정 시점에 실행된다.
보통 함수 안에 다른 함수를 매개변수로 받는 경우 많다고 한다.

✔️ 장점

  • 함수를 인자로 받기 때문에 필요에 따라 함수의 정의를 달리해 전달할 수 있다.
  • 함수를 굳이 정의하지 않고 익명함수로 전달 가능하다.
  • 비동기 처리방식의 문제점을 해결할 수 있다.
    ⇒ 특정 코드의 실행이 끝날 때 까지 기다리지 않고 다음 코드로 바로 넘어가는 것을 의미

✔️ 단점

  • 너무 남용하면 코드의 가독성이 떨어진다.
  • 에러 처리가 어렵다.
function greeting() {
  console.log("안녕하세요?")
}
setInterval(greeing, 2000); // 2초마다 greeing 함수를 반복해
// setInterval은 특정 시간마다 반복해서 함수를 실행하도록 하는 함수

****
  
setInterval(function () {
  console.log("안녕하세요?")
}, 2000); // 콜백함수 이면서 함수 이름 없이 바로 실행되는 함수(익명 함수)


➰ 자바스크립트 함수는 1급 시민(first-class) ⇒ 공부 더 필요!!!

자바스크립트의 경우 함수 가 1급 시민
숫자 타입의 데이터들은 대부분의 프로그래밍 언어에서 1급 시민의 조건을 충족한다.

1급 시민

  • 변수에 할당할 수 있어야 한다.
let sum = (a, b) => a + b;
sum(10, 20);
  • 함수를 다른 함수의 인자로 전달할 수 있어야 한다.
    ⇒ hello() 함수를 미리 정의해 놓고 login() 함수를 실행할 때 fn 매개변수에 hello() 함수를 인자로 넘겨주기
function hello() {
  return "안녕하세요.";
}
function bye() {
  return "안녕히 가세요.";
}
function userCheck(name, fn) { // fn 함수를 전달받아서 실행
  console.log(`${name}`, fn())
}

userCheck("홍길동", hello); //홍길동님, 안녕하세요.
userCheck("도레미", bye); //도레미님, 안녕히 가세요.
  • 함수에서 다른 함수 반환하기 (반환값(return값)으로 전달할 수 있어야한다.)
    ⇒ 함수는 값이므로 함수에서 다른 함수를 반환할 수 있다.
let init = () => {
  return function(a, b) {
    return a - b > 0 ? a - b : b - a;
  }
}
console.log(`${init()(10,20)}`) // 10

1급 함수 ????

1급 함수란 1급 객체이면서 아래의 추가 조건을 만족하는 함수를 말한다.

  • runtime 생성이 가능하다.
  • 익명으로 생성 가능하다.
function addMaker(a) {
  return function(b) {
    return a + b;
  }
}

let add5 = addMaker(5);
let add7 = addMaker(7);

console.log(add5(5)) // 10
console.log(add7(2)) // 9



🏁 전개구문(spread 문법?)

값을 펼쳐주는 구문
배열처럼 값이 다양한 자료를 한꺼번에 인자로 넘겨주거나
배열과 배열을 합할 때처럼 배열을 하나의 덩어리로 처리해야 할 때 유용하다.

➰ 전개구문 작성 방법

... 기호를 이용해 사용해 값만 꺼내 펼쳐서 보여준다.
⇒ 문자열, 배열, 객체처럼 여러 개의 값을 담고 있는 자료형에서 다른 정보 필요 없이 그 안의 값만 꺼내 사용하려고 할 때 매우 유용하다.

let fruits = ["banana", "apple", "grape"];

console.log(fruits) // (3) ['banana', 'apple', 'grape']
console.log(...fruits) // banana apple grape


➰ 나머지 매개변수

함수를 선언하면서 나중에 몇 개의 인수를 받게 될지 알 수 없는 경우
전개 구문을 사용해 매개 변수를 만드는데 이것을 나머지 매개변수 라고 한다.

// 인수의 개수와 상관없이 숫자를 더하는 프로그램
function addNum(...numbers) {
  let sum = 0;
  
  for(let number of numbers) {
    sum += number;
  }
  return sum;
}

console.log(addNum(1, 3)); // 4
console.log(addNum(1, 3, 5, 7)); // 16
// 인수의 일부분만 나머지 매개변수로 받기
function displayFav(first, ...favs) {
  let str = `가장 좋아하는 과일은 ${first}군요`
  return str;
}
console.log(displayFav("사과", "바나나", "포도")) // 가장 좋아하는 과일은 사과군요



🏁 시간 고려하는 타이머 함수

자바스크립트에는 특정 시간이 되었을 때 함수를 실행하거나 특정 시간 동안 함수를 반복하기 위해 시간을 재는 함수가 있는데 이것을 타이머 함수 라고 한다.
타이머 함수는 실행할 함수와 시간을 지정하는 함수로,
타이머 함수에 실행할 함수를 인자로 받기 때문에 콜백함수를 매개변수로 사용한다.

➰ setInterval() : 일정 시간마다 반복하기

setInterval(콜백함수, 시간) // 시간은 밀리초 단위를 사용(1000 밀리초는 1초)

➰ clearInterval() : 반복 실행 멈추기

setInterval() 함수는 한 번 실행하면 웹 브라우저를 종료하기 전까지 문서를 새로고침하거나
다른 페이지로 이동해도 계속 실행되는데,
함수를 계속 반복하는게 아니라 특정 조건이 됐을 때 반복 실행을 멈추려면 clearInterval() 함수를 사용해야 한다.

clearInterval(타이머)

✔️ 타이머 종료하기(setInterval() 과 clearInterval() 사용)

let counter = 0;

let timer = setInterval(() => { // 타이머 시작
  console.log("안녕하세요")
  counter++; // 인사말 표시 횟수가 1 증가합니다.
  if (counter === 5) 
    clearInterval(timer); // counter = 5 가 되면 타이머를 종료합니다.
}, 2000);

⇒ 콘솔창에 "안녕하세요" 문자열 앞에 숫자가 5까지만 늘어나고 더이상 늘어나지 않는다.

➰ setTimeout() : 특정 시간 이후 실행하기

지정한 시간이 지난 후에 콜백함수를 실행합니다.

setTimeout(콜백 함수, 시간)
setTimeout(() => {
  console.log("Hi")
}, 3000) // 3초 후에 Hi 라는 인사말 표시하기



📌 Reference

함수선언식 함수표현식
스코프 참고링크
1급함수 참고링크
콜백함수 읽어보기

0개의 댓글