반응형
Jake Seo
제이크서 위키 블로그
Jake Seo
전체 방문자
오늘
어제
  • 분류 전체보기 (715)
    • 일상, 일기 (0)
    • 백준 문제풀이 (1)
    • 릿코드 문제풀이 (2)
    • 알고리즘 이론 (10)
      • 기본 이론 (2)
      • 배열과 문자열 (8)
    • 데이터베이스 (15)
      • Planet Scale (1)
      • MSSQL (9)
      • 디비 기본 개념 (1)
      • SQLite 직접 만들어보기 (4)
    • 보안 (7)
    • 설계 (1)
    • 네트워크 (17)
      • HTTP (9)
      • OSI Layers (5)
    • 회고 (31)
      • 연간 회고 (2)
      • 주간 회고 (29)
    • 인프라 (52)
      • 도커 (12)
      • AWS (9)
      • 용어 (21)
      • 웹 성능 (1)
      • 대규모 서비스를 지탱하는 기술 (9)
    • 깃 (7)
    • 빌드 도구 (7)
      • 메이븐 (6)
      • 그레이들 (0)
    • Java (135)
      • 이펙티브 자바 (73)
      • 자바 API (4)
      • 자바 잡지식 (30)
      • 자바 디자인 패턴 (21)
      • 톰캣 (Tomcat) (7)
    • 프레임워크 (64)
      • next.js (14)
      • 스프링 프레임워크 (28)
      • 토비의 스프링 (6)
      • 스프링 부트 (3)
      • JPA (Java Persistence API) (5)
      • Nest.js (8)
    • 프론트엔드 (48)
      • 다크모드 (1)
      • 노드 패키지 관리 매니저 (3)
      • CSS (19)
      • Web API (11)
      • tailwind-css (1)
      • React (5)
      • React 새 공식문서 요약 (1)
      • HTML (Markup Language) (5)
    • 자바스크립트 (108)
      • 모던 자바스크립트 (31)
      • 개념 (31)
      • 정규표현식 (5)
      • 코드 스니펫 (1)
      • 라이브러리 (6)
      • 인터뷰 (24)
      • 웹개발자를 위한 자바스크립트의 모든 것 (6)
      • 팁 (2)
    • Typescript (49)
    • 리눅스와 유닉스 (10)
    • Computer Science (1)
      • Compiler (1)
    • IDE (3)
      • VSCODE (1)
      • IntelliJ (2)
    • 세미나 & 컨퍼런스 (1)
    • 용어 (개발용어) (16)
      • 함수형 프로그래밍 용어들 (1)
    • ORM (2)
      • Prisma (2)
    • NODEJS (2)
    • cypress (1)
    • 리액트 네이티브 (React Native) (31)
    • 러스트 (Rust) (15)
    • 코틀린 (Kotlin) (4)
      • 자바에서 코틀린으로 (4)
    • 정규표현식 (3)
    • 구글 애널리틱스 (GA) (1)
    • SEO (2)
    • UML (2)
    • 맛탐험 (2)
    • 리팩토링 (1)
    • 서평 (2)
    • 소프트웨어 공학 (18)
      • 테스팅 (16)
      • 개발 프로세스 (1)
    • 교육학 (1)
    • 삶의 지혜, 통찰 (1)
    • Chat GPT (2)
    • 쉘스크립트 (1)
    • 컴파일 (2)
    • Dart (12)
    • 코드팩토리의 플러터 프로그래밍 (4)
    • 플러터 (17)
    • 안드로이드 스튜디오 (1)
    • 윈도우즈 (1)
    • 잡다한 백엔드 지식 (1)
    • 디자인 패턴 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 디자인패턴
  • prerendering
  • 느린 쿼리
  • 서버리스 컴퓨팅
  • 프로그래머의 뇌
  • 메이븐 골
  • 러스트
  • 팩터리 메서드 패턴
  • 자바스크립트
  • Javadoc 자바독 자바주석 주석 Comment
  • 객체복사
  • 이펙티브 자바 item9
  • Java
  • 싱글턴
  • 메이븐 라이프사이클
  • 알고리즘
  • 자바스크립트 인터뷰
  • bean Validation
  • 자바스크립트 면접
  • 이펙티브 자바
  • Next.js
  • 참조 해제
  • try-with-resources
  • 플라이웨이트패턴
  • pnpm
  • 작업기억공간
  • 도커공식문서
  • NEXT JS
  • 자바 검증
  • 토비의 스프링
  • next js app
  • 빈 검증
  • 싱글톤
  • item7
  • 자료구조
  • 이펙티브자바
  • 스프링 검증
  • 싱글톤 패턴
  • 외래키 제약조건
  • item9
  • 메이븐 페이즈
  • rust
  • item8
  • 추상 팩터리 패턴
  • Pre-rendering
  • 자바 디자인패턴
  • 자바
  • 슬로우 쿼리
  • MSSQL
  • serverless computing

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jake Seo

