자바스크립트 화살표 함수

|

화살표 함수는 단순히 함수를 ‘짧게’ 쓰기 위한 용도로 사용되지 않는다. 자바스크립트를 사용하다 보면 저 멀리 동떨어진 곳에서 실행될 작은 함수를 작성해야 하는 상황을 자주 만나게 된다.

  • arr.forEach(func) : funcforEach가 호출될 때 배열 arr의 요소 전체를 대상으로 실행된다.
  • setTimeout(func): func는 내장 스케줄러에 의해 실행된다.

이처럼 자바스크립트에선 함수를 생성하고 그 함수를 어딘가에 전달하는 것이 아주 자연스럽다. (함수는 일급 객체)

그런데 어딘가에 함수를 전달하게 되면 함수의 컨텍스트를 잃을 수 있다. 이럴 때 화살표 함수를 사용하면 현재 컨텍스트를 잃지 않아 편리하다.

화살표 함수에는 ‘this’가 없다

this 값은 런타임에 결정된다. 즉, 호출되는 컨텍스트에 따라 달라진다. 동일한 함수라도 다른 객체에서 호출했다면 this가 참조하는 값이 달라진다.

let user = { name: "John"};
let admin = { name: "Admin" };

function sayHi() {
  alert( this.name );
}

// 별개의 객체에서 동일한 함수를 사용함
user.f = sayHi;
admin.f = sayHi;

// 'this'는 '점(.) 앞의' 객체를 참조하기 때문에
// this 값이 달라짐
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)

admin['f'](); // Admin (점과 대괄호는 동일하게 동작함)

규칙은 간단하다. obj.f()를 호출했다면 thisf를 호출하는 동안의 obj이다. 위 예시에서는 objuseradmin을 참조한다.

객체 없이 호출하기

function sayHi() {
  alert(this);
}

sayHi(); // undefined

위와 같은 코드를 엄격 모드에서 실행하면, this에는 undefined가 할당된다. 따라서 this.name으로 name에 접근하려고 하면 에러가 발생한다.

그런데 엄격 모드가 아닐 때는 this가 전역 객체를 참조 한다. 브라우저 환경에서는 window라는 전역 객체를 참조한다.

이런 식의 코드는 대개 실수로 작성된 경우가 많다. 함수 본문에 this가 사용되었다면, 객체 컨텍스트 내에서 함수를 호출할 것이라고 예상하면 된다.

자유로운 this가 만드는 결과

자바스크립트에서 this는 런타임에 결정된다. 메서드가 어디서 정의되었는지에 상관없이 this는 ‘점 앞의’ 객체가 무엇인가에 따라 ‘자유롭게’ 결정된다.

이렇게 this가 런타임에 결정되면 좋은 점도 있고 나쁜 점도 있다. 함수(메서드)를 하나만 만들어 여러 객체에서 재사용할 수 있다는 것은 장점이지만, 이런 유연함이 실수로 이어질 수 있다는 것은 단점이다.

개발자는 this의 동작 방식을 충분히 이해하고 장점을 취하면서 실수를 피하는 데에 집중해야 한다.

객체 리터럴에서 ‘this’ 사용하기

function makeUser() {
  return {
    name: "John",
    ref: this
  };
};

let user = makeUser();

alert( user.ref.name ); // TypeError: Cannot read property 'name' of undefined
  • this를 설정할 때 객체 정의가 사용되지 않기 때문에 에러가 발생한다. this 값은 호출 시점에 결정된다.
  • 위 코드에서 makeUser()this는 메서드로서 호출된 게 아니라 함수로써 호출되었기 때문에 undefined이다.
  • this 값은 전체 함수이다.
function makeUser() {
  return {
    name: "John",
    ref() {
      return this;
    }
  };
};

let user = makeUser();
alert( user.ref().name ); // John
  • 위처럼 코드를 수정하면 user.ref()가 메서드가 되고 this. 앞의 객체가 되기 때문에 에러가 발생하지 않는다.

계산기 만들기

let calculator = {
  sum() {
    return this.a + this.b;
  },
  mul() {
    return this.a * this.b
  },
  read() {
    this.a = +prompt("a:", 0);
    this.b = +prompt("b:", 0);
  },
}

calculator.read();
alert( calculator.sum() );
alert( calculator.mul() );

체이닝

let ladder = {
  step: 0,
  up() {
    this.step++;
    return this;
  },
  down() {
    this.step--;
    return this;
  },
  showStep: function() { // 사다리에서 몇 번째 단에 올라와 있는지 보여줌
    alert( this.step );
    return this;
  }
};

ladder.up().up().down().up().down().showStep(); // 1

각 메서드에서 this를 반환하도록 하면 체이닝 가능

this가 없는 화살표 함수

화살표 함수는 일반 함수와는 달리 ‘고유한’ this를 가지지 않는다. 화살표 함수에서 this를 참조하면, 화살표 함수가 아닌 ‘평범한’ 외부 함수에서 this 값을 가져온다.

