221010 TIL | 자바스크립트 코딩 기법과 핵심 패턴_#3 리터럴과 생성자

객체 리터럴

  • 자바스크립트의 객체는 ‘이름-값’ 쌍의 해시 테이블
  • 사용자가 정의한 네이티브 객체는 언제든 변경 가능하며, 내장 네이티브 객체의 프로퍼티도 대부분 변경 가능

  • 프로퍼티와 메서드 값을 변경할 수 있다.

      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() 등 내장 생성자들은 사용을 자제한다.