3장 : 리터럴과 생성자

장윤희·2022년 5월 29일
0

JsPatterns

목록 보기
3/5
post-thumbnail

객체리터럴

객체

  • 이름-값 쌍의 해시테이블('연관배열'과 유사)
  • 원시 데이터 타입과 객체 모두 값이 될 수 있다
  • 함수도 값이 될 수 있으며 이런 함수는 매서드 라고 한다
  • 언제든지 변경 가능
객체의 프로퍼티들도 대부분 변경 가능
빈 객체를 정의해놓고 기능 추가 가능
ex) var dog = {}; //빈 객체에서 시작
    dog.name - 'Benji'; //프로퍼티 추가
    dog.getName = function(){
        return dog.name;
    } //메서드 추가

객체리터럴 표기법을 쓰면 생성시점에 객체에 기능을 추가할 수 있다

객체리터럴 문법

  • 객체를 중괄호로 감싼다
  • 객체 내의 프로퍼티와 매서드를 쉼표로 분리
  • 프로퍼티명과 프로퍼티 값은 클론으로 분리

생성자 함수로 객체 생성하기

객체를 생성할 때는 직접 만든 생성자 함수를 사용할 수 도 있고,
Object(), Date(), String()등 내장 생성자를 사용할 수도 있다

객체를 생성하는 두가지 방법

var car = {goes:'far'}; //리터럴 사용
var car = new Object();
car.goes = 'far'; //내장 생성자 사용 - 안티패턴

객체 생성자의 함정

  • Object() 생성자가 인자를 받을 수 있다
  • 인자로 전달되는 값에 따라 생성자함수가 다른 내장 생성자에 객체 생성을 위함 할 수 있고 다른 객체가 반환되기도 한다
  • 런타임에 결정하는 동적인 값이 생성자에 인자로 전달될 경우 예기치 않은 결과가 반환될 수 있다 => 사용하지 말것

사용자 정의 생성자 함수

직접 생성자 함수를 만들어 객체를 생성할 수도 있다

var adam = new Person('Adam');
adam.say(); //'I am adam'

아래는 Person생성자 함수를 정의한 예시
var Person = function (name){
    this.name = name;
    this.say = function (){
        return 'I am ' + this.name;
    };
};

new와 함께 생성자 함수를 호출하면 함수 안에서 생기는 일

  • 빈 객체가 생성된다 (this라는 변수로 참조할 수 있고, 해당 함수의 프로토타입을 상속 받는다)
  • this로 참조되는 객체에 프로퍼티와 메서드가 추가된다
  • 마지막에 다른 객체가 명시적으로 반환되지 않을 경우, this로 참조된 이 객체가 반환된다
var Person = function (name){
    var this = {};//객체리터럴로 새로운 객체 생성
    this.name = name;
    this.say = function (){
        return 'I am' + this.name;
    }; //프로퍼티와 메스드를 추가

    return this;
}

new Person()을 호출 할때 마다 메모리에 새로운 함수가 생성된다
say()라는 메서드가 인스턴스 별로 달라지는게 아니므로 프로토타입에 추가하는 것이 낫다

Person.prototype.say = function(){
    return 'I am' + this.name;
};

재사용되는 멤버는 프로토타입에 추가해야한다

var this = {};
'빈'객체라는게 실제로 텅 빈것이 아니다.
이 객체는 Person의 프로토타입을 상속받는다
var this = Object.create(Person.prototype);

생성자의 반환값

  • 생성자 함수를 new와 함께 호출하면 항상 객체가 반환된다
  • 기본값은 this로 참조되는 객체다
  • 함수 내에 return문을 쓰지 않았더라도 생성자는 암묵적으로 this를 반환한다
  • 생성자에서는 어떤 객체라도 반환할 수 있다
  • 객체가 아닌 (문자열, false값)을 반환하려고 하면 무시되고 this에 의해 참조된 객체가 대신 반환된다