let user = {
  firstName: "보라",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};

user.sayHi(); // 보라

위 예시에서 함수 arrow()this는 외부 함수 user.sayHi()this가 된다.

별개의 this가 만들어지는 건 원하지 않고, 외부 컨텍스트에 있는 this를 이용하고 싶은 경우 화살표 함수가 유용하다.

이런 화살표 함수으 ㅣ특징은 객체의 메서드(showList()) 안에서 동일 객체의 프로퍼티(students)를 대상으로 순회를 하는 데 사용할 수 있다.

let group = {
  title: "1모둠",
  students: ["보라", "호진", "지민"],

  showList() {
    this.students.forEach(
      student => alert(this.title + ': ' + student)
    );
  }
};

group.showList();

위의 forEach에서 화살표 함수를 사용했기 때문에 화살표 함수 본문에 있는 this.title은 화살표 함수 바깥에 있는 메서드인 showList가 가리키는 대상과 동일하다. 즉 this.titlegroup.title과 같다.

위 예시에서 화살표 함수 대신 ‘일반’ 함수를 사용하면 다음과 같은 에러가 발생한다.

'use strict';

let group = {
  title: "1모둠",
  students: ["보라", "호진", "지민"],

  showList() {
    this.students.forEach(function(student) {
      // TypeError: Cannot read property 'title' of undefined
      alert(this.title + ': ' + student)
    });
  }
};

group.showList();

에러는 forEach에 전달되는 함수의 thisundefined이기 때문에 발생한다. alert 함수에서 undefined.title에 접근하려 했기 때문에 alert 창에 에러가 출력된다.

그런데 화살표 함수는 this 자체가 없기 때문에 이런 에러가 발생하지 않는다.

화살표 함수는 new와 함께 실행할 수 없다.

this가 없기 때문 화살표 함수는 생성자 함수로 사용할 수 없다. 화살표 함수는 new와 함께 호출할 수 없다.

화살표 함수에는 ‘arguments’가 없다

화살표 함수는 일반 함수와는 다르게 모든 인수에 접근할 수 있게 해주는 유사 배열 객체 arguments를 지원하지 않는다.

이런 특징은 현재 this 값과 arguments 정보를 함께 실어 호출을 포워딩해 주는 데코레이터를 만들 때 유용하게 사용된다.

아래 예시에서 데코레이터 defer(f, ms)는 함수를 인자로 받고 이 함수를 래퍼로 감싸 반환하는데, 함수 fms 밀리초 후에 호출된다.

function defer(f, ms) {
  return function() {
    setTimeout(() => f.apply(this, arguments), ms)
  };
}

function sayHi(who) {
  alert('안녕, ' + who);
}

let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("철수"); // 2초 후 "안녕, 철수" 출력

화살표 함수를 사용하지 않고 동일한 기능을 하는 데코레이터 함수를 만들면 다음과 같다.

function defer(f, ms) {
  return function(...args) {
    let ctx = this;
    setTimeout(function() {
      return f.apply(ctx, args);
    }, ms);
  };
}

일반 함수에선 setTimeout에 넘겨주는 콜백 함수에서 사용할 변수 ctxargs를 반드시 만들어줘야 한다.

참고

-화살표 함수 다시 살펴보기 -메서드와 ‘this’

200910TIL

|
  • SAP - 폼 형태 메서드에서는 메서드 목록에서 수정하고자 하는 메서드를 선택하고 매개변수, 예외사항은 위의 버튼을 눌러 입력함
  • SAP에서 테이블 FK를 수정할 때 오류발생할 경우 참조 function을 확인해볼 것
  • SAP 유지보수 뷰 만들기
  • 배민 리액트 강의 - generator, promise
  • 평가와 할당

리액트 공부 도중 질문들...(미해결)

|

다시 리액트를 복습하다가 그때 적어뒀던 질문들과 추가된 질문들을 기록용으로 남겨둔다.

  • create-react-app 했을 때 무슨 task들이 일어나는가?
  • 브라우저에서 없는 기능들(예:import React from ‘react')을 브라우저에서도 사용하기 위해 번들러(bundler)를 사용한다고 한다. 대표적인 번들로러 웹팩, Parcel, browserify라는 도구들이 있다고 한다. 각 도구들의 특성은 어떻게 될까?
  • 웹팩의 편의성과 확장성이 다른 도구보다 뛰어나다고 하는데, 어떤 면에서 그런가?
  • 브라우저에서의 import 구문과 프로젝트 번들링과 정확히 어떤 점이 다른가?
  • JSX를 babel이 브라우저에서 실행 가능한 코드로 변환할 때, 어떤 일이 일어나는가? (어떤 일이 일어나길래 우리는 주석도 다른 식으로 작성해야 하나?)
  • 조화 과정에서 빠른 비교를 위해 component들을 하나의 객체 (예: )로 묶는다고 하는데, 그렇게 되면 이점이 뭔지? 트리 구조로 만들어서 비교하기 위함인가?