함수 in JS

동동·2021년 6월 24일
1
post-thumbnail

함수란?

인자를 입력받아 어떠한 연산을 수행한 후 결과값을 반환하는 재사용가능한 로직의 모음이다.

함수는 호출할 수 있는 값이다.

JS에서 함수는?

일급 객체

일급 객체(영어: first-class object)란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다. 보통 함수에 매개변수로 넘기기, 함수의 리턴값으로 반환하기, 변수에 대입하기와 같은 연산을 지원할 때 일급 객체라고 한다. 따라서 JS에서 함수는 일급 객체이다.

https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function

함수는 Function의 instance이다

모든 함수는 객체이며 Function의 instance이다. 따라서 모든 함수는 Function.prototype의 메서드를 사용할 수 있습니다.

함수의 역할

1. 일반 함수

생성자 또는 메서드가 아닌 함수를 일반 함수라고 일반적으로 칭합니다.

일반 함수의 명명은 소문자로 시작하는 것이 관례입니다.

2. 생성자

생성자 명명은 대문자로 시작하는 것이 관례입니다.

생성자는 new 연산자를 사용하여 호출하며, 생성자의 prototype을 프로토타입으로 하는 객체를 생성하는 역할을 수행합니다.

3. 메서드

객체의 프로퍼티의 값이 함수인 프로퍼티를 메서드라고 합니다.

ES6에서는 메서드가 속한 객체를 내부 프로퍼티[[HomeObject]]로 가진 함수가 메서드라고 공식적으로 정의하고 있습니다.

foo는 메서드이지만 bar는 메서드가 아닙니다. bar는 단지 값이 함수인 데이터 프로퍼티입니다.

const obj = {
  foo() {
    return "foo";
  },
  bar: function() {
    return "bar";
  }
}

※ 내장 프로토타입에 메서드를 추가할 일이 없어야 겠지만...
만약 내장 프로토타입에 메서드를 추가한다면 아래와 같이 프로퍼티를 정의하여야 합니다. 값을 할당하거나 value에 일반 함수로 정의한다면, 해당 함수에 [[Constructor]] 메서드가 존재하기 때문에 생성자로 사용될 수 있습니다.


Object.defineProperty(
  Object.prototype,
  "pio",  // 메서드 명
  { 
    value() {  // ⛔ value: function() {
      /* do what you want */
    },
    writable: true,
    enumerable: false,
    configurable: true
  }
);

메서드 이름은 소문자로 시작하는 것이 관례입니다.

메서드는 this를 통해서 자신이 속한 객체를 참조합니다.

만약 메서드를 객체에서 추출하면, 객체와의 참조가 사라집니다. 따라서 추출된 메서드는 함수일 뿐 메서드가 아니며,

this는 strict mode에서는 undefined로, sloppy mode에서는 전역객체로 바뀝니다.

이러한 문제를 해결하기 위해서는 bind를 사용하여 this값을 명시적으로 가지는 함수를 생성하여 추출하여야 합니다.

메서드와 화살표 함수는 생성자로 사용할 수 없습니다

함수의 유형

1. 일반 함수

  • this가 묵시적 매개변수로 전달됩니다. sloppy mode의 경우, this로서 전역객체가 전달됩니다. strict mode의 경우, this는 undefined가 들어 있습니다.

2. 화살표 함수

  • 실행하는 시점에 결정되는 JS의 this binding 특성으로 인해 일반 함수내에서 this를 다루기가 굉장히 까다롭고, 이는 JS의 큰 단점으로 지적받았다.
  • 이에 대한 해법으로 ES6에 추가된 것이 화살표 함수이다. 화살표 함수는 자체적인 this를 가지지 않아 함수를 선언하는 시점(lexical 환경에서)의 가장 가까운 this가 binding된다. (자체 arguments 객체도 가지지 않고, lexical 환경의 가장 가까운 arguments를 가르킨다.)
  • 자체 this를 가지지 않기 때문에 생성자 함수로 사용할 수 없다. (new 연산자 사용 불가!)
  • 일반 함수는 function 키워드를 항상 작성하여야 하는 반면 화살표 함수는 (parameter) => { /* block */ } 의 형태로 간결하게 작성할 수 있다. (parameter) => (/* value */)와 같이 작성하면 (/* value */)를 바로 반환하는 것으로 간주된다.
  • this, super, arguments, [new.target](http://new.target) 바인딩: 화살표 함수를 최근접에서 둘러싸고 있는 일반함수에 의해 정의됩니다.
    • 화살표 함수는 this 바인딩을 하지 않으므로, 화살표 함수 내 this 값은 스코프 체인을 통해서만 결정됩니다. 만약 화살표 함수가 일반 함수 안에 포함되는 형태로 있으면 this 값은 화살표 함수를 감싸는 함수에서의 값과 같을 것이고, 그렇지 않으면 this는 전역 스코프의 this값과 같습니다.
  • this를 변경할 수 없음: 함수 내부의 this가 변경되지 않습니다. 즉, 선언 당시의 this 값으로 생명주기 내내 유지됩니다.
    • call, apply, bind를 사용하여 this 값을 변경할 수 없습니다.
  • 생성자로 사용할 수 없음: [[Construct]] 메서드가 존재하지 않으므로 생성자로 사용할 수 없습니다. 또한 prototype 프로퍼티가 존재하지 않습니다.
  • 암시적 반환: 본문에 표현식만 있을 경우, return을 기재하지 않아도 표현식을 평가한 값이 반환됩니다.

함수를 정의하는 방법

1. 함수 선언문

함수 선언은 새 변수를 선언하고 함수 객체를 만들어 그 변수에 할당하는 것입니다.

function foo() {
  
  return 3;
}

함수 선언문은 완벽하게 호이스팅됩니다. 선언과 할당이 모두 스코프 최상단으로 끌어올려집니다.

2. 함수 표현식

함수 표현식은 값, 즉 함수 객체를 생성합니다.

// ES 5
var foo = function (a) {
  
  return a + 3;
};

// ES 6+
const bar = (a) => a + 3;

함수 표현식은 부분적으로 호이스팅됩니다. 즉, 선언은 모두 스코프 최상단으로 끌어올려지지만, 할당은 끌어올려지지 않습니다. 따라서 함수 표현식으로 선언한 함수를 선언 이전에 접근하면 undefined가 반환되고, 이를 실행하면 에러가 발생합니다. undefined는 function이 아니므로 실행할 수 없기 때문입니다.

3. new Function

함수는 Function의 instance이기 때문에 Function 생성자를 이용하여 생성할 수 있습니다.

var foo = new Function("a", "b", "return a + b");

다만, 실제로 Function 생성자를 이용하여 함수를 생성하는 경우는 거의 없습니다.

함수를 호출하는 방법

함수를 호출하면 항상 this 가 묵시적 매개변수로 전달됩니다.

1. 일반 함수

함수의 이름을 쓰고 괄호 안에 매개변수를 기재한다.

sloppy mode 에서 this는 전역 객체, strict mode에서 this는 항상 undefined 입니다.

foo(1,2);

2. 생성자

생성자로서 사용하기 위해서는 new 연사자로 호출하여야 합니다. 생성자의 prototype 프로퍼티를 프로토타입으로 하는 객체가 생성되어 this로 전달됩니다.

new Foo();

3. 메서드

객체를 통해 호출한다. 메서드를 호출한 객체(자신이 속한 객체)가 this로 전달됩니다. 이 this값을 메서드에서는 메서드 호출의 수신자(receiver)라고 부릅니다.

obj.method();

reference

profile
작은 실패, 빠른 피드백, 다시 시도

0개의 댓글