객체는 사용자, 주문 등과 같이 실제 존재하는 개체를 표현하고자 할 때 생성된다.
사용자는 현실에서 물건 선택하기, 로그인하기, 로그아웃하기 등의 행동을 한다. 사용자를 나태나는 객체 user 도 특정한 행동을 할 수 있다.
자바스크립트에서는 객체의 프로퍼티에 함수를 할당해 객체에게 행동할 수 있는 능력을 부여해 준다.
객체 user 에게 인사할 수 있는 능력을 부여해 보자.
let user = {
name: "John",
age: 30
};
user.sayHi = function() {
alert("안녕하세요!");
};
user.sayHi(); // 안녕하세요!
함수 표현식으로 함수를 만들고, 객체 프로퍼티에 함수를 할당해 주었다. 이제 객체에 할당된 함수를 호출하면 user 가 ‘안녕하세요'를 출력한다.
이렇게 객체 프로퍼티에 할당된 함수를 ‘메서드’라고 한다.
미리 정의된 함수를 이용해서 메서드를 만들 수도 있다.
let user = {
name: "John",
age: 30
};
function sayHi() {
alert("안녕하세요!");
}
user.sayHi = sayHi;
user.sayHi(); // 안녕하세요!
💡 객체 지향 프로그래밍
객체를 사용하여 개체를 표현하는 방식을 객체 지향 프로그래밍(OOP)이라고 한다. OOP 그 자체로도 학문의 분야가 되는 중요한 주제이다. 올바른 개체를 선택하는 방법, 개체 사이의 상호작용을 나타내는 방법 등에 관한 의사결정은 객체 지향 설계를 기반으로 이루어진다. OOP 관련 추천도서로는 GoF의 디자인 패턴, UML을 활용한 객체지향 분석 설계 등이 있다.
객체 리터럴 안에 메서드를 선언할 때 function 을 생략할 수도 있다.
// 아래 두 객체는 동일하게 동작한다.
user = {
sayHi: function() {
alert("Hello");
}
};
// 단축 구문을 사용하면 더 깔끔해진다.
user = {
sayHi() { // "sayHi: function()"과 동일
alert("Hello");
}
};
두 방법 사이에는 객체 상속과 관련된 미묘한 차이가 있지만, 동작은 같다.
매서드는 객체에 저장된 정보에 접근할 수 있어야 제 역할을 할 수 있다. 대부분의 메서드는 객체 프로퍼티의 값을 활용한다. 메서드 내부에서 객체에 접근하기 위해 사용하는 키워드가 바로 this
이다.
let user = {
name: "John",
age: 30,
sayHi() {
// 'this'는 '현재 객체'를 의미
alert(this.name);
}
};
user.sayHi(); // John
user.sayHi() 가 실행되는 동안 this 는 user 라는 객체를 나타낸다.
this 를 사용하지 않고 외부 변수를 참조해 객체에 접근할 수도 있지만, 예상치 못한 에러가 발생할 수 있다. 예를 들어 user 를 복사해 다른 변수에 할당하고, user를 전혀 다른 값으로 덮어쓴다면, sayHi() 는 원치 않는 값을 참조하게 된다.
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name );
}
};
let admin = user;
user = null; // user를 null로 덮어쓴다.
admin.sayHi(); // sayHi()가 엉뚱한 객체(user.name)를 참고하면서 에러가 발생
자바스크립트의 this 는 다른 프로그래밍 언어의 this 와 동작 방식이 다르다. 자바스크립트에서는 모든 함수에 this 를 사용할 수 있다.
function sayHi() {
alert( this.name );
}
자바스크립트의 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 (점과 대괄호는 동일하게 동작함)
💡 **객체 없이 호출하기: this == undefined**
엄격 모드인 경우, this 를 사용한 함수를 객체 없이 호출하면 this 에는 undefined 가 할당된다.
엄격 모드가 아닐 경우, this 는 전역 객체를 참조한다. 브라우저 환경에서는 window 라는 전역 객체를 참조하게 된다. 이런 식으로 작성된 코드는 실수인 경우가 많다. 따라서 함수 본문에 this 가 사용되었다면 객체 컨텍스트 내에서 이 함수를 호출할 것이라고 예상하면 된다.
💡 **자유로운 this 가 만드는 결과**
자바스크립트에서 this 는 런타임에 결정된다. 메서드가 어디에서 정의되었는지와 상관없이 ‘점 앞의’ 객체가 무엇인가에 따라 자유롭게 결정된다.
자유로운 this 의 좋은 점은 함수를 하나만 만들어 여러 객체에서 재사용할 수 있다는 것이다. 그러나 이런 유연함이 실수로 이어질 수 있다는 단점도 있다.
화살표 함수는 일반 함수와 달리 ‘자신만의' this 를 가지지 않는다. 화살표 함수에서 this 를 참조하면, 화살표 함수가 아닌 평범한 외부 함수에서 this 값을 가져온다.
아래 예시에서 arrow()의 this 는 외부 함수 user.sayHi() 의 this 가 된다.
let user = {
firstName: "보라",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // 보라
별개의 this 가 만들어지는 것은 원하지 않고, 외부 컨텍스트에 있는 this 를 이용하고 싶은 경우 화살표 함수가 유용하다.
함수로 호출한 경우 this 에는 undefined 값이 할당된다.
아래 코드는 undefined의 name 을 찾으려는 것이므로 에러가 발생한다.
function makeUser() {
return {
name: "John",
ref: this
};
};
let user = makeUser();
alert( user.ref.name );
에러가 발생하지 않으려면 메소드로 호출하게 만들어야 한다.
function makeUser() {
return {
name: "John",
ref() {
return this;
}
};
};
let user = makeUser();
alert( user.ref().name ); // John