new를 강제하는 패턴

  • new를 빼고 생성자를 호출한다면 논리적 오류가 생겨 예기치 못한 결과가 나올 수 있다
  • new를 빼먹으면 생성자 내부의 this가 전역객체를 가리키게 되기 때문이다(브라우저라면 this가 window를 가르킴)
  • 전격객체에 새로운 프로퍼티가 생성되는것이다. 네임스페이스는 깨끗하게 유지되어야함
function Waffle(){
    this.tastes = 'yummy';
}
var good_morning = Waffle(); //안티패턴
console.log(typeof good_morning); // undefined
console.log(widow.tastes); //yummy

명명규칙

  • 생성자 함수 : 첫글자를 대문자
  • 일반적 함수, 메서드 : 첫글자를 소문자

that 사용

  • 생성자가 항상 생성자로 동작하게 해주는 패턴
  • this에 모든 멤버를 추가하는 대신 that에 모든 멤버를 추가한 후 that을 반환
  • 간단한 객체라면 that이라는 지역변수를 만들 필요도 없이 객체 리터럴을 통해 객체를 반환해도 된다
function Waffle(){
    return{
        tastes : 'yummy'
    };
}
항상 객체가 반환
var first = new Waffle(),
    second = Waffle();
console.log(first.tastes); //'yummy'
console.log(second.tastes); //'yummy'

위 패턴의 문제는 프로토타입과의 연결고리를 잃어버린다
Waffle() 프로토타입에 추가한 멤버를 객체에서 사용할 수 없다

스스로를 호출하는 생성자

위의 패턴의 문제점을 해결하고 인스턴스객체에서 프로토타입의 프로퍼티들을 사용할 수 있게 하려면
1. 생성자 내부에서 this가 해당 생성자의 인스턴스인지 확인하고
2. new와 함께 스스로를 재호출 하는 것이다

function Waffle(){
    if(!(this instanceof Waffle){
        return new Waffle();
    })
    this.tastes = 'yummy';
}
Waffle.prototype.wangtAnother = true;

인스턴스를 판별하는 또다른 범용적인 방법은 생성자 이름을 하드코딩하는 대신 arguments.callee와 비교하는 것이다

if(!(this instanceof arguments.callee)){
    return new arguments.callee();
}

이것은 모든 함수가 호출 될 때 내부적으로 arguments라는 객체가 생성되며 이 객체가 함수에 전달된 모든 인자를 담고 있다는 점을 활용한 패턴
------> 더 자세히쓰기

배열리터럴

  • 배열은 객체다
  • Array()로 배열을 생성하는 것 보다 배열리터럴표기법이 더 간단하고 장점이 많다
var a = new Array('itsy', 'bitsy', 'spider'); //안티패턴
var a = ['itsy', 'bitsy', 'spider'];
console.log(typeof a); //배열도 객체이기 때문에 object가 출력
console.log(a.constructor === Array); //true

배열리터럴표기법

  • 각 원소는 쉼표로 분리
  • 전체 목록을 대괄호로 감싼다
  • 객체나 다른 배열 등 어떤 타입의 값이든 배열원소로 지정 할 수 있다

배열 생성자의 특이성

Array() 생성자에 숫자 하나를 전달할 경우, 이 값은 배열의 첫번째 원소값이 되는게 아니라 배열의 길이를 지정한다
즉, Array(3)은 길이가 3이고 실제 원소값은 가지지않는 배열을 생성한다
아래는 리터럴과 생성자 함수의 서로 다른 동작 방식을 보여준다

//한 개의 원소를 가지는 배열
var a = [3];
console.log(a.lengh);//1
console.log(a[0]);//3
//세 개의 원소를 가지는 배열
var a = new Array(3);
console.log(a.length); //3
console.log(typeof a[0]); //undefined

new Array()에 정수가 아닌 부동소수점을 가지는 수를 전달 할 경우
부동소수점은 배열의 길이로 유효한 값이 아니기 때문에 에러가 발생한다
런타임에 동적으로 배열을 생성할 경우 에러 발생을 피하려면 배열의 리터럴 표기법을 쓰는것이 안정적이다