제이크서 위키 블로그

자바스크립트/웹개발자를 위한 자바스크립트의 모든 것

웹 개발자를 위한 자바스크립트의 모든 것 3장 새로운 함수 기능

2022. 12. 22. 22:29

화살표 함수와 this, super, 그 외

화살표 함수

화살표 함수는 ES6 에서 새로 도입되었다.

쉼표 연산자 (Comma Operator) 를 사용하여 map 반환하기

MDN - Comma Operator

handlers = handlers.map((handler) => (unregister(handler), register(handler)));
  • 쉼표로 이어진 값을 평가하고 가장 마지막 값을 반환한다.
  • map 의 본문을 간략화하고 싶을 때 사용할 수 있다.
  • 이런식으로 사용하는 것을 추천한다기보다는 간혹 이런식으로 작성된 코드를 볼 수 있어서 알아두면 좋다.

화살표 함수의 this

this 를 갖지 않는다. 대신 바로 바깥 스코프의 this 를 가진다.

화살표 함수는 new 키워드로 생성할 수 없지만, 가볍다.

  • 화살표 함수는 일반 function 선언처럼 new 키워드로 오브젝트화 할 수 없다.
  • 그래서 일반 함수보다 가벼워질 수 있다.
    • Function 이 갖는 다양한 프로토타입을 가지지 않을 수 있다.
function normalFunction() {}
const arrowFunction = () => {};

normalFunction.prototype; // {constructor: ƒ}
arrowFunction.prototype; // undefined
  • 화살표 함수에는 .prototype 이 존재하지 않는다.
  • 그래서 constructor 가 없기 때문에 new 로 생성할 수도 없다.

기본 값

function animate(type, duration) {
  if (duration === undefined) {
    duration = 300;
  }
}
function animate(type, duration = 300) {}

위의 두 코드는 동일하다.

기본 값은 표현'식'이다.

function animate(type, duration = getDuration(type)) {}
  • 위와 같은 표현이 가능하다.
  • 단 type 이 뒤에 나오면 ReferenceError: type is not defined 를 던진다.
  • 외부 범위에 있는 것은 기본 값으로 참조 가능하다.
  • 함수 본문에 있는 것은 당연히 참조 불가능하다.
function e(
  callback = (o) => {
    with (o) {
      return answer;
    }
  }
) {
  console.log(callback({ answer: 42 }));
}
  • 기본 값에 함수를 넣는 것도 가능하다.

내부 동작

function animate(duration = 1000, type = "default") {}

위와 같은 기본 값이 있다면, 실제 동작은 아래와 매우 흡사하다.

function animate() {
  let duration = arguments[0];
  if (duration === undefined) {
    duration = 1000;
  }

  let type = arguments[1];
  if (type === undefined) {
    type = "default";
  }

  const animationImplementation = () => {
    // ...
  };
}
  • 파라미터와 함수 내용은 범위가 구분된다.

'단순하지 않은' 매개변수 목록

  • 파라미터에 기본 값이 있는 경우, 이를 '단순하지 않은' 매개변수 목록 이라고 한다.
    • 단순하지 않은 매개변수 목록이 있는 함수는 "use strict" 지시문을 가질 수 없다.
    • 단순하지 않은 매개변수 목록이 이미 느슨한 모드 코드로 정의되었는데, 지시문이 함수 내에서 엄격 모드를 활성화하기 때문에 에러가 난다.

"use strict" 를 사용할 수 없는 케이스 - MDN

function example(answer = 42) {
    "use strict" // Uncaught SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list
}

기본 값이 있는 파라미터를 포함하여 이후의 파라미터는 함수의 인자 수에 포함되지 않는다.

