JS) new.target

kangdari·2020년 3월 31일
1

기존 ES5 문법

function Person (name) {
    if(this instanceof Person) {
        this.name = name
    } else{
        throw new Error('new 연산자를 사용하세요')
    }
}

Person('kang') // Uncaught Error: new 연산자를 사용하세요
new Person('kang') // Person {name: 2}

Person을 함수로 호출하면 에러가 발생하고 new Person으로 호출하도록 강제하는 방법이다.
그냥 Person으로 호출할 경우 this는 Window이므로 Person의 instance가 아니고,
new Person으로 호출할 경우 this는 Person의 instance 입니다.

const p1 = new Person('kang') // Person {name: "kang"}

const p2 = Person('kang') // Uncaught Error: new 연산자를 사용하세요

const p3 = Person.call({}, 'hyeok') // Uncaught Error: new 연산자를 사용하세요

const p4 = Person.call(p1, 'hyeok') // undefined
p1 // Person {name: "hyeok"}

p1의 경우 new 연산자를 사용했기 때문에 오류가 없습니다.
p2의 경우 new 연산자를 사용하지 않았기 때문에 오류가 발생
p3의 경우 this의 값이 null이므로 this는 Person의 instance가 아니므로 오류가 발생
p4의 경우 p1은 Person의 instance입니다. 그렇다면 this는 p1이므로 this.name = name의 결과 p1의 name 값이 hyeok이 됩니다. p4는 if(this instanceof Person)에서 return하는 값이 없으므로 undefined입니다.

p4의 경우 new 연산자를 사용하지 않고도 위 함수가 실행되는 예외적인 경우가 발생합니다.
그래서 ES6에서 new.target가 등장했습니다.

new.target

new.target은 new 라는 객체의 프로퍼티가 아닙니다.
이것은 함수 생성 시 생성되는 것으로 new.target 자체가 내부에서만 작동하는 하나의 변수입니다. new 연산자를 사용하여 함수를 실행했을 때 그 실행한 함수 자체가 new.target이 됩니다.

function Person (name) {
    if(new.target !== undefined) {
        this.name = name
    } else {
        throw new Error('new 연산자를 사용하세요')
    }
}

Person('kang') // Uncaught Error: new 연산자를 사용하세요
new Person('kang') // ƒ Person(name) , new.target: Person('kang')
const p1 = new Person('kang') // Person {name: "kang"}

const p4 = Person.call(p1, 'hyeok') // undefined ,Uncaught Error: new 연산자를 사용하세요

p1의 경우 new 연산자를 사용했으므로 정상적으로 작동합니다.
p4의 경우 new 연산자를 사용하지 않아 new.target이라는 대상 자체가 없으므로 오류가 발생합니다.

arrow function에서 new.target

arrow functino은 this, super, new.target을 바인딩 하지 않습니다.

function Person (name) {
    const af = n => {
        this.name = n
        console.log(new.target)
    }
    af(name)
}
const p1 = new Person('kang') // ƒ Person (name) { ...

const p2 = Person('kang') // undefined

arrow funciton인 af는 this와 new.target 모두 바인딩 하지 않으므로 외부 함수인 Person에서 값을 가져옵니다.
그래서 p1의 경우 new.target은 Person('kang') 입니다.
p2의 경우 new 연산자를 사용하지 않았으므로 undefined 입니다.

function Person(name) {
    this.name = name
}
function Android(name) {
    Person.call(this, name)
}
const p1 = new Android('kang') // Android {name: "kang"}

new Android('kang') Android 인스턴스를 생성하였는데 이미 {name: "kang"} 프로퍼티값이 설정되었습니다.
이는 Android(name) 생성자 안에서 Person을 생성자가 아닌 함수로써 그냥 실행시킵니다.


다시 new.target을 사용해보겠습니다.

function Person (name) {
    console.dir(new.target)
    if(new.target !== undefined) {
    // if(new.target === Person) {
        this.name = name
    } else {
        throw new Error('new 연산자를 사용하세요')
    }
}

function Android (name) {
    Person.call(this, name)
}

const p1 = new Android('kang') // undefined, Uncaught Error: new 연산자를 사용하세요

function Android (name) 생성자에서 new 연산자를 사용하지 않았으므로 오류가 발생합니다.

new.target 활용

class

class A { // 추상클래스처럼 흉내
    constructor() {
        if(new.target === A) { throw new Error('A는 추상클래스입니다.')}
        console.log(new.target)
    }
}
class B extends A {
    constructor () {
        super();
    }
}

const b = new B()
const a = new A() // Uncaught Error: A는 추상클래스입니다.

A를 클래스로 호출 불가, 오직 B에 의해서만 호출이 가능하도록 설정하여 A를 추상클래스처럼 흉내낼 수 있습니다.

0개의 댓글