Class method vs field function

Javascript와 같은 함수를 일급 객체(first class citizen)로 취급하는 언어는 함수를 값처럼 사용할 수 있기 때문에 온갖 곳에 사용할 수 있다.
그러면 class의 field에 함수를 사용하는건 어떨까??
아래 Typescript 코드를 보자

1
2
3
4
5
6
7
8
9
10
11
12
class Person {
constructor(
private name: string
) {}

// method
hello(): void {
console.log(`Hello. My name is ${this.name}!`);
}
}

new Person('Chris').hello(); // Hello. My name is Chris!
1
2
3
4
5
6
7
8
9
10
11
12
class Person {
constructor(
private name: string
) {}

// field function
hello = () => {
console.log(`Hello. My name is ${this.name}!`);
}
}

new Person('Chris').hello(); // Hello. My name is Chris!

둘 다 사용하는 방식이나 기능의 결과물은 같다.
개인적으로 Java를 계속 사용해왔기에 메서드를 사용하지 않고 필드에 함수를 넣는다는건 거부감이 들고,
메서드가 있는데 굳이 혼용하는 방식으로 필드에 함수를 할당해야하나 라는 생각이 든다.

일단 Javascript 내부적으로 보면서 둘의 차이점을 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
constructor(
private name: string
) {}

hello1() {
console.log(`Hello. My name is ${this.name}!`);
}

hello2 = () => {
console.log(`Hello. My name is ${this.name}!`);
}
}

위 Typescript 코드를 ES5로 compile 하면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"use strict";
var Person = /** @class */ (function () {
function Person(name) {
var _this = this;
this.name = name;
this.hello2 = function () { // `hello2` field에 함수 할당
console.log("Hello. My name is " + _this.name + "!");
};
}
Person.prototype.hello1 = function () { // `Person.prototype`에 hello1 설정
console.log("Hello. My name is " + this.name + "!");
};
return Person;
}());

컴파일된 Javascript 코드를 보면 알 수 있듯

  • hello1(method)은 prototype.hello1에 함수를 지정하고
  • hello2(field function)는 생성자에서 필드(this.hello2)에 함수를 할당한다

method의 경우 prototype에 함수를 지정하는 반면, field function의 경우 인스턴스가 생성될 때마다 함수를 새로 생성하여 필드에 할당하기 때문에 메모리나 속도면에서 method에 비해 뒤쳐질 수 밖에 없다.

또 field function V.S. method 에 대한 의견은 서버보다는 클라이언트 쪽에서 많은데,
Airbnb 같은 경우는 Airbnb React/JSX Style Guide에서 method 부분을 보면 알 수 있듯, field function을 very bad라 할 정도로 사용하는 것을 지양하고 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// very bad
class extends React.Component {
onClickDiv = () => {
// do stuff
}

render() {
return <div onClick={this.onClickDiv} />
}
}

// good
class extends React.Component {
constructor(props) {
super(props);

this.onClickDiv = this.onClickDiv.bind(this);
}

onClickDiv() {
// do stuff
}

render() {
return <div onClick={this.onClickDiv} />;
}
}

Why? A bind call in the render path creates a brand new function on every single render. Do not use arrow functions in class fields, because it makes them challenging to test and debug, and can negatively impact performance, and because conceptually, class fields are for data, not logic.

하지만 Component에 bind를 매번 해줘야하기 때문에 method를 사용하는걸 기피하는 개발자도 있다.
(다른 리액트 개발자에게 물어봤을 때도 자기는 method로 되있는 걸 보면 field function으로 변경한다고 하였다)

이 Airbnb style guide에서 참고 링크로 걸은 블로그 글에 달린 댓글들을 보면 성능도 성능이지만 utility도 무시할 수 없다는 의견도 많다.


가볍게 생각한 것인데 깊게 파보니 여러 의견이 있어서 신기하기도 했고 개발자로서 사소하더라도 궁금한건 한 번 파볼 가치가 있다는 걸 다시 느꼈다.
결론은 뭐든 그렇듯 Silver bullet은 없다 :)

Share