본문 바로가기
JavaScript

자바스크립트는 모든 것이 객체일까?

by Vintz 2023. 3. 30.
반응형

Photo by Samuel Branch - https://unsplash.com/photos/hGRRew460B0

왜인지는 모르겠지만 나는 꽤 오랫동안 자바스크립트의 값(value)들이 모두 객체(object)라고 생각을 했었다. 블로그든 강의든 어디선가 잘못 이해하고, 그 내용이 머릿속에 강하게 남았나보다.

 

많은 시간이 지나고, 이제서야 개념을 바로잡게 되었다. 결론부터 말하자면 자바스크립트에서 대부분의 것들은 객체이지만, 원시값(primitive)이있으며, 이들은 객체가 아니다.

 

다음으로 객체란 무엇인지, 왜 모든 값들이 객체라고 생각을 했는지, 그리고 그 값은 왜 객체가 아닌지에 대해서 자세히 알아보자.

객체 알아보기

객체는 관련된 데이터와 함수(프로퍼티와 메서드)를 가지는 복합 자료 구조이다. 자바스크립트의 객체는 실제로 동적 구조와 프로토타입 기반의 상속 덕분에 매우 유연하고 강력한데, 이는 언어의 중요한 특징 중 하나이다.

const person = {
  name: 'Vintz',
  age: 27,
};

// 객체에 새로운 프로퍼티를 추가
person.job = 'Developer';

// 객체의 프로퍼티를 삭제
delete person.age;

이처럼 동적으로 프로퍼티(메서드도 동일)를 추가하거나 제거할 수 있다. 이는 객체를 생성한 후에도 프로퍼티와 메서드를 변경할 수 있음을 의미하며, 이를 통해 객체를 유연하게 사용할 수 있다.

const animal = {
  speak() {
    console.log(`${this.name}가 소리를 낸다.`);
  },
};

const dog = Object.create(animal);
dog.name = '초코';
dog.speak(); // '초코가 소리를 낸다.'

프로토타입 기반의 객체 지향 언어인 자바스크립트는 객체가 다른 객체로부터 직접 상속을 받을 수 있다. 객체 간의 상속 관계를 구현하는 데 사용되는 메커니즘인 프로토타입 체인을 통해 코드를 재사용하고, 객체 간의 관계(상속, 연관, 의존, 집합 등)를 구현할 수 있다.(java와 python 같은 클래스 기반 객체지향 언어는 객체 간의 상속이 아닌 클래스 간의 상속이 이루어 진다.)

그럼 이것도 객체 아니야?

위에서 객체는 프로퍼티와 메서드를 가지는 복합 데이터 구조라고 설명했다. 그런데 다음과 같은 코드를 보면 이상한 점이 있다.

const str = 'hello';
console.log(str.length); // 5
console.log(str.toUpperCase()); // 'HELLO'

이게 어떻게 가능한 것일까? 문자열이 프로퍼티와 메서드에 접근했으니, 결국 문자열도 객체일까?

일시적 객체 취급

자바스크립트에서는 문자열과 같은 원시값을 객체처럼 사용할 때, 일시적인 래퍼 객체(wrapper object)가 생성되어 해당 메서드나 프로퍼티에 접근할 수 있게 해준다. 이러한 일시적 래퍼 객체 생성은 자바스크립트 엔진에서 내부적으로 처리되기 때문에, 직접 관리할 필요가 없으며 원시값을 좀 더 쉽게 다룰 수 있게 해준다.(작업이 완료된 후, '일시적'으로 생성된 래퍼 객체는 메모리에서 해제된다.)

const str = 'Hello, world!';
const length = str.length; // 일시적인 String 래퍼 객체가 생성되어 length 프로퍼티에 접근할 수 있다.

const num = 42;
const strNum = num.toString(); // 일시적인 Number 래퍼 객체가 생성되어 toString 메서드에 접근할 수 있다.

이렇게 원시값을 일시적인 래퍼 객체로 자동 변환해 주는 것을 오토박싱(autoboxing)이라고 한다. 이러한 자바스크립트의 내부 동작으로 인해 원시값을 객체처럼 사용할 수 있는 것이다.

자바스크립트에서의 값

위의 설명에서 계속해서 나온 '원시값'은 무엇일까? 자바스크립트의 값은 원시 데이터 타입(primitive data types)과 객체, 이 둘로 나눌 수 있다. 원시 데이터 타입에는 다음과 같은 것들이 포함된다:

  1. 문자열(string)
  2. 숫자(number)
  3. 불리언(boolean)
  4. null
  5. undefined
  6. symbol(ES6부터 도입된 고유한 식별자)

원시값들은 그 자체로 데이터를 나타내어, 객체와 달리 프로퍼티와 메서드를 가지고 있지 않다. 하지만 이제는 오토박싱을 통해 일시적으로 객체처럼 취급하여 프로퍼티와 메서드에 접근할 수 있게 해준다는 것을 알고 있다.

typeof null

