JavaScript: `this`가 이렇게 작동하는 이유는 무엇입니까?

9981 단어 oopwebdevjavascript
"나는 JavaScript에 대해 이것을 싫어합니다.", "제로 이득을 위해 너무 많은 혼란과 많은 버그를 만듭니다.", "고장났어요, 사용하지 마세요!"이것이 많은 JavaScript 개발자가 this 키워드에 대해 생각하는 것입니다. 그들 중 많은 사람들에게 this는 확실히 더 복잡한 앱에서 가장 문제를 일으켰습니다.

다른 컨텍스트에서 키워드의 의미에 대한 많은 기사가 있지만 지금은 this 작동 방식을 설명하여 더 잘 이해할 수 있도록 하고 싶습니다.

우선 자바스크립트 객체 시스템이 프로토타입을 기반으로 한다는 것을 기억하자. 프로토타입이란 무엇입니까? 실제로는 다른 개체에 의해 "상속"될 수 있는 개체일 뿐입니다. 프로토타입은 단순한 객체이므로 프로토타입 자체를 가질 수 있습니다.

주어진 개체의 속성이나 메서드에 액세스하려고 하면 먼저 개체 자체의 속성을 검색합니다. 찾을 수 없으면 개체의 프로토타입을 검색합니다. 그래도 찾을 수 없으면 프로토타입의 프로토타입을 검색합니다. 그런 다음 속성을 찾을 때까지 계속 검색합니다. 어디에서도 속성을 찾을 수 없으면 undefined 입니다.

예를 들어 보겠습니다.

function DogThatQuacks(name) {
    this.name = name
}
DogThatQuacks.prototype.bark = function() {
    return `${this.name} says "Quack!"`
}

const bartholomew = new DogThatQuacks('Bartholomew')

// Outputs 'Bartholomew says "Quack!"'
bartholomew.bark() 


마지막 줄에서 JavaScript 엔진은 먼저 개체bartholomewbark 메서드가 있는지 검색합니다. name가 유일한 속성이므로 프로토타입을 조사합니다. 그곳에서 메소드를 찾고 마지막으로 DogThatQuacks.prototype.bark 를 실행합니다.

문제는 메서드barkDogThatQuacks.prototype가 아니라 객체bartholomew에 있다는 것입니다. 메소드는 어떻게 접근할 수 있습니까bartholomew.name? this의 값은 함수를 호출하는 방법에 따라 다르기 때문입니다.

결국 메서드DogThatQuacks.prototype.bark를 호출하지만 개체bartholomew의 메서드로 호출합니다. 그런 이유로 이 경우 thisbartholomew를 참조합니다. 이제 조금 더 플레이해 보겠습니다.

// Outputs 'undefined says "Quack!"'
DogThatQuacks.prototype.bark()

// Outputs 'undefined says "Quack!"', but
// it throws an error in strict mode
const bark = bartholomew.bark
bark()


첫 번째 예에서는 DogThatQuacks.prototype.bark를 직접 호출합니다! 짐작할 수 있듯이 thisname 속성이 없는 프로토타입 자체에 대한 참조입니다.

그리고 두 번째 경우에는 엄격 모드를 사용하는 경우 오류가 발생하고 "undefined가 Quack을 말합니다!"엄격 모드가 아닌 경우. 왜요? bark를 객체의 메소드로 호출하지 않기 때문에 간단한 함수로 호출하는 것입니다.

엄격 모드에서 함수를 호출할 때 this가 정의되지 않습니다. 그리고 엄격 모드가 활성화되어 있지 않으면 전역 개체를 참조합니다. 다시 말하지만 this 값은 함수를 호출하는 방법에 따라 다릅니다.

더 많은 예:

function makeDogBark(barkMethod) {
    console.log(barkMethod())
}
// Outputs 'undefined says "Quack!"', but
// it throws an error in strict mode
makeDogBark(bartholomew.bark)


DogThatQuacks.prototype.actuallyBark = function() {
    const internalFunction = function() {
        return `${this.name} now says "Woof!"`
    }

    return internalFunction()
}

// Outputs 'undefined now says "Woof!"', but
// it throws an error in strict mode
bartholomew.actuallyBark()


첫 번째 예에서는 bartholomew.bark 를 함수 makeDogBark 에 인수로 전달합니다. 그러나 함수는 인수barkMethod, 즉 단순 함수를 호출합니다.

두 번째 경우에는 다시 단순 함수internalFunction를 호출하므로 엄격 모드가 활성화되었는지 여부에 따라 정의되지 않거나 전역 객체입니다.

또한 모든 것이 수업에도 적용된다는 점을 고려해야 합니다. 이것이 JavaScript의 클래스가 프로토타입을 위한 구문 설탕인 이유입니다.

class CatThatSaysMoo {
    constructor(name) {
        this.name = name
    }
    meow() {
        return `${this.name} says "Moo!"`
    }
}
const florence = new CatThatSaysMoo('Florence')

// Outputs 'Florence says "Moo!"'
florence.meow()

// Outputs 'undefined says "Moo!"'
CatThatSaysMoo.prototype.meow()

const meowFunction = florence.meow

// Throws an error, `this` is undefined
meowFunction()


메서드를 함수에 인수로 전달해야 하거나 변수에 메서드를 저장해야 하는 경우 화살표 함수(부모 범위에서 this를 "상속") 또는 bind 방법:

DogThatQuacks.prototype.actuallyBark = function() {
    const internalFunction = () => {
        // It inherits the `this` from
        // `DogThatQuacks.prototype.actuallyBark`
        return `${this.name} now says "Woof!"`
    }

    return internalFunction()
}

// Outputs 'Bartholomew now says "Woof!"'
bartholomew.actuallyBark()


// If fixes `this` as a reference
// to the object `florence`
const meowFunction = florence.meow.bind(florence)
// Outputs 'Florence says "Moo!"'
meowFunction()



추신. 읽은 내용이 마음에 드셨나요? 매주 저는 더 나은 JavaScript 개발자가 되기 위한 무료 팁과 통찰력이 포함된 이메일을 보냅니다. 관심이 있으시면 click here to subscribe .

좋은 웹페이지 즐겨찾기