ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JavaScrip] 프로토타입 - 1
    JavaScript 2023. 1. 3. 17:52
    728x90

    자바스크립트는 명령형, 함수형, 프로토타입 기반 객체지향 프로그래밍을 지원하는 멀티 패러다임 프로그래밍 언어이다. 자바스크립트는 클래스 기반 객체지향 프로그래밍 언어보다 효율적이다.

    자바스크립트는 원시 타입의 값을 제외한 ‘모든 것’이 객체라고 할 수 있다.

    객체지향 프로그래밍

    객체지향 프로그래밍은 독립적 단위인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임을 말합니다.

    객체지향 프로그래밍은 실체를 인식하는 철학적 사고를 프로그래밍에 접목하려는 시도에서 시작합니다. 따라서, 실제는 특징이나 성질을 나타내는 속성을 가지고 있고, 이를 통해 인식하거나 구별합니다.

    예를 들어, 사람은 이름, 주소, 성별 등 다양한 속성을 갖습니다. 이름 xx 성별 xx 나이 xx 인 사람으로 구체적인 속성을 표현하면 사람을 구별하여 인식할 수 있습니다. 이것을 프로그래밍에 접목시키면 우리가 구현하는 프로그래밍에서 사람의 ‘이름’과 ‘주소’에 관심이 있어 ‘이름’, ‘주소’로 간추려 내어 표현하는 것을 추상화라고 합니다.

    // 이름과 주소 속성을 갖는 객체
    const person = {
      name: 'Lee',
      address: 'Seoul'
    };
    
    console.log(person); // {name: "Lee", address: "Seoul"}
    
    • 객체 : 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조
    • 객체지향 프로그래밍 : 독립적인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임

    원(Circle)이라는 개념을 객체로 만들어보겠습니다.

    원에는 반지름이라는 속성이 있습니다. 우리는 이 반지름을 통해 지름, 둘레, 넓이를 구할 수 있습니다.

    • 상태를 나타내는 데이터 : 반지름
    • 동작 : 원의 지름, 둘레, 넓이
    const circle = {
      radius: 5, // 반지름
    
      // 원의 지름: 2r
      getDiameter() {
        return 2 * this.radius;
      },
    
      // 원의 둘레: 2πr
      getPerimeter() {
        return 2 * Math.PI * this.radius;
      },
    
      // 원의 넓이: πrr
      getArea() {
        return Math.PI * this.radius ** 2;
      }
    };
    
    console.log(circle);
    // {radius: 5, getDiameter: ƒ, getPerimeter: ƒ, getArea: ƒ}
    
    console.log(circle.getDiameter());  // 10
    console.log(circle.getPerimeter()); // 31.41592653589793
    console.log(circle.getArea());      // 78.53981633974483
    

    상속과 프로토타입

    • 상속 : 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것
    // 생성자 함수
    function Circle(radius) {
      this.radius = radius;
      this.getArea = function () {
        // Math.PI는 원주율을 나타내는 상수다.
        return Math.PI * this.radius ** 2;
      };
    }
    
    // 반지름이 1인 인스턴스 생성
    const circle1 = new Circle(1);
    // 반지름이 2인 인스턴스 생성
    const circle2 = new Circle(2);
    
    // Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을 하는
    // getArea 메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다.
    // getArea 메서드는 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 바람직하다.
    console.log(circle1.getArea === circle2.getArea); // false
    
    console.log(circle1.getArea()); // 3.141592653589793
    console.log(circle2.getArea()); // 12.566370614359172
    

    Circle 생성자가 생성하는 모든 객체는 radius 프로퍼티와 getArea 메서드를 갖습니다.

    radius 프로퍼티의 값은 인스턴스마다 다르지만 getArea 메서드는 동일합니다.

    하지만 인스턴스를 생성할 때 마다 getArea 메서드를 중복 생성하고 소유하기 때문에 메모리를 불필요하게 낭비하게 됩니다.

    자바스크립트는 프로토타입을 기반으로 이를 해결할 수 있습니다.

    // 생성자 함수
    function Circle(radius) {
      this.radius = radius;
    }
    
    // Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를
    // 공유해서 사용할 수 있도록 프로토타입에 추가한다.
    // 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다.
    Circle.prototype.getArea = function () {
      return Math.PI * this.radius ** 2;
    };
    
    // 인스턴스 생성
    const circle1 = new Circle(1);
    const circle2 = new Circle(2);
    
    // Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는
    // 프로토타입 Circle.prototype으로부터 getArea 메서드를 상속받는다.
    // 즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유한다.
    console.log(circle1.getArea === circle2.getArea); // true
    
    console.log(circle1.getArea()); // 3.141592653589793
    console.log(circle2.getArea()); // 12.566370614359172
    

    circle1, circle2는 radius 프로퍼티만 소유하고 있고, getArea는 프로토타입에게 상속받아 사용할 수 있습니다.

    프로토타입 객체

    프로토타입 객체는 상속을 구현하기 위해서 사용됩니다.

    모든 객체는 [[Protorype]]이라는 내부 슬롯을 가지게 되며, null일 수 있습니다.

    proto 접근자 프로퍼티

    모든 객체는 proto 접근자 프로퍼티를 통해 자신의 프로터타입 [[Prototype]] 내부 슬롯에 간접적으로 접근할 수 있다.

    proto 는 접근자 프로퍼티이다.

    [[Get]], [[Set]] 프로퍼티 어트리뷰트로 구성된 프로퍼티이다.

    proto 접근자 프로퍼티는 상속을 통해 사용된다.

    const person = { name: 'Lee' };
    
    // person 객체는 __proto__ 프로퍼티를 소유하지 않는다.
    console.log(person.hasOwnProperty('__proto__')); // false
    
    // __proto__ 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype의 접근자 프로퍼티다.
    console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__'));
    // {get: ƒ, set: ƒ, enumerable: false, configurable: true}
    
    // 모든 객체는 Object.prototype의 접근자 프로퍼티 __proto__를 상속받아 사용할 수 있다.
    console.log({}.__proto__ === Object.prototype); // true
    

    proto 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유

    상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위함입니다.

    const parent = {};
    const child = {};
    
    // child의 프로토타입을 parent로 설정
    child.__proto__ = parent;
    // parent의 프로토타입을 child로 설정
    parent.__proto__ = child; // TypeError: Cyclic __proto__ value
    

    proto 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.

    직접 상속을 통해 Object.prototype을 상속받지 않은 객체를 생성할 수 있기 때문에 proto 접근자 프로퍼티를 사용할 수 없는 경우가 있다.

    // obj는 프로토타입 체인의 종점이다. 따라서 Object.__proto__를 상속받을 수 없다.
    const obj = Object.create(null);
    
    // obj는 Object.__proto__를 상속받을 수 없다.
    console.log(obj.__proto__); // undefined
    
    // 따라서 __proto__보다 Object.getPrototypeOf 메서드를 사용하는 편이 좋다.
    console.log(Object.getPrototypeOf(obj)); // null
    
    • 프로토타입의 참조를 취득 : Object.getPrototypeOf
    • 프로토타입 교체 : Object.setPrototypeOf
    const obj = {};
    const parent = { x: 1 };
    
    // obj 객체의 프로토타입을 취득
    Object.getPrototypeOf(obj); // obj.__proto__;
    // obj 객체의 프로토타입을 교체
    Object.setPrototypeOf(obj, parent); // obj.__proto__ = parent;
    
    console.log(obj.x); // 1
    

    함수 객체의 prototype 프로퍼티

    함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.

    // 함수 객체는 prototype 프로퍼티를 소유한다.
    (function () {}).hasOwnProperty('prototype'); // -> true
    
    // 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
    ({}).hasOwnProperty('prototype'); // -> false
    

    non-constructor 인 화살표 함수와, 메서드 축약 표현으로 정의한 메서드는 프로토타입을 생성하지 않는다.

    // 화살표 함수는 non-constructor다.
    const Person = name => {
      this.name = name;
    };
    
    // non-constructor는 prototype 프로퍼티를 소유하지 않는다.
    console.log(Person.hasOwnProperty('prototype')); // false
    
    // non-constructor는 프로토타입을 생성하지 않는다.
    console.log(Person.prototype); // undefined
    
    // ES6의 메서드 축약 표현으로 정의한 메서드는 non-constructor다.
    const obj = {
      foo() {}
    };
    
    // non-constructor는 prototype 프로퍼티를 소유하지 않는다.
    console.log(obj.foo.hasOwnProperty('prototype')); // false
    
    // non-constructor는 프로토타입을 생성하지 않는다.
    console.log(obj.foo.prototype); // undefined
    

    접근자 프로퍼티와 함수 객체만이 가지는 prototype 프로퍼티는 결국 동일한 프로토타입을 가리키지만 사용하는 주체가 다르다.

    구분 소유 값 사용 주체 사용 목적

    prototype        
    접근자 프로퍼티 모든 객체 프로토타입의 참조 모든 객체 객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용
    prototype        
    프로퍼티 constructor 프로토타입의 참조 생성자 함수 생성자 함수가 자신이 생성할 객체의 프로토타입을 할당하기 위해 사용
    // 생성자 함수
    function Person(name) {
      this.name = name;
    }
    
    const me = new Person('Lee');
    
    // 결국 Person.prototype과 me.__proto__는 결국 동일한 프로토타입을 가리킨다.
    console.log(Person.prototype === me.__proto__);  // true
    

    프로토타입의 constructor 프로퍼티와 생성자 함수

    모든 프로토타입은 constructor 프로퍼티를 갖는다.

    // 생성자 함수
    function Person(name) {
      this.name = name;
    }
    
    const me = new Person('Lee');
    
    // me 객체의 생성자 함수는 Person이다.
    console.log(me.constructor === Person);  // true
    
    const you = new me.constructor('Park') // 가능
    

    me 객체는 Person 생성자 함수를 통해 프로토타입의 constructor 프로퍼티를 통해 생성자 함수와 연결된다. me 객체에는 constructor 프로퍼티가 없지만 프로토아입인 Person.prototype의 constructor 프로퍼티를 상속받아 사용할 수 있다.

    리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입

    new 연산자와 함께 생성자 함수를 호출하여 인스턴스를 생성하지 않고 리터럴 표기법에 의한 객체 생성 방식이 있다.

    리터럴 생성 방식

    // 객체 리터럴
    const obj = {};
    
    // 함수 리터럴
    const add = function (a, b) { return a + b; };
    
    // 배열 리터럴
    const arr = [1, 2, 3];
    
    // 정규표현식 리터럴
    const regexp = /is/ig;
    

    리터럴 표기법에 의해 생성된 객체도 프로토타입이 존재합니다. 하지만 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 함수라고 단정 지을 수 없습니다.

    • Object 생성자 함수로 객체 생성

    2번에서 value를 undecined 나 null로 전달하면 OrdinaryObjectCreate를 호출하여 Object.prototype을 프로토타입으로 갖는 빈 객체를 생성한다고 되어있다.

    // 2. Object 생성자 함수에 의한 객체 생성
    // 인수가 전달되지 않았을 때 추상 연산 OrdinaryObjectCreate를 호출하여 빈 객체를 생성한다.
    let obj = new Object();
    console.log(obj); // {}
    
    // 1. new.target이 undefined나 Object가 아닌 경우
    // 인스턴스 -> Foo.prototype -> Object.prototype 순으로 프로토타입 체인이 생성된다.
    class Foo extends Object {}
    new Foo(); // Foo {}
    
    // 3. 인수가 전달된 경우에는 인수를 객체로 변환한다.
    // Number 객체 생성
    obj = new Object(123);
    console.log(obj); // Number {123}
    
    // String  객체 생성
    obj = new Object('123');
    console.log(obj); // String {"123"}
    
    • 객체리터럴로 객체 생성

    객체 리터럴이 평가될 때에도 위와 같이 OrdinaryObjectCreate 를 호출하여 빈 객체를 생성하고 프로퍼티 주기를 추가하도록 정의 되어 있다.

    constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정지을 수는 없습니다. 하지만, 생성 과정에서 미묘한 차이만 있을뿐 결국 객체로서 동일한 특성을 가지고 있기 때문에 프로토타입의 contstructor 프로퍼티를 통해 연결되어 있는 생성자 함수를 리터럴 표기법으로 생성한 객체를 생성한 생성자 함수로 생각해도 크게 무리가 없습니다.

    자바스크립트는 원시 타입의 값을 제외한 ‘모든 것’이 객체라고 할 수 있다.

    객체지향 프로그래밍

    객체지향 프로그래밍은 독립적 단위인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임을 말합니다.

    객체지향 프로그래밍은 실체를 인식하는 철학적 사고를 프로그래밍에 접목하려는 시도에서 시작합니다. 따라서, 실제는 특징이나 성질을 나타내는 속성을 가지고 있고, 이를 통해 인식하거나 구별합니다.

    예를 들어, 사람은 이름, 주소, 성별 등 다양한 속성을 갖습니다. 이름 xx 성별 xx 나이 xx 인 사람으로 구체적인 속성을 표현하면 사람을 구별하여 인식할 수 있습니다. 이것을 프로그래밍에 접목시키면 우리가 구현하는 프로그래밍에서 사람의 ‘이름’과 ‘주소’에 관심이 있어 ‘이름’, ‘주소’로 간추려 내어 표현하는 것을 추상화라고 합니다.

    // 이름과 주소 속성을 갖는 객체
    const person = {
      name: 'Lee',
      address: 'Seoul'
    };
    
    console.log(person); // {name: "Lee", address: "Seoul"}
    
    • 객체 : 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조
    • 객체지향 프로그래밍 : 독립적인 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임

    원(Circle)이라는 개념을 객체로 만들어보겠습니다.

    원에는 반지름이라는 속성이 있습니다. 우리는 이 반지름을 통해 지름, 둘레, 넓이를 구할 수 있습니다.

    • 상태를 나타내는 데이터 : 반지름
    • 동작 : 원의 지름, 둘레, 넓이
    const circle = {
      radius: 5, // 반지름
    
      // 원의 지름: 2r
      getDiameter() {
        return 2 * this.radius;
      },
    
      // 원의 둘레: 2πr
      getPerimeter() {
        return 2 * Math.PI * this.radius;
      },
    
      // 원의 넓이: πrr
      getArea() {
        return Math.PI * this.radius ** 2;
      }
    };
    
    console.log(circle);
    // {radius: 5, getDiameter: ƒ, getPerimeter: ƒ, getArea: ƒ}
    
    console.log(circle.getDiameter());  // 10
    console.log(circle.getPerimeter()); // 31.41592653589793
    console.log(circle.getArea());      // 78.53981633974483
    

    상속과 프로토타입

    • 상속 : 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것
    // 생성자 함수
    function Circle(radius) {
      this.radius = radius;
      this.getArea = function () {
        // Math.PI는 원주율을 나타내는 상수다.
        return Math.PI * this.radius ** 2;
      };
    }
    
    // 반지름이 1인 인스턴스 생성
    const circle1 = new Circle(1);
    // 반지름이 2인 인스턴스 생성
    const circle2 = new Circle(2);
    
    // Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을 하는
    // getArea 메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다.
    // getArea 메서드는 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 바람직하다.
    console.log(circle1.getArea === circle2.getArea); // false
    
    console.log(circle1.getArea()); // 3.141592653589793
    console.log(circle2.getArea()); // 12.566370614359172
    

    Circle 생성자가 생성하는 모든 객체는 radius 프로퍼티와 getArea 메서드를 갖습니다.

    radius 프로퍼티의 값은 인스턴스마다 다르지만 getArea 메서드는 동일합니다.

    하지만 인스턴스를 생성할 때 마다 getArea 메서드를 중복 생성하고 소유하기 때문에 메모리를 불필요하게 낭비하게 됩니다.

    자바스크립트는 프로토타입을 기반으로 이를 해결할 수 있습니다.

    // 생성자 함수
    function Circle(radius) {
      this.radius = radius;
    }
    
    // Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를
    // 공유해서 사용할 수 있도록 프로토타입에 추가한다.
    // 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다.
    Circle.prototype.getArea = function () {
      return Math.PI * this.radius ** 2;
    };
    
    // 인스턴스 생성
    const circle1 = new Circle(1);
    const circle2 = new Circle(2);
    
    // Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는
    // 프로토타입 Circle.prototype으로부터 getArea 메서드를 상속받는다.
    // 즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유한다.
    console.log(circle1.getArea === circle2.getArea); // true
    
    console.log(circle1.getArea()); // 3.141592653589793
    console.log(circle2.getArea()); // 12.566370614359172
    

    circle1, circle2는 radius 프로퍼티만 소유하고 있고, getArea는 프로토타입에게 상속받아 사용할 수 있습니다.

    프로토타입 객체

    프로토타입 객체는 상속을 구현하기 위해서 사용됩니다.

    모든 객체는 [[Protorype]]이라는 내부 슬롯을 가지게 되며, null일 수 있습니다.

    proto 접근자 프로퍼티

    모든 객체는 proto 접근자 프로퍼티를 통해 자신의 프로터타입 [[Prototype]] 내부 슬롯에 간접적으로 접근할 수 있다.

    proto 는 접근자 프로퍼티이다.

    [[Get]], [[Set]] 프로퍼티 어트리뷰트로 구성된 프로퍼티이다.

    proto 접근자 프로퍼티는 상속을 통해 사용된다.

    const person = { name: 'Lee' };
    
    // person 객체는 __proto__ 프로퍼티를 소유하지 않는다.
    console.log(person.hasOwnProperty('__proto__')); // false
    
    // __proto__ 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype의 접근자 프로퍼티다.
    console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__'));
    // {get: ƒ, set: ƒ, enumerable: false, configurable: true}
    
    // 모든 객체는 Object.prototype의 접근자 프로퍼티 __proto__를 상속받아 사용할 수 있다.
    console.log({}.__proto__ === Object.prototype); // true
    

    proto 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유

    상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위함입니다.

    const parent = {};
    const child = {};
    
    // child의 프로토타입을 parent로 설정
    child.__proto__ = parent;
    // parent의 프로토타입을 child로 설정
    parent.__proto__ = child; // TypeError: Cyclic __proto__ value
    

    proto 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.

    직접 상속을 통해 Object.prototype을 상속받지 않은 객체를 생성할 수 있기 때문에 proto 접근자 프로퍼티를 사용할 수 없는 경우가 있다.

    // obj는 프로토타입 체인의 종점이다. 따라서 Object.__proto__를 상속받을 수 없다.
    const obj = Object.create(null);
    
    // obj는 Object.__proto__를 상속받을 수 없다.
    console.log(obj.__proto__); // undefined
    
    // 따라서 __proto__보다 Object.getPrototypeOf 메서드를 사용하는 편이 좋다.
    console.log(Object.getPrototypeOf(obj)); // null
    
    • 프로토타입의 참조를 취득 : Object.getPrototypeOf
    • 프로토타입 교체 : Object.setPrototypeOf
    const obj = {};
    const parent = { x: 1 };
    
    // obj 객체의 프로토타입을 취득
    Object.getPrototypeOf(obj); // obj.__proto__;
    // obj 객체의 프로토타입을 교체
    Object.setPrototypeOf(obj, parent); // obj.__proto__ = parent;
    
    console.log(obj.x); // 1
    

    함수 객체의 prototype 프로퍼티

    함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.

    // 함수 객체는 prototype 프로퍼티를 소유한다.
    (function () {}).hasOwnProperty('prototype'); // -> true
    
    // 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
    ({}).hasOwnProperty('prototype'); // -> false
    

    non-constructor 인 화살표 함수와, 메서드 축약 표현으로 정의한 메서드는 프로토타입을 생성하지 않는다.

    // 화살표 함수는 non-constructor다.
    const Person = name => {
      this.name = name;
    };
    
    // non-constructor는 prototype 프로퍼티를 소유하지 않는다.
    console.log(Person.hasOwnProperty('prototype')); // false
    
    // non-constructor는 프로토타입을 생성하지 않는다.
    console.log(Person.prototype); // undefined
    
    // ES6의 메서드 축약 표현으로 정의한 메서드는 non-constructor다.
    const obj = {
      foo() {}
    };
    
    // non-constructor는 prototype 프로퍼티를 소유하지 않는다.
    console.log(obj.foo.hasOwnProperty('prototype')); // false
    
    // non-constructor는 프로토타입을 생성하지 않는다.
    console.log(obj.foo.prototype); // undefined
    

    접근자 프로퍼티와 함수 객체만이 가지는 prototype 프로퍼티는 결국 동일한 프로토타입을 가리키지만 사용하는 주체가 다르다.

    구분 소유 값 사용 주체 사용 목적

    prototype        
    접근자 프로퍼티 모든 객체 프로토타입의 참조 모든 객체 객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용
    prototype        
    프로퍼티 constructor 프로토타입의 참조 생성자 함수 생성자 함수가 자신이 생성할 객체의 프로토타입을 할당하기 위해 사용
    // 생성자 함수
    function Person(name) {
      this.name = name;
    }
    
    const me = new Person('Lee');
    
    // 결국 Person.prototype과 me.__proto__는 결국 동일한 프로토타입을 가리킨다.
    console.log(Person.prototype === me.__proto__);  // true
    

    프로토타입의 constructor 프로퍼티와 생성자 함수

    모든 프로토타입은 constructor 프로퍼티를 갖는다.

    // 생성자 함수
    function Person(name) {
      this.name = name;
    }
    
    const me = new Person('Lee');
    
    // me 객체의 생성자 함수는 Person이다.
    console.log(me.constructor === Person);  // true
    
    const you = new me.constructor('Park') // 가능
    

    me 객체는 Person 생성자 함수를 통해 프로토타입의 constructor 프로퍼티를 통해 생성자 함수와 연결된다. me 객체에는 constructor 프로퍼티가 없지만 프로토아입인 Person.prototype의 constructor 프로퍼티를 상속받아 사용할 수 있다.

    리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입

    new 연산자와 함께 생성자 함수를 호출하여 인스턴스를 생성하지 않고 리터럴 표기법에 의한 객체 생성 방식이 있다.

    리터럴 생성 방식

    // 객체 리터럴
    const obj = {};
    
    // 함수 리터럴
    const add = function (a, b) { return a + b; };
    
    // 배열 리터럴
    const arr = [1, 2, 3];
    
    // 정규표현식 리터럴
    const regexp = /is/ig;
    

    리터럴 표기법에 의해 생성된 객체도 프로토타입이 존재합니다. 하지만 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 함수라고 단정 지을 수 없습니다.

    • Object 생성자 함수로 객체 생성

    2번에서 value를 undecined 나 null로 전달하면 OrdinaryObjectCreate를 호출하여 Object.prototype을 프로토타입으로 갖는 빈 객체를 생성한다고 되어있다.

    // 2. Object 생성자 함수에 의한 객체 생성
    // 인수가 전달되지 않았을 때 추상 연산 OrdinaryObjectCreate를 호출하여 빈 객체를 생성한다.
    let obj = new Object();
    console.log(obj); // {}
    
    // 1. new.target이 undefined나 Object가 아닌 경우
    // 인스턴스 -> Foo.prototype -> Object.prototype 순으로 프로토타입 체인이 생성된다.
    class Foo extends Object {}
    new Foo(); // Foo {}
    
    // 3. 인수가 전달된 경우에는 인수를 객체로 변환한다.
    // Number 객체 생성
    obj = new Object(123);
    console.log(obj); // Number {123}
    
    // String  객체 생성
    obj = new Object('123');
    console.log(obj); // String {"123"}
    
    • 객체리터럴로 객체 생성

    객체 리터럴이 평가될 때에도 위와 같이 OrdinaryObjectCreate 를 호출하여 빈 객체를 생성하고 프로퍼티 주기를 추가하도록 정의 되어 있다.

    constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정지을 수는 없습니다. 하지만, 생성 과정에서 미묘한 차이만 있을뿐 결국 객체로서 동일한 특성을 가지고 있기 때문에 프로토타입의 contstructor 프로퍼티를 통해 연결되어 있는 생성자 함수를 리터럴 표기법으로 생성한 객체를 생성한 생성자 함수로 생각해도 크게 무리가 없습니다.

    728x90

    'JavaScript' 카테고리의 다른 글

    [JavaScript]프로토타입-3  (0) 2023.01.12
    [JavaScript]프로토타입-2  (0) 2023.01.05
    [JavaScrip] 함수와 일급 객체  (0) 2022.11.24
    [JavaScript] 생성자 함수에 의한 객체 생성  (0) 2022.11.23
    [백준]1132 : 합(javascript)  (0) 2022.07.25

    댓글

Designed by Tistory.