객체 생성방식

  • 리터럴 : 일반적이고 간단

    1
    var a = {b : ‘c’, d : ‘e’}
    cs

     

  • 생성자 함수 : 오늘 알아볼것

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function a() {
     
    this.b = ‘c’;
     
    this.d = ‘e’;
     
    }
     
    var a = new a();
    cs

 

생성자 함수

  • new와 함께 호출하여 객체를 생성하는 함수

  • new없이 호출하면 생성자 함수가 아닌 일반 함수로 동작

  • 종류 : Object, String, Number, Boolean, Function, Array, Date, RegExp, Promise 등

  • 사용하는 이유

    • 객체 리터럴의 문제점 : 동일한 객체 여러번 생성할때 비효율적
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      const a1 {
       
      num : 123,
       
      printnum() {return this.num};
       
       }
       
      console.log(a1.printnum()); // 123
       
       
       
      const b1 {
       
      num : 456,
       
      printnum() {return this.num};
       
       }
       
      console.log(b1.printnum()); // 456
      cs
    • 생성자 함수를 사용할 경우 프로퍼티 구조 동일한 객체를 간편하게 생성 가능
    • 자바의 클래스 개념과 유사
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      function a(num) {
       
      this.num = num;
       
      this.printnum = function() {return this.num};
       
      }
       
       
       
      const a1 = a(123);
       
      const b1 = a(456);
       
       
       
      console.log(a1.printnum()); // 123
       
      console.log(b1.printnum()); // 456
      cs

생성자 함수, 인스턴스 생성 과정

  • 인스턴스 생성과 this 바인딩
    • 빈 인스턴스가 생성
    • this는 빈 인스턴스에 바인딩
      • 앞에 new 붙이지 않으면, this는 window를 가르킴(일반 함수로 호출되기 때문)
      • 그래서 함수와 생성자 함수를 구분하기 위해서 생성자 함수는 첫글자를 대문자로함(파스칼 케이스라고 부름)
    • 위 과정은 생성자 함수 코드가 실행되기 전에 실행됨
      • 어려운 말로, 런타임 이전에 실행
  • 인스턴스 초기화
    • 생성자 함수 코드 한줄씩 실행되며, 인스턴스를 초기화
  • 인스턴스 반환
    • return 따로 명시하지 않아도 this를 반환
    • 만약 this아닌 다른 객체를 return 하면, this가 아닌 다른 객체를 return
    • this 아닌 primitive 반환하면 무시
      • primitive : String, Number 등
      • 이러면 꼬이게됨, 일반적으로 return을 생략해서 사용
  • 예시
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function Circle(radius) {
     
    // 1. 빈 인스턴스 생성 및 this에 바인딩
     
     
     
    // 2. 코드 한줄씩 실행되며 인스턴스 초기화
     
    this.radius = radius;
     
    this.getDiameter = function() {
     
        return 2 * this.radius;
     
    };
     
     
     
    // 3.this를 return
     
    };
     
    cs

생성자 함수로 만든 객체가 일반 객체와 다른점

  • 일반 객체는 호출할 수 없지만 함수는 호출 가능
    • 일반 객체에게 없는, 함수로서 동작하기 위한 내부슬롯과 내부 메소드를 가짐
    • 내부슬롯, 내부메소드 : 외부로 노출되진 않지만, 자바스크립트 내부에서 동작하는 객체, 메소드
    • 내부슬롯
      • [[Environment]]
      • [[FormalPrameters]]
    • 내부 메소드
      • [[Call]] : 일반함수로서 호출되면 실행
      • [[Construct]] : new 연산자와 함께 호출되면 실행

생성자 함수로 객체 생성이 불가능한 함수

  • 함수 내부 메소드로 [[Construct]]가 없는것은 생성자로 함수로 객체 생성이 불가능
  • 객체 생성이 가능한 함수
    • constructor로 부름
    • 함수 선언문 : function Circle() {~~}
    • 함수 표현식 : var Circle = function() {~~}
    • 클래스(클래스도 함수)
  • 객체 생성이 불가능한 함수
    • ES6 메서드 축약 표현 :
      const obj = {
      x() {}
      }
    • 화살표 함수 : const Circle = () => {};

생성자 함수가 일반 함수와 다른점

  • this
    • 생성자 함수는 this가 인스턴스를 가르키지만,
  • 일반 함수의 this는 window를 가르킴
    • 그래서 함수와 생성자 함수를 구분하기 위해서 생성자 함수는 첫글자를 대문자로함(파스칼 케이스라고 부름)
  • new.target : 생성자 함수로서 호출되었는지 확인 가능
  • 생성자 함수로 호출되면(new 연산자로 호출되면), new.target은 함수 자신을 가르킴
    • 일반 함수로 호출되면 new.target은 undefined
    • 일반 함수로 호출되어도 생성자 함수로 호출되도록 사용하기 위해 활용됨
      1
      2
      3
      4
      5
      6
      7
      8
      9
      function Circle(radius) {
          if(!new.target) {
              return new Circle(radius); // 일반 함수로 호출되었을경우 생성자로 다시 호출
      }
       
      this.radius = radius;
      this.getDiameter = function() {return 2*this.radius;};
       
      }
      cs
    • 스코프 세이프 생성자 패턴
      • new.target은 ES6부터 도입해서, 비교적 최신 버전에서만 지원
      • 그래서 new.target 대신, this instanceof Circle 로 사용하기도 함

