[자바스크립트] 객체 제대로 이해하기
자바와 같은 객체지향 언어를 먼저 접한 이후 자바스크립트를 배웠다면, 객체를 다룰 때 꽤나 혼동이 있을 것이다. 하지만 객체에 대한 개념을 제대로 잡아두지 않으면 후에 프로토타입이나 클로저로 이어지는 자바스크립트의 언어적 특징과 심오한 철학에 대해서도 이해하기 어렵다. 천리길도 한걸음부터라고 했으니, 차근차근 시작해보도록 하자.
❙ 자바스크립트에서의 객체
자바스크립트에서는 모든 것이 다 객체다. 숫자, 문자열, boolean 등 기본 타입을 제외한 모든 값은 객체다. 배열, 정규표현식은 물론이고 모든 함수마저도 자바스크립트에서는 객체로 취급된다. 자바스크립트에서 말하는 객체는 단순하게 key:value 형태의 값들을 저장하는 컨테이너로 볼 수 있다.
var foo = {
name : 'foo',
age : 30,
gender : 'male',
region : function(){ // -->
return "seoul";
}
};
숫자, 문자열 등의 기본 타입은 값 하나만을 가지는데 비해 객체는 여러가지 프로퍼티(property)를 가질 수 있는 것이다. 객체의 프로퍼티에는 함수가 포함될 수 있는데, 자바스크립트에서는 이러한 프로퍼티를 메서드라고 부른다. 위 예시코드의 5번째 라인에서처럼 말이다.
함수에 대해서 조금 덧붙여보자면, 자바스크립트에서는 함수도 객체로 본다. 다시말해 함수 역시 프로퍼티를 가질 수 있다는 것이다. 간단한 예를 들어보자.
function add(x, y) {
return x+y;
}
add.status = 'OK';
console.log(add.status); // 'OK'
위와 같은 코드를 실행해보면 콘솔창에는 OK가 찍힌다. 위에서 봤던 자바스크립트 객체의 예처럼 함수 역시 프로퍼티를 가질 수 있음이 확인된 것이다. 그런데, 함수 객체는 일반 객체에는 없는 표준 프로퍼티가 별도로 정의되어 있다. 위 예제에서 정의한 add의 프로퍼티를 콘솔창에 출력해보면 다음과 같은 결과를 확인할 수 있다.
argument, caller, length 등은 따로 정의하지 않아도 함수 객체에서 기본적으로 생성되는 프로퍼티들이다. 이러한 프로퍼티를 함수 객체의 표준 프로퍼티라고 한다. 간단히 살펴보면 argument 프로퍼티는 함수 호출시 전달된 인자값을 나타내는데, 예시에서는 함수를 호출한 상태가 아니므로 null이다. calle 프로퍼티는 자신을 호출한 함수를, length 프로퍼티는 인자의 개수를 나타낸다.
❙ 기본 타입의 메서드 호출
자바스크립트에도 문자열과 같은 기본타입이 있고, 메서드라는 개념은 객체의 프로퍼티로서 정의된다고 하였다. 이렇게 깔끔하게 정리되면 좋겠지만 사실 기본타입인 문자열 또한 메서드를 가질 수 있다. 다음과 같이 말이다.
console.log("test".charAt(2)); // 's'
코드를 실행해보면 콘솔창에는 s가 출력된다. 언뜻보면 당연하지만, 분명 메서드라는 개념은 객체에 포함된다고 하지 않았는가. 기본타입인 문자열은 객체가 아닌데도 메서드를 가질 수 있다는 것일까. 결과적으로 말하면 가질 수 있다. 정확하게는 charAt이라는 메서드를 호출할 때 문자열 "test"는 객체로 변환된다. 그리고 charAt을 호출한 뒤 다시 기본값으로 복귀하게 되는 것이다.
❙ 객체 생성 방식
자바스크립트에서 객체를 생성하는 방식에는 두 가지가 있다. 객체 리터럴, 또는 생성자 함수를 이용하는 것이다. 간단한 예제를 보며 두 방식의 차이를 눈으로 익혀보자.
//객체 리터럴 방식
var foo = {
name : 'foo',
age : 35,
genger : 'man'
};
console.dir(foo);
//생성자 함수
function Person(name, age, gender, position){
this.name = name;
this.age = age;
this.gender = gender;
}
// Person 생성자 함수를 이용해 bar 객체, baz 객체 생성
var bar = new Person('bar', 33, 'woman');
console.dir(bar);
var baz = new Person('baz', 25, 'woman');
console.dir(baz);
cs
console.dir은 객체의 모든 프로퍼티를 보여주는 메서드이다. 위에서는 리터럴 방식을 이용했고, 아래 Person 함수는 생성자 함수를 이용해 객체를 생성했다. 코드를 실행하면 결과는 다음과 같다.
별 차이가 없어보여도 사실 프로토타입에서 차이가 있다. 객체 리터럴 방식에서는 객체의 생성자 함수가 Object()인데 반해, 생성자 함수 방식에서는 생성자 그 자체(예시에서는 Person())이다. 이는 결과적으로 두 방식이 다른 프로토타입 객체를 가지게 하는데, 프로토타입에 대해서는 더 깊게 들어가면 또 할말이 많으니 여기서는 이정도까지만 알아두자.
❙ new 키워드를 붙이지 않고 생성자 함수를 호출한다면
만약 new를 붙이지 않고 생성자를 호출하면 어떻게 될까. 아래 코드처럼 말이다.
var qux = Person('qux', 20, 'man');
console.log(qux); // undefined
console.log(window.name); // qux
console.log(window.age); // 20
console.log(window.gender); // man
결과는 undefined인데, 놀랍게도 전역 객체인 window에 프로퍼티들이 바인딩된다. new를 붙이지 않았기 때문에, 1라인의 코드는 객체 생성이 아닌 일반적인 함수 호출로 인식된다. 자바스크립트에서 this는 함수 호출 컨텍스트에서 전역 객체로 바인딩(자바스크립트에서의 this 바인딩 참조)되는데, 따라서 window 객체에 동적으로 name, age, gender 프로퍼티가 생성된 것이다. new 키워드 하나를 빼먹는 작은 실수가 전혀 다른 결과를 만들어내니 유의하자.