객체 리터럴
- 자바스크립트의 객체는 ‘이름-값’ 쌍의 해시 테이블
-
사용자가 정의한 네이티브 객체는 언제든 변경 가능하며, 내장 네이티브 객체의 프로퍼티도 대부분 변경 가능
-
프로퍼티와 메서드 값을 변경할 수 있다.
dog.getName = function () { // 메서드가 하드코딩된 값을 반환하도록 재정의한다. return "Fido"; }; -
프로퍼티나 메서드를 완전히 삭제한다.
delete dog.name; -
다른 프로퍼티나 메서드를 추가한다.
dog.say = function () { return "Woof!"; }; dog.fleas = true;
객체 생성자의 함정
-
객체 리터럴을 사용할 수 있는 상황에서 new Object() 생성자를 쓸 이유가 없다.
생성자 함수를 사용한다면 지역 유효범위에 동일한 이름의 생성자가 있을 수 있기 때문에 호출 시점부터, 전역 Object 생성자까지 인터프리터가 거슬러 올라가며 유효범위를 검색하기 때문이다.
- Object() 생성자가 인자를 받을 수 있다는 점에서 문제가 발생한다.
- Object() 생성자를 이용하여 객체를 생성했음에도 다른 생성자로 생성된 객체가 반환되고 있다.
// 경고: 모두 안티패턴이다
// 빈 객체
var o = new Object();
console.log(o.constructor === Object); // true
// 숫자 객체
var o = new Object(1);
console.log(o.constructor === Number); // true
console.log(o.toFixed(2)); // "1.00"
// 문자열 객체
var o = new Object(/ZI am a string");
console.log(o.constructor === String); // true
// 일반적인 객체에는 substring)이라는 메서드가 없지만 문자열 객체에는 있다
console.log(typeof o.substring); // "function"
// 불린객체
var o = new Object(tr니e);
console.log(o.constructor === Boolean); // true
- Object() 생성자의 이같은 동작 방식 때문에 런타임에 결정하는 동적인 값이 생성자에 인자로 전달될 경우 예기치 않은 결과가 반환될 수 있다.
- 때문에 new Object()를 사용하지 않고 객체 리터럴 방식을 사용하도록 한다.
사용자 정의 생성자 함수
- 다음 예시는 Java에서 Person이라는 클래스를 사용하여 객체를 생성하는 방식과 유사해보인다.
var adam = new Person("Adam");
adam.say(); // "I am Adam"
- 하지만 자바스크립트에서 클래스라는 것이 없기 때문에 Person은 new와 함께 호출될 뿐, 보통의 함수일 뿐이다.
- Person 생성자 함수를 정의한 예시이다.
var Person = function (name) {
this.name = name;
this.say = function () {
return "I am " + this.name; };
};
- new 키워드와 함께 생성자 함수를 호출하면 다음과 같은 일이 일어난다.
- 빈 객체(Person의 프로토타입을 상속받은)가 생성된다. 이 객체는 this라는 변수로 참조할 수 있고, 해당 함수의 프로토타입을 상속 받는다.
- this로 참조되는 객체에 프로퍼티와 메서드가 추가된다.
- 마지막에 다른 객체가 명시적으로 반환되지 않을 경우 this로 참조된 이 객체가 반환된다.
- 코드로 보면 이런 흐름으로 흘러간다.
var Person = function (name) {
// 객체 리터럴로 새로운 객체를 생성한다
// var this = Object.create(Person.prototype);
// 프로퍼티와 메서드를 추가한다.
this.name = name; this.say = function () {
return "I am " + this.name; };
// this를 반환한다.
// return this;
};
- this에 say()라는 메서드를 간단하게 추가했지만, 결과적으로 new Person()을 호출할 때마다 메모리에 새로운 함수가 생성된다.
- 이런 방법은 비효율적이기 때문에 Person의 프로토타입에 추가하는 것이 낫다.
Person.prototype.say = function () {
return "I am '' + this.name;
};
**메서드와 같이 재사용되는 멤버는 생성자가 아닌 프로토타입에 추가해야 한다.**- 자세한 것은 프로토타입과 상속에서 다뤄보도록 하자.
생성자의 반환 값
- 생성자 함수를 new와 함께 호출하면 항상 객체가 반환이 된다고 했다.
- 이는 함수 내에 return 문을 쓰지 않으면 생성자는 암묵적으로 this를 반환하기 때문이다.
- 물론 반환 값이 될 객체를 별도로 설정할 수도 있다.
- 객체가 아닌 것(문자열이나 false 값)을 반환하려고 시도하면 무시되고 this에 의해 참조된 객체가 반환된다.
new를 강제하는 패턴
- 생성자는 new와 함꼐 호출될 뿐 별다를 것 없는 함수에 불과하다고 했었다.
- new를 빼먹으면 문법 오류나 런타임 에러가 발생하지 않지만 논리적인 오류가 생겨 예상치 못한 결과가 나올 수 있다.
new를 빼먹으면 생성자 내부의 this가 전역 객체를 가리키게 때문이다.- 브라우저라면 this가 window를 가리킨다.
// 생성자
function Waffle() {
this.tastes = "yummy";
}
// 새로운 객체
var good_morning = new Waffle();
console.log(typeof good_morning); // "object"
console.log(good_morning.tastes); // "yummy"
// 안티패턴:
// 'new’ 를 빼먹었다
var goodmorning = Waffle();
console.log(typeof good_morning); // "undefined"
console.log(window.tastes); // "yummy"
- ECMAScript5에서는 이러한 동작 방식의 문제에 대한 해결책으로 strict 모드에서는 this가 전역 객체를 가리키지 않도록 했다.
명명 규칙
- 일반 함수와 헷갈리지 않도록 생성자 함수의 첫 글자는 대문자를 사용한다. (MyConstructor)
배열 리터럴
- 자바스크립트의 배열인 이 언어 내 다른 모든 것들과 마찬가지로 객체이다.
- new Array()에 정수가 아닌 부동소수점을 가지는 수를 전달할 경우 에러가 발생할 수 있다.
배열인지 판별하는 방법
- isArray()를 사용한다.
- toString()을 사용한다. (object Array가 반환되기 때문에 문자열을 비교할 수 있다.)
JSON
- JSON(JavaScript Object Notation)은 데이터 전송 형식의 일종이다.
- 자바스크립트를 비롯한 여러 언어에서 가볍고 편리하게 사용할 수 있다.
- JSON은 객체 리터럴 표기법의 조합이며, 프로퍼티명을 따옴표로 감싸야 한다는 점을 제외하면 문법이 같다.
JSON 다루기
- JSON 문자열을 평가하기 위해서는 JSON.parse()를 사용하는 것이 최선책이다.
원시 데이터 타입 래퍼
- 자바스크립트의 원시 데이터 타입(number, boolean, string, null, undefined)은 null, undefined를 제외하고 각각 원시 데이터 타입 래퍼라 불리는 객체를 가지고 있다.
- ES6 Symbol, BigInt 추가
- 래퍼 객체는 각각 내장 생성자인
Number(), String(), Boolean()을 사용하여 생성된다. - 래퍼 객체에는 유용한 프로퍼티와 메서드들이 있다.
에러 객체
- 자바스크립트에는 Error(), SyntaxError(), TypeError() 등 여러가지 에러 생성자가 내장되어 있다.
- throw 문과 함께 사용되며, 다음과 같은 프로퍼티를 가진다.
name: 객체를 생성한 생성자 함수의 name 프로퍼티, “Error”일 수도 있고 “RangeError”와 같이 특화된 생성자일 수도 있다.message: 객체를 생성할 때 생성자에 전달할 문자열
- throw문은 어떤 객체와도 함께 사용할 수 있으며, 반드시 에러 생성자를 사용해야 하는 것은 아니다.
- 사용자가 정의한 객체를 던지는 예시를 보자.
try {
// 에러를 발생시킨다.
throw {
name: "MyErrorType", // 임의의 에러 타입
message: "oops",
extra: "This was rather embarrassing", remedy: genericErrorHandler // 에러를 처리할 함수
};
} catch (e) {
// 사용자에게 공지한다.
alert(e.message); // "oops"
// 훌륭하게 에러를 처리한다
e.remedy(); // genericErrorHandler() 호출
}
요약
- 객체 리터럴 표기법 - 이름-값 쌍을 쉼표로 분리하고 괄호로 감싸 객체를 만드는 품위있는 방법이다
- 생성자함수 : 내장생성자 함수와 사용자정의 생성자를 살펴보았다. 내장생성자의 경우, 대개는 대응하는 리터럴 표기법을 쓰는 것이 낫다.
- 배열 리터럴 표기법 - 대괄호 안에서 값 목록을 쉼표로 분리한다.
- JSON - 객체와 배열 표기법으로 이루어진 데이터 형식이다.
- String(), NumberO, Boolean() 및 여러 가지 Error() 등 내장 생성자들은 사용을 자제한다.