this

  • this는 기본적으로 window

  • 객체 메서드 안에서 this는 객체를 가르킴

    • 객체 메서드 : 객체 안에 있는 함수

    • 객체의 메서드 호출할 때 내부적으로 this를 바꿔줌

    • 단, 객체의 메서드를 호출한 것이 아닌, 객체의 메서드를 변수에 저장해서 호출할 경우 객체가 아닌 window를 가르킴

      1
      2
      3
      4
      5
      6
      7
      var obj = {
          a : function() {console.log(this)}
      };
      obj.a(); // obj
       
      var a2 = obj.a; 
      a2(); // window
      cs
  • 생성자 함수로 만든 객체에서 this는 인스턴스 자신을 나타냄

  • 이벤트 리스너

    • this : 이벤트 발생한 객체
    • 바뀐것이기 때문에 외울수밖에 없음
    1
    2
    3
    document.body.onclick = function(){
    console.log(this); // <body>
    }
    cs
  • 제이쿼리

    • 내부적으로 this를 클릭한 객체로 바꿔줌
    1
    2
    3
    $(‘div’).on(‘click’,function(){
        console.log(this);
    })
    cs
    • click 이벤트 안에서 메소드 나타날경우, this는 window가르킴

      • 함수의 this는 기본적으로 window라는 원칙을 따름
      • 이때 이벤트 호출한 객체를 저장하기 위해 var that = this와 같이 새로운 변수에 저장해서 사용하는 방법이 있음
    1
    2
    3
    4
    5
    6
    7
    $(‘div’).on(‘click’,function(){
        console.log(this); // div
    function inner() {
        console.log(‘inner’,this); // inner window
    }
    });
     
    cs

내부 슬롯, 내부 메서드

  • ECMAScript에서 사용하는 의사 프로퍼티(pseudo property), 의사 메서드(pseudo method)
  • ECMAScript에서 이중 대괄호로 감싸서 표현
  • 실제로 동작하지만, 개발자가 접근할 수 없음
    • 자바스크립트 엔진의 내부 로직
    • 일부 내부 슬롯, 내부 메서드에 한하여 간접적으로 접근 가능
  • 예시
    • 모든 객체 [[Prototype]] 내부 슬롯 직접 접근 불가능, proto로 접근 가능
1
2
3
const o = {};
o.[[prototype]] // Error
o.__proto__ // Object.prototype
cs

프로퍼티 어트리뷰트

  • 객체에서 프로퍼티 생성할 때, 프로퍼티 상태 나타내는 어트리뷰트도 함께 생성
  • 내부슬롯이어서 직접 접근 불가능
  • Object.getOwnPropertyDescriptor 사용해서 간접적 확인 가능
1
2
3
4
const person = {name:’Lee’};
console.log(Object.getOwnPropertyDescriptor(person, ‘name’));
// {value : ‘Lee’, writable : true, enumerable: true, configurable: ture}
 
cs
  • 종류(데이터 프로퍼티의 경우)
    • 데이터 프로퍼티 : 일반적 프로퍼티, 데이터 프로퍼티 외에 접근자 프로퍼티도 있음
      • [[value]] : 값
      • [[writable]] : 갱신 가능여부
      • [[enumerable]] : 열거 가능 여부
      • [[configurable]] : 재정의 가능 여부
    • 갱신과 재정의의 차이 :
      • 갱신 가능할경우, 해당 프로퍼티를 덮어 쓸 수 있음
      • 재정의 가능할 경우, 삭제하거나 재정의 불가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Object.defineProperty(person, ‘firstName’, {
    value:’Jango’
}); // value만 명시해줄경우, 나머지 속성들 false
console.log(Object.getOwnPropertyDescriptor(person, ‘firstName’);
// {value:’Jango’, writable: false, enumerable: false, configurable : false};
 
person.firstName = ‘Dingo’; // 갱신 불가능, writable = false이므로
// 에러발생하진 않고 무시됨
 
delete person,firstName; // 삭제 불가능, configurable = false이므로
// 에러 발생하진 않고 무시됨
 
Object.defineProperty(person, ‘lastName, {enumerable:true}) 
// 재정의 불가능, configurable = false이므로
// 에러 발생
 
cs

프로퍼티 종류

  • 데이터 프로퍼티 : 일반적 프로퍼티
    • 앞의 프로퍼티 어트리뷰트를 가짐
  • 접근자 프로퍼티 : 값을 갖지 않고, 다른 프로퍼티 읽거나 저장할때 사용
    • 프로퍼티 어트리뷰트
      • [[get]] : 데이터 프로퍼티 값읽을때 호출
      • [[set]] : 데이터 프로퍼티 값 저장할때 호출
      • [[enumerable]] : 데이터 프로퍼티 enumerable과 일치
      • [[configurable]] : 데이터 프로퍼티 configurable과 일치
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const person = {
    // 데이터 프로퍼티
    firstName : ‘Jango’,
    lastName : ‘Han’
 
    // 접근자 프로퍼티
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }
set fullName(name) {
        [this.firstName, this.lastName] = name.split(‘ ‘);
    }
}
 
console.log(person.fullName); // Jango Han
person.fullName = ‘HY L’
console.log(person.firstName); // HY
console.log(person.lastName); // L
cs

출처

  • 모던 자바스크립트 Deep Dive[이웅모 지음], Ch.16

+ Recent posts