배열인지 판단하는 방법
배열에 typeof연산자를 사용하면 'object'가 반환된다
console.log(typeof[1,2]); //object

값이 실제로 배열인지 알아내야 하는 경우가 자주 있다
하지만 배열이 아닌 객체가 똑같은 이름의 프로퍼티나 메서드를 가지지 말란 법은 없다
ECMAScript5에서는 Array.isArray()라는 새로운 매서드가 정의되어있어 이 메서드는 인자가 배열이면 true를 반환한다

Array.isArray([]); //true
//배열과 비슷한 객체로 속여본다
Array.isArray({
    length:1,
    '0':1,
    slice: function(){}
}); //false가 반환된다

이 매서드를 사용할 수 없는 경우에는 Object.prototype.toString()메서드를 호출하여 판별

  • 배열에 toString을 호출하면 '[object Array]'라는 문자열 반환
  • 객체일 경우 문자열 '[object object]'가 반환

배열 판단 매서드

if(typeof Array.isArray === 'undefined'){
    Array.isArray = function (arg){
        return Object.prototype.toString.call(arg) === '[object Array]';
    };
}

JSON

  • 자바스크립트 객체 표기법의 준말, 데이터 전송 형식의 일종
  • 배열과 객체리터럴 표기법의 조합
  • 프로퍼티명을 따옴표로 감싸야한다는 것이 객체 리터럴과의 유일한 문법적 차이
  • 함수나 정규식 리터럴을 사용할 수 없다
eval보다 JSON.parse()를 사용하는 것이 최선책이다

//입력되는 JSON 문자열
var jstr = '{'mykey':'my value'}';

//안티패턴
var data = eval('('+jstr+')');

//권장안
var data = JSON.parse(jstr);
console.log(data.mykey);

JSON.parse()의 반대는 JSON.stringify()이다
이 메서드는 객체 또는 배열을 인자로 받아 JSON문자열로 직렬화한다

var dog = {
    name :'Fido',
    dob : new Date(),
    legs : [1,2,3,4]
};

var jsonstr = JSON.stringify(dog);

정규표현식 리터럴

정규표현식 역시 객체이며 정규식을 생성하는 방법은 2가지이다

  • new RegExp() 생성자를 사용한다
  • 정규식 리터럴을 사용한다
//정규식 리터럴
var re = /\\/gm;

//생성자
var re = new RegExp('\\\\', 'gm');

정규표현식 리터럴 문법

매칭에 사용되는 정규식 패턴을 슬래시로 감싼다
두번째 슬러시 뒤에는 따옴표없이 문자 형태의 변경자를 둘 수 있다

g:전역매칭
m:여러 줄 매칭
i:대소문자 구분 없이 매칭
  • 패턴변경자는 순서에 상관없이 쓸 수 있으며 여러개를 함께 써도 된다
  • 매칭 시킬 패턴을 미리 알 수 없고 런타임에 문자열로 만들어지는 경우에는 new RegExp()를 사용해야한다
  • 정규식리터럴과 생성자의 또 다른 차이점으로는 정규식 리터럴의 경우 파싱 될 때 단 한번만 객체를 생성한다
  • 루프안에서 동일한 정규식을 생성하면 이미 생성된 객체가 반환되며 lastIndex등 모든 프로퍼티는 최초에 설정 된 상태를 이어받는다
  • new를 빼먹고 RegExp()를 호출해도(생성자가 아니라 함수처럼 호출해도) new와 함께 호출한 것처럼 동작한다

원시데이터 타입래퍼

자바스크립트에는 숫자, 문자열, 불린, null, undefined의 다섯가지 원시데이터타입이 있다
null, undefined를 제외한 나머지 세개는 원시 데이터 타입 래퍼라 불리는 객체를 가지고 있다
이 래퍼 객체는 각각 내장 생성자인 Number(), String(), Boolean()을 사용하여 생성된다