null은 자바스크립트에서 비어 있는 값, 즉 아무 값도 없음을 나타내는 특별한 원시 데이터 타입이다. null은 객체가 아니지만 개발자 도구 콘솔에서 typeof null을 입력하면 'object'가 나오는 것을 알 수 있다. 이것은 설계상의 오류로 인한 것이다. 이 오류는 초기 자바스크립트 구현에서 발생했으며, 이후 버전에서도 하위 호환성을 유지하기 위해 수정되지 않고 있다.

null의 타입이 'object'라고 나오는 것은 설계상의 오류이다.

따라서 null 값을 검사할 때는 typeof를 사용하는 대신, value === null과 같은 명시적인 방식으로 비교하자.

함수도 객체, 배열도 객체. 거의 모든 것이 객체

이제는 원시값을 제외한 모든 것이 객체라는 것을 알게 되었다. 이는 함수, 배열, 정규 표현식, 에러(Error) 객체 등이 포함된다.

함수도 객체

자바스크립트에서 함수가 객체인 이유는 언어의 설계 철학과 프로토타입 기반의 객체 지향 언어라는 점에서 기인한다. 함수는 Function이라는 내장 생성자를 기반으로 생성되며, 이 생성자는 객체로 구현되어 있기 때문에 다른 객체처럼 프로퍼티와 메서드를 가질 수 있다. 따라서 모든 자바스크립트 함수는 Function 객체이다.

모든 자바스크립트 함수는 사실 Function 객체이다.

결국 이러한 함수를 일급 객체(first-class citizens)로 취급하여, 함수를 다른 값들과 마찬가지로 변수에 할당하거나, 인수로 전달하거나, 그리고 다른 함수에서 반환할 수 있게 된다.(함수를 다른 변수와 동일하게 다루는 것을 "일급 함수를 가졌다"고 표현하기도 한다.)

 

이렇게 함수가 객체라는 점은 자바스크립트에서 다양한 프로그래밍 패턴과 기법을 가능하게 한다. 예를 들어, 고차 함수(higher-order function)를 통해 추상화와 코드 재사용성을 높일 수 있다.

함수가 일급 객체로 취급되지 않더라도 다양한 문제를 해결할 수 있지만, 코드 작성에 있어 일부 제약이 따른다. 최신 프로그래밍 언어들은 함수형 프로그래밍 패러다임을 수용하여, 함수를 일급 객체로 취급하는 경향이 있다.

배열도 객체

자바스크립트의 배열은 일반적인 배열의 동작을 흉내낸 특수한 객체이며, 다른 배열과 마찬가지로 순차적인 요소들을 저장하는 데 특화되어 있다.

 

Array라는 내장 생성자 함수를 사용하여 만들어지며, 인덱스를 통해 요소에 접근할 수 있다. 또한, 추가적으로 배열 전용 메서드와 속성(e.g. length, push, pop 등)을 갖고 있어 배열 관련 작업을 쉽게 수행할 수 있다.

// 배열 리터럴 구문을 사용한 배열 생성
const arr1 = [1, 2, 3, 4, 5];

// Array 생성자 함수를 사용한 배열 생성
const arr2 = new Array(1, 2, 3, 4, 5);

배열도 객체이기 때문에, 객체와 같이 프로퍼티와 메서드를 추가할 수 있다. 그러나 배열의 일반적인 쓰임과는 맞지 않아, 해당 방법은 좋지 않은 습관이라고 할 수 있다.

// 배열 생성
const arr = [1, 2, 3, 4, 5];

// 배열에 프로퍼티 추가
arr.myProperty = 'Hello, World!';

// 배열에 메서드 추가
arr.printSum = function () {
  let sum = 0;

  for (const num of this) {
    sum += num;
  }

  console.log(`Sum: ${sum}`);
};

console.log(arr.myProperty); // 'Hello, World!'
arr.printSum(); // 'Sum: 15'

이렇게 배열에 직접 추가한 프로퍼티와 메서드는 해당 배열 인스턴스에만 적용된다. 다시 말하지만 권장하지 않는 방법이므로, 별도의 객체를 생성하여 관리하는 것이 좋다.

모든 것이 객체가 아니다. 거의 모든 것이 객체이다

자바스크립트의 객체는 매우 유연하고 강력하며, 중요한 특징 중 하나라는 것, 그리고 자바스크립트의 모든 값은 객체가 아님을 알게 되었다. 마지막으로 내용을 정리해보자:

  • 자바스크립트의 데이터 타입에는 원시값과 객체가 있다.
  • 객체란, 관련된 데이터와 함수(프로퍼티와 메서드)를 가지는 복합 자료 구조이다.
  • 원시값도 '일시적'으로 객체처럼 사용할 수 있다. 이는 자바스크립트 엔진이 자동으로 처리한다.
    • 이러한 처리를 오토박싱이라고 한다.
  • 결국, 모든 값은 객체가 아니다. 원시값을 제외한 거의 모든 값들이 객체이다.
반응형