function fn1(one, two, three) {}
console.log(fn1.length); // 3
function fn2(one = 1, two, three) {}
console.log(fn2.length); // 0
  • 함수.length 를 찍으면 파라미터의 개수가 나온다.
  • 파라미터에 기본 값이 있으면, 그 파라미터를 포함하여 전부 파라미터의 개수에 포함되지 않는다.

나머지 매개변수

나머지 매개변수 이전의 코드

function extend(target) {
  for (let n = 1; n < arguments.length; n++) {
    const source = arguments[n];
    // source 에는 객체 형태만 와야 한다.
    Object.keys(source).forEach((key) => {
      target[key] = source[key];
    });
  }

  return target;
}

console.log(extend({}, { a: 1 }, { b: 2 })); // {a:1, b:2}
  • 느슨한 모드에서는 양식 매개변수에 연결되는 방식으로 성능 문제가 발생
    • 엄격 모드를 통해 해결 가능
  • 인수를 전부 이용해야 해서 확장이 불편하다.
  • arguments 는 배열 프로토타입을 물려받지 않아서, 배열 기능 이용이 불가능하다.
  • 화살표 함수에는 arguments 기능이 없다.

나머지 매개변수 이후의 코드

function extend(target, ...sources) {
  sources.forEach((source) => {
    Object.keys(source).forEach((key) => {
      target[key] = source[key];
    });
  });

  return target;
}

console.log(extend({}, { a: 1 }, { b: 2 })); // {a:1, b:2}
  • target 뒤에 오는 모든 매개변수를 나머지 매개변수 로 묶었다.
  • 자연적으로 Array.prototype 을 상속하게 되어 Array.prototype 의 메서드를 사용할 수 있게 된다.
    • forEach 를 사용했다.
  • 이제 arguments 객체가 필요 없으니 extend 자체를 화살표 함수로 구성할 수 있다는 점도 주목할만 하다.

나머지 매개변수에 빈 값이 온다면?

function rest(...args) {
  console.log(args);
}

console.log(rest(1, 2, 3)); // [1, 2, 3]
console.log(rest()); // []
  • 빈 값이 오면, 빈 배열([])을 제공하여 일관성 있는 반환값을 제공한다.

후행 쉼표와 함수 호출

function additionalComma(
  a,
  b,
  c,
) {
  console.log(a, b, c);
}

additionalComma(
  "a",
  "b",
  "c",
); // a b c
  • 이젠 함수 파라미터나, 함수 인자에 추가적인 쉼표가 들어가도 에러가 아니다.
  • 매개변수나 인수를 엔터로 구분할 때 유용하다.

함수의 name 속성

  • name 속성을 통해 함수의 이름을 알 수 있다.
  • 이름을 알게 되면, 오류에 대한 호출 스택에서 오류가 난 곳을 정확히 파악하는데 도움이 된다.
function foo() {}
console.log(foo.name); // foo

const f = function foo() {};
console.log(f.name); // foo

let foo = function () {};
console.log(foo.name); // foo

// 나중에 함수를 할당
let bar;
bar = function () {};
console.log(bar.name); // bar

// 화살표 함수
let b = () => {};
console.log(b.name); // b

// 오브젝트 속성 함수
const obj = {
  foo: function () {},
};
console.log(obj.foo.name); // foo

// 함수를 기본 매개변수 값으로 사용
(function (callback = function () {}) {
  console.log(callback.name); // "callback"
})();

name 이 없는 특수한 경우

const obj = {};
obj.foo = function () {};
console.log(obj.foo.name); // "" - 이름이 없음
  • 기존 객체의 객체 속성에 할당하면, 이름이 없다.
  • TC39 는 이를 너무 많은 정보 유출로 간주했다. (?)
cache[getUserSecret(user)] = function () {};
  • 핸들러 함수를 타사 코드에 제공했을 때 getUserSecret(user) 의 값이 제공될 수 있어서 일부러 생략했다.

이유에 대해서는 사실 잘 이해가 안 된다.

블록 내 함수 선언

  • 블록 내 함수 선언은 ES2015 부터 공식적으로 사양의 일부가 되었다.
  • 이전까지는 사용은 가능했으나, 공식 사양은 아니었다.

블록 내 함수 선언의 문제

간단한 함수 선언은 문제 없었으나, 다음을 어떻게 처리할지에 대해서 여러 논의가 있었다.

function branching(num) {
  console.log(num);

  if (num < 0.5) {
    function doSomething() {
      console.log("true");
    }
  } else {
    function doSomething() {
      console.log("false");
    }
  }

  doSomething();
}