아래는 원시데이터 타입 숫자와 숫자 객체의 차이를 보여준다

//원시 데이터 타입 숫자
var n = 100;
console.log(typeof n); //'numer'
//숫자 객체
var nobj = new Number(100);
console.log(typeof nobj); //'object'

숫자객체에는 toFixed()와 toExponential()같은 메서드가 있고
문자열객체에는 substring(), charAt(), toLowerCase()같은 메서드와 length 프로퍼티가 있다
메서드를 호출하는 순간 내부적으로는 원시 데이터 타입 값이 객체로 임시변환되어 객체처럼 동작한다

//원시 데이터 타입 문자열을 객체로 사용한다
var s = 'hello';
console.log(s.toUpeprCase()); //'HELLO'

//값자체만으로도 객체처럼 동작할 수 있다
'monkey'.slice(3,6);//'key'

//숫자도 마찬가지다
(22/7).toPrecision(3); //'3.14'

값을 확장하거나 상태를 지속시키기 위해 래퍼 객체를 쓰는 경우도 있다
원시데이터타입은 객체가 아니기 때문에 프로퍼티를 추가하여 확장할 수가 없다

//원시 데이터 타입 문자열
var greet = 'Hello there';
//split() 메서드를 쓰기 위해 원시 데이터타입이 객체로 변환된다

greet.split(' ')[0]; //'Hello'
//원시데이터 타입에 확장을 시도할 경우 에러는 발생하지 않는다

greet.smile = true;
//그러나 실제로 동작하는것은 아니다
typeof greet.smile;//'undefined'

위에서 greet프로퍼티 또는 메서드에 접근하는 작업을 에러 없이 처리하기 위해 일시적으로만 객체로 변환된것 뿐이다
만약 greet가 newString()을 사용하여 객체로 정의되었다면 기대한대로 smile프로퍼티가 생성되었을 것이다

new를 빼먹고 래퍼 생성자를 사용하면 래퍼생성자가 인자를 원시데이터 타입의 값으로 변환된다

typeof Number(1);//'number'
typeof Number('1');//'number'
typeof Number(new Number());//'number'
typeof string(1);//'string'
typeof Boolean(1);//'boolean'

애러 객체

js에는 Error(), SyntaxError(), TypeError()등 여러 가지 에러 생성자가 내장되어 있으며 throw문과 함께 사용된다

  • name : 객체를 생성한 생성자 함수의 name프로퍼티
  • message : 객체를 생성할 때 생성자에 전달된 문자열

throw문은 어떤 객체와도 함께 사용할 수 있다
반드시 에러 생성자를 통해 객체를 생성해야 하는 것은 아니며 직접 정의한 객체를 던질 수도 있다
이 객체는 'name'과 'message'외에도 임의의 프로퍼티를 가질 수 있기 때문에 catch문에서 처리할 정보를 담아 전달하면 된다

try{
    //에러를 발생시킨다
    throw{
        name:'MyErrorType',// 임의의 에러타입
        message:'oops',
        extra:'This was rather embarrassing',
        remedy:genericErorHandler//에러를 처리할 함수
    };
}catch(e){
    //사용자에게 공지한다
    alert(e.message);//'oops'
    //훌륭하게 예외를 처리한다
    e.remedy();//genericErorHandler()호출
}

에러 생성자를 new없이 일반 함수로 호출해도 new를 써서 생성자로 호출한 것과 동일하게 동작하여 여러 객체가 반환된다

객체리터럴표기법 : 이름-값 쌍으로 쉼표로 분리하고 괄호로 감싸 객체를 만듬
생성자 함수 : 내장생성자의 경우 객체리터럴을 사용하는 것이 낫다
배열 리터럴 표기법 : 대괄호 안에서 값 목록을 쉼표로 분리한다
JSON : 객체와 배열 표기법으로 이루어진 데이터 형식

profile
멋쟁이

0개의 댓글