instance method는 어디에 위치할까? (Java vs JavaScript)

Gwon HongGwon Hong
3 min read

글의 목적

  1. 나의 오해 풀기

    특정 클래스의 객체를 생성할 때 (Java의 static, JS의 prototype등을 제외한) 메서드들은 각 객체에 매번 복제될 것이라고 생각했다. 이것이 아니라는 것을 알아 이 사실을 좀 더 정확히 정리해보고자 한다.

  2. Java와 JS의 객체 간단 비교

    내가 얕게 알고 있는 두 언어의 객체를 비교함으로써 객체라는 것에 대해 좀 더 깊게 탐구해보고자 한다.

Java의 instance method

MyClass obj;
obj = new MyClass();

new MyClass()를 통해 새 객체를 힙 메모리에 할당해주고(간혹 스택에 직접 객체를 저장하는 경우도 존재한다고 한다. 이를 Escape Analysis라고 한다.), 그 객체를 가리키는 포인터를 스택 메모리에 존재하는 변수인 obj에 저장한다. 여기까지가 일반적으로 알고 있는 부분이다.

하지만, 이 때 메모리에 할당된 객체에 모든 메서드들이 다 저장되어있을까?

class MyClass {
    public void myMethod1() {}
    public void myMethod2() {}
    public void myMethod3() {}
    ...
    public void myMethod100() {}
}

이렇게 100개의 메서드가 선언되어있다면, 이것이 매 객체 생성 시마다 전부 copy 되어야 하는 것일까? 당연히 아니고, 객체에는 이를 가리키는 reference만 존재한다.

여기서 의문은, 이건 마치 static 메서드에서만 가능할 법한 것인데 어떻게 이것이 가능한 것일까? 메서드 중 객체의 변수에 접근하는 것은 어떻게 작동하는 것일까?

https://www.geeksforgeeks.org/static-methods-vs-instance-methods-java/

Instance methods are not stored on a per-instance basis, even with virtual methods. They’re stored in a single memory location, and they only “know” which object they belong to because this pointer is passed when you call them.

그렇다. obj.myMethod() 와 같은 식으로 instance method를 호출할 때, 이 obj를 가리키는 포인터도 함께 전달되어 이게 가능한 것이다. 참 간단하고 똑똑하다! 대충 obj.myMethod(this) 와 같은 식으로 호출이 된다고 봐도 될 것 같다.

JS의 instance method

ES6 이전

JS는 ES6에 처음으로 class 문법이 소개되었다. 그 전까지는 생성자 함수 같은 것을 통해 class와 비슷하게 구현을 해왔었다. 간단히 이를 되짚어보면:

function Person(name, age, id) {
    var privateId = id;

    this.name = name;
    this.age = age;
    this.getPrivateId = function() {
        return privateId;
    }
}

var gwon = new Person("gwon", 100, 1234);
console.log(gwon.name); // "gwon"
console.log(gwon.getPrivateId()); // 1234

이런 식으로 field와 method들을 정의했다. 이런 옛날 방식의 경우, 각 객체가 method의 정보를 포함하게 된다. 내가 이렇게 배웠어서 Java의 객체 생성 시에도 이렇게 작동할 것이라고 오해를 했던 것이다.

물론 JS에도 객체마다 method를 포함하는 것이 비효율적이라는 것을 알기에 prototype이라는 것이 존재한다. 이는 마치 Java의 static method와 같이 class 객체 자체에 존재하는 method들이라고 보면 된다. 하지만 이것으로 instance method를 다음과 같이 정의해볼 수 있다:

function Person(name, age, id) {
    var privateId = id;

    this.name = name;
    this.age = age;
    this.getPrivateId = function() {
        return privateId;
    }
}
Person.prototype.introduce = function() { 
    console.log("hello, I'm " + this.name);
};

var gwon = new Person("gwon", 100, 1234);
console.log(gwon.name); // "gwon"
gwon.introduce(); // "hello, I'm gwon"

그렇다. prototype method를 호출할 때 객체 자신 또한 (내부적으로) 매개변수로 넘겨 이에 접근이 가능한 것이다. 이를 통해 Java와 같이 instance method를 구현할 수 있다.

ES6

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes#instance_methods

class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
  }
  getRed() {
    return this.values[0];
  }
}

const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255

if you use a method, it will be shared between all instances. A function can be shared between all instances, but still have its behavior differ when different instances call it, because the value of this is different. If you are curious where this method is stored in — it's defined on the prototype of all instances, or Color.prototype

class에 정의한 method는 앞서 ES6 이전의 예시 중 후자처럼 prototype에 저장된다. 물론,

class Color {
  constructor(r, g, b) {
    this.values = [r, g, b];
    this.getRed = function () {
      return this.values[0];
    };
  }
}

이따구로이렇게 정의하면 여전히 객체마다 함수가 생성되는 기적을 볼 수 있다!

결론

Java는 일반적으로? 상식적으로? 동작하는 반면 JavaScript가 꽤나 이상하게 동작한다고 생각한다.

  1. Java의 instance method에 대한 (객체마다 생성될 것이라는)오해를 풀 수 있었고

  2. prototype은 JavaScript라는 언어를 이해하기 위해 잘 알아야하는 개념이라는 알 수 있었다.

0
Subscribe to my newsletter

Read articles from Gwon Hong directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Gwon Hong
Gwon Hong