📝 TIL

[TIL] JS 함수, 클로저, call(), apply(), bind(), 구조분해할당

오늘 ONEUL 2022. 4. 22. 21:31

✍ Today I Learned

[함수 Functions]

  • 익명 함수란? 말 그대로 이름이 없는 함수. 예를 들면 함수 표현식에서 사용되는 함수는 변수를 통해 호출하기 때문에 함수 이름이 필요하지 않다.
const x = function (a, b) {return a * b};
let z = x(4, 3);
  • 함수를 만들어내는 자바스크립트 내장 함수 Function() 생성자를 사용하여 함수를 만들 수도 있다. 그러나 위의 익명 함수 코드와 동일하게 작동하기 때문에 굳이 사용할 필요는 없다.
const myFunction = new Function("a", "b", "return a * b");

let x = myFunction(4, 3);
  • 자바스크립트는 초기화가 아닌 선언만 호이스팅 되기 때문에 변수 선언은 시작 부분에서 하는 것이 좋다.
  • 함수 표현식, 화살표 함수는 호이스팅 되지 않는다.
  • 화살표 함수의 this는 상위 환경의 this를 가리킨다.
  • 자체 호출 함수(Self invoking)는 호출 없이 자동으로 실행된다. 먼저 실행되어야 하는 세팅값이나, 클로저에 사용된다.
(function () {
  let x = "Hello!!";  // I will invoke myself
})();

 

[매개변수와 전달 인자 Parameter & Arguments]

  • parameter - 정의된 함수에 나열된 변수
  • arguments - 함수에 전달되는 실제 값
  • 자바스크립트에서는 parameter의 타입을 따로 명시하지 않고, argument로 전달된 값에 대해 어떤 타입 검사도 하지 않는다.
  • 함수를 호출할 때 함수의 정의보다 적은 인수가 전달되더라도 오류를 발생시키지 않고, 전달되지 않은 값은 undefined를 설정한다.
  • 위와 같은 경우, parameter에 별도로 기본값을 설정할 수 있다 -> Optional Parameter
function multiply(a, b = 1) {
  return a*b
}

multiply(5, 2)          // 10
multiply(5)             // 5
multiply(5, undefined)  // 5
  • Optional Parameter는 항상 parameter의 가장 마지막에 설정해야 한다.
  • 자바스크립트 함수에는 arguments라는 내장 객체가 있다. 함수가 호출될 때 전달된 인수를 배열의 형태로 저장한다. (실제 Array 객체는 아님)
  • arguments 객체에 Optional Parameter의 값은 저장되지 않는다.
  • Rest Parameter - 생략 접두사(...)를 사용하여 특정 위치의 인수부터 마지막 인수까지를 한 번에 지정한다. 함수 내부에서 재할당 및 연산을 위해 필요한 파라미터와 값만 전달받는 파라미터를 분리하여 사용해야 할 때 편리하다.
function restParamFunc(a, b, ...others) {
    console.log(a + b);
    console.log(others);
    for (const arg of others) {
        console.log(`others : ${arg}`);
    }
}
restParamFunc(10, 20, 30, 40, 50); // 30, [30, 40, 50], others: 30...

 

[함수 호출 Invocation]

  • 소속된 객체 없이 전역에서 함수를 호출할 경우 this는 전역 객체가 된다. 웹 브라우저 전역 객체는 window 객체이다.
  • 객체에 소속된 함수를 할 경우 this는 소속된 객체 자체가 된다.

 

[함수 클로저 Closures]

  • Closures(클로저) - 함수와 함수가 선언된 어휘적 환경의 조합이다. 핵심은 스코프를 이용하여 변수의 접근 범위를 닫는(폐쇄)것이다.
  • 변수에 self-invoking을 할당하고, 외부 함수의 리턴 값으로 내부 함수를 할당하여 전역 변수처럼 활용할 수 있다.
const add = (function () {
  let counter = 0;
  return function () {counter += 1; return counter}
})();

add();
add();
add();

// 현재 count는 3이다.
  • 클로저는 외부 함수가 닫힌 후에도 외부 함수 내 변수에 접근할 수 있는 함수이다.

 

[call() 메서드]

  • func.call(thisArg, arg1, arg2) - 이미 할당되어있는 다른 객체의 함수, 메서드를 호출하는 해당 객체에 재할당할 때 사용된다. thisArg는 func호출에 제공되는 this의 값이다.
const person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}

const person1 = {
  firstName:"John",
  lastName: "Doe"
}

person.fullName.call(person1, "Oslo", "Norway");

 

[apply() 메서드]

  • func.apply(thisArg, \[argArray\]) - call() 메서드와 유사하다.
  • call() 메서드와 apply() 메서드의 차이점은? call() 메서드는 인수를 별도로 사용하고, apply() 메서드는 인수를 배열로 사용한다.
const person = {
  fullName: function(city, country) {
    return this.firstName + " " + this.lastName + "," + city + "," + country;
  }
}

const person1 = {
  firstName:"John",
  lastName: "Doe"
}

person.fullName.apply(person1, ["Oslo", "Norway"]);
  • 저번 json_iteration 2번 문제에서 배열의 최댓값을 return 받기 위해 사용했던 function이 apply()를 사용한 function이었다..! 배열에는 max()라는 메서드가 없기 때문에 apply()를 이용해서 대신 적용할 수 있다.
function myArrayMax(arr) {
    return Math.max.apply(null, arr);
}

 

[bind() 메서드]

  • func.bind(thisArg, arg1, arg2) - 이미 할당되어있는 다른 객체의 함수, 메서드를 호출하는 해당 객체로 빌려와 실행시킬 때 사용된다.
  • call() 메서드와 apply() 메서드는 내가 뭔가를 시키는 개념, bind() 메서드는 빌려서 가져오는 개념.
  • 할당된 변수를 함수호출식으로 실행하면 데이터 객체의 메서드처럼 실행된다.
  • bind() 메서드는 로드 시점과 실행 시점이 다르기 때문에 setTimeout(callback, duration)과 같이 실행 시차를 가지는 함수에서 this 키워드를 사용하더라도 데이터 객체를 그대로 참조할 수 있다.
const person = {
  firstName:"John",
  lastName: "Doe",
  display: function () {
    let x = document.getElementById("demo");
    x.innerHTML = this.firstName + " " + this.lastName;
  }
}

let display = person.display.bind(person);
setTimeout(display, 3000);

 

[구조 분해 할당 Destructuring assignment]

  • 구조 분해 할당이란? 함수에 객체나 배열을 전달해야 하는 경우, 객체나 배열에 저장된 데이터의 전체가 아닌 일부만 필요한 경우에 객체나 배열을 변수로 '분해’할 수 있게 해주는 특별한 문법이다.
// 이름과 성을 요소로 가진 배열
let arr = ["Eunsol", "An"]

// 구조 분해 할당을 이용해
// firstName엔 arr[0]을
// surname엔 arr[1]을 할당
let [firstName, surname] = arr;

alert(firstName); // Eunsol
alert(surname);  // An