위의 코드를 처리하기 위해 아래와 같은 방법들이 나왔다.

function branching(num) {
  console.log(num);
  var doSomething;
  if (num < 0.5) {
    doSomething = function doSomething() {
      console.log("true");
    };
  } else {
    doSomething = function doSomething() {
      console.log("false");
    };
  }

  doSomething();
}
  • 실제로 함수 표현식인 것 처럼 만든다.
function branching(num) {
  function doSomething() {
    console.log("true");
  }
  function doSomething() {
    console.log("false");
  }
  console.log(num);
  var doSomething;
  if (num < 0.5) {
  } else {
  }

  doSomething();
}
  • 호이스트 선언처럼 처리한다.
  • 후에 선언된 함수가 이겨 항상 "false" 만이 출력된다.

결국 위의 방식으로 코딩하면, 브라우저마다 동작이 다르다. 이 외에도 가장 확실하게 구문 오류로 처리하는 옵션도 있었다.

블록 내 함수 선언: 표준 의미론

가장 간단한 처리는 표준 시맨틱으로, 항상 엄격 모드로 적용되는 것이다.

"use strict";
function branching(num) {
  console.log(num);

  if (num < 0.5) {
    console.log("true branch, typeof doSomething = " + typeof doSomething);
    function doSomething() {
      console.log("true");
    }
  } else {
    console.log("false branch, typeof doSomething = " + typeof doSomething);
    function doSomething() {
      console.log("false");
    }
  }

  doSomething();
}

branching(Math.random());

/*
true branch, typeof doSomething = function
Uncaught ReferenceError: doSomething is not defined
    at branching (<anonymous>:17:3)
    at <anonymous>:20:1
*/
  • 에러 메세지를 보면, 블록 내에서 function 인 것을 인지한 것을 보아 블록 내에서 호이스팅은 되었다.
  • 그러나 함수 내용 마지막에 doSomething() 호출이 ReferenceError 를 내며 실패한 것으로 보아, 외부에서 doSomething 함수를 참조할 수는 없는 것으로 보인다.

위의 코드는 엄격모드에서 아래와 같이 변환된다.

"use strict";
function branching(num) {
  console.log(num);

  if (num < 0.5) {
    console.log("true branch, typeof doSomething = " + typeof doSomething);
    let doSomething = function doSomething() {
      console.log("true");
    };
  } else {
    console.log("false branch, typeof doSomething = " + typeof doSomething);
    let doSomething = function doSomething() {
      console.log("false");
    };
  }

  doSomething();
}

branching(Math.random());

위의 코드는 당연히 에러가 난다. let 은 블록스코프인데, doSomething() 을 호출하는 곳은 스코프 밖이다.

아래와 같이 변환하면 실행이 가능하다.

"use strict";
function branching(num) {
  let f;
  console.log(num);

  if (num < 0.5) {
    console.log("true branch, typeof doSomething = " + typeof doSomething);
    f = doSomething;
    function doSomething() {
      console.log("true");
    }
  } else {
    console.log("false branch, typeof doSomething = " + typeof doSomething);
    f = doSomething;
    function doSomething() {
      console.log("false");
    }
  }

  f();
}

branching(Math.random());

블록의 함수 선언: 레거시 웹 의미 체계

function branching(num) {
  console.log(`num = ${num}, typeof doSomething = ${typeof doSomething}`);

  if (num < 0.5) {
    console.log("true branch, typeof doSomething = " + typeof doSomething);
    function doSomething() {
      console.log("true");
    }
    console.log("end of true block");
  } else {
    console.log("false branch, typeof doSomething = " + typeof doSomething);
    function doSomething() {
      console.log("false");
    }
    console.log("end of true block");
  }

  doSomething();
}

branching(Math.random());

이는 레거시 웹 의미 체계에서는 아래와 같이 번역된다.

  • 함수는 단일 블록 내에서만 선언되고 참조된다.
  • 함수는 단일 블록 내에서 선언되고 사용 가능하지만, 동일한 블록 내에 포함되지 않은 내부 함수 정의에 의해 참조된다.
  • 함수는 단일 블록 내에서 선언되고 사용 가능하지만, 후속 블록 내에서 참조된다.
function branching(num) {
  var varDoSomething;
  console.log(num);

  if (num < 0.5) {
    let letDoSomething = function doSomething() {
      console.log("true");
    };
    console.log("true branch, typeof doSomething = " + typeof doSomething);
    varDoSomething = letDoSomething; // 선언이 있던 곳
    console.log("end of true block");
  } else {
    let letDoSomething = function doSomething() {
      console.log("false");
    };
    console.log("false branch, typeof doSomething = " + typeof doSomething);
    varDoSomething = letDoSomething; // 선언이 있던 곳
    console.log("end of true block");
  }

  doSomething();
}

branching(Math.random());

레거시 의미 체계에서는 블록 내부에서 호이스팅과 같은 현상이 일어나고, 바깥 스코프에서도 사용 가능하도록 조정해준다.

이러한 레거시 웹 의미 체계에 의존하는 코드를 더이상 작성하지 말자.

액션 플랜

화살표 함수를 사용하는 경우

  • 콜백 함수에서 호출 컨텍스트 this 에 접근하고 싶을 때 사용하자.
  • Function.prototype.bind 대신 사용하자.
  • var self = this 와 같은 구시대의 유물이 있을 때 리팩토링하자.

this 혹은 인수를 사용하지 않을 때는 콜백에 화살표 함수를 사용하자.

someArray.sort((a, b) => a - b);

화살표 함수가 더 간결하다.

함수 호이스팅이 필요 없다면, 화살표 함수를 사용하자.

가끔은 전통적 function 을 이용하여 함수 호이스팅을 사용해야 할 때도 있다.

호출자가 이 값을 제어해야 할 때는 화살표 함수를 사용하지 마라.

때로는 콜백함수에서 지원하는 this 가 유용하게 쓰일 때도 있으니, function 도 두번째 선택지에 두자.

  • addEventListener 로 연결한 DOM 이벤트에 응답할 때
  • this 를 이용한 객체간 공유 메서드를 작성할 때

기본값을 제공하는 코드 대신 기본 매개변수를 사용하자.

function fn(param1) {
  if (param1 === undefined) {
    param1 = 5000;
  }
}
function fn(param1 = 5000) {}

코드가 간결하고 의도가 명확해진다.

인수 키워드 (arguments) 대신 나머지 매개변수를 사용하자.

function fn() {
  // arguments 를 이용한 코드
}
function fn(...args) {
  // args 를 이용한 코드
}

나머지 인수를 사용한다는 의도를 명확히 드러낼 수 있다.

후행 쉼표를 고려하자.

function additionalComma(
  a,
  b,
  c,
) {
  console.log(a, b, c);
}

additionalComma(
  "a",
  "b",
  "c",
); // a b c

때때로 가독성이 좋아진다.

반응형
저작자표시 비영리 (새창열림)

'자바스크립트 > 웹개발자를 위한 자바스크립트의 모든 것' 카테고리의 다른 글

웹 개발자를 위한 자바스크립트의 모든 것 6장 이터러블, 이터레이터, 제너레이터  (0) 2023.01.16
웹 개발자를 위한 자바스크립트의 모든 것 5장 새로운 객체 기능  (0) 2023.01.02
웹 개발자를 위한 자바스크립트의 모든 것 4장 클래스  (0) 2022.12.29
웹 개발자를 위한 자바스크립트의 모든 것 2장 let, const 정리  (0) 2022.12.18
웹 개발자를 위한 자바스크립트의 모든 것 1장 ES2015 - ES2020 정리  (0) 2022.12.17
    '자바스크립트/웹개발자를 위한 자바스크립트의 모든 것' 카테고리의 다른 글
    • 웹 개발자를 위한 자바스크립트의 모든 것 5장 새로운 객체 기능
    • 웹 개발자를 위한 자바스크립트의 모든 것 4장 클래스
    • 웹 개발자를 위한 자바스크립트의 모든 것 2장 let, const 정리
    • 웹 개발자를 위한 자바스크립트의 모든 것 1장 ES2015 - ES2020 정리
    Jake Seo
    Jake Seo
    ✔ 잘 보셨다면 광고 한번 클릭해주시면 큰 힘이 됩니다. ✔ 댓글로 틀린 부분을 지적해주시면 기분 나빠하지 않고 수정합니다. ✔ 많은 퇴고를 거친 글이 좋은 글이 된다고 생각합니다. ✔ 간결하고 명료하게 사람들을 이해 시키는 것을 목표로 합니다.

    티스토리툴바