ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript] this의 동적 바인딩, 정적 바인딩, call, this, apply, bind
    웹/JavaScript 2023. 1. 14. 10:14

    I. ⭐️ this - "이곳의~"

    • 기본적으로 자신이 속한 곳을 가리킴 - 문맥 context
    • 💡 함수의 호출 방식에 따라 가리키는 바가 달라짐 - 자바스크립트 특성



    1. 전역에서의 this

    console.log(this);

    💡 아래의 환경들에서 각각 실행해 볼 것

    • 브라우저의 콘솔창: Window 객체 - globalThis와 같음
    • Node.js의 REPL: global 객체 - globalThis와 같음
    • .js문서로 작성 후 실행 - 빈 객체
      • Node.js에서 각 .js 문서들은 이후 배울 모듈로서 실행되기 때문



    2. 함수 안에서의 this

    느슨한 모드와 엄격 모드에서 다르게 동작

    function func () {
    console.log(this); // globalThis
    }
    func();

     

    'use strict';
    function func () {
    console.log(this); // undefined
    }
    func();
    • 객체에 속하지 않은 함수에서는 this 사용이 의미 없음



    3. 객체 안에서의 this

    • ⚠️ 일단 화살표 함수 제외

    a. 객체 리터럴 - 해당 객체를 가리킴

    const obj = {
    x: 123,
    getX: function () {
    return this.x;
    }
    }
    console.log(obj.getX());



    b. 생성자 함수 - 생성될 인스턴스를 가리킴

    function Person (name, age) {
    this.name = name;
    this.age = age;
    this.introduce = function () {
    return `저는 ${this.name}, ${this.age}세입니다.`
    }
    }
    console.log(
    new Person('홍길동', 20).introduce()
    );



    c. 클래스 선언 - 생성될 인스턴스를 가리킴

    class YalcoChicken {
    constructor (name, no) {
    this.name = name;
    this.no = no;
    }
    introduce () {
    return `안녕하세요, ${this.no}${this.name}점입니다!`;
    }
    }
    console.log(
    new YalcoChicken('강남', 17).introduce()
    );




     

    II. ⭐ 동적 바인딩

    • 자바스크립트의 독특한 동작방식
    • this가 가리키는 대상이 함수의 호출 주체 또는 그 방식에 따라 달라짐

     

    const korean = {
    favorite: '김치',
    makeStew: function (isHot, pots) {
    return `${isHot ? '매운' : '순한'} ${this.favorite}찌개 ${pots}냄비`;
    }
    };
    const italian = {
    favorite: '피자'
    };

     

    console.log(
    korean.makeStew(true, 1)
    );

     

    // 이탈리아인에게 한국인이 찌개 끓이는 법을 알려줌
    italian.makeStew = korean.makeStew;

     

    console.log(
    italian.makeStew(false, 2)
    );
    • 💡 함수가 누가, 어떻게 호출되었는가에 따라 this가 가리키는 대상이 달라짐



    해결방법들

    1. call를 사용한 함수 호출

    console.log(
    italian.makeStew.call(korean, false, 2)
    );



    2. apply를 사용한 함수 호출

    console.log(
    italian.makeStew.apply(korean, [false, 2])
    );



    3. ⭐ bind를 사용한 this 대상 고정

    // ⭐ this가 바인딩된 새 함수를 만듦
    italian.makeRightStew = korean.makeStew.bind(korean);
    console.log(
    italian.makeRightStew(false, 2)
    );

     

    // 💡 추가 인자들까지 바인딩 가능
    italian.makeClassicStew = korean.makeStew.bind(korean, true, 1);
    console.log(
    italian.makeClassicStew()
    );



    4. 바인딩된 함수를 내보내는 함수

    const korean = {
    favorite: '김치',
    makeStew: function (isHot, pots) {
    return `${isHot ? '매운' : '순한'} ${this.favorite}찌개 ${pots}냄비`;
    },
    teachMakingStew: function () {
    return this.makeStew.bind(this);
    }
    };
    const italian = {
    favorite: '피자'
    };

     

    italian.makeStew = korean.teachMakingStew();
    console.log(
    italian.makeStew(false, 2)
    );



    5. 생성자 함수일 경우 - 함수 자체를 미리 인스턴스에 바인딩하기

    function Korean () {
    this.favorite = '김치';
    this.makeStew = function (isHot, pots) {
    return `${isHot ? '매운' : '순한'} ${this.favorite}찌개 ${pots}냄비`;
    };
    // 💡 여기서 바인딩을 고정시켜버림
    this.makeStew = this.makeStew.bind(this);
    }
    function Italian () {
    this.favorite = '피자';
    }

     

    const korean = new Korean();
    const italian = new Italian();
    italian.makeStew = korean.makeStew;

     

    console.log(
    italian.makeStew(false, 2)
    );



    call, apply, bind의 다른 활용

    const korean = {
    favorite: '김치',
    makeStew: function (isHot, pots) {
    return `${isHot ? '매운' : '순한'} ${this.favorite}찌개 ${pots}냄비`;
    }
    };
    console.log(
    // 💡 임의로 특수한 동작을 시킬 때
    korean.makeStew.call({favorite: '된장'}, true, 2)
    );



    // 객체들이 가져다 쓰도록 만든 함수
    function intro (job) {
    return `${this.name}(${this.age}세) - ${job}`
    }
    const hong = {
    name: '홍길동',
    age: 20
    };
    const jeon = {
    name: '전우치',
    age: 25
    };

     

    // 동적 바인딩 특성 활용
    hong.intro = intro;
    console.log(
    hong.intro('개발자')
    );

     

    console.log(intro.call(hong, '개발자'));
    console.log(intro.apply(jeon, ['사무직']));

     

    const introHong = intro.bind(hong, '개발자');
    console.log(
    introHong()
    );




     

    ⭐ 배열 메서드의 thisArg

    • 해당 강 다시보기
    • 콜백으로 주어진 함수 내에서 this가 가리킬 대상
    • 보통 콜백함수 다음 인자로 넣음
    function recommendForYou (me) {
    const products = [
    { sex: 'F', size: 'M' },
    { sex: 'M', size: 'L' },
    { sex: 'F', size: 'M' },
    { sex: 'U', size: 'S' },
    { sex: 'M', size: 'L' },
    { sex: 'F', size: 'S' },
    ];
    products
    .map((itm, idx) => {
    return { ...itm, idx }
    })
    // ⚠️ 화살표 함수 대신 function 선언 함수 사용 주목
    .filter(function ({sex, size}) {
    return ['U', this.sex].includes(sex)
    && size === this.size
    }, me) // 💡 thisArg
    .forEach(function ({idx}) {
    console.log(`${this.name}님, ${++idx}번은 어떠세요?`);
    }, me); // 💡 thisArg
    }

     

    const peter = {
    name: '피터',
    sex: 'M',
    size: 'L'
    };
    const jane = {
    name: '제인',
    sex: 'F',
    size: 'S'
    };

     

    recommendForYou(peter);
    recommendForYou(jane);

     

     

    객체의 메서드 종류별 비교

    const obj = {
    // function 선언 함수
    func1: function () { return true; },
    // 메서드
    func2 () { return true; },
    // 화살표 함수
    func3: () => true
    }
    console.log(
    obj.func1(),
    obj.func2(),
    obj.func3()
    );


    로그로 출력된 형태 비교

    console.log(obj.func1);
    console.log(obj.func2);
    console.log(obj.func3);


    생성자 역할 수행 여부

    const func1 = new obj.func1();
     
    const func2 = new obj.func2();
     
    const func2 = new obj.func2();
    • function 선언 함수만 생성자로 활용 가능 - 더 많은 기능이 있고 무겁다는 의미


    ES6 함수 구분

    생성자 역할프로토타입argumentssuper
    function 선언 함수
    메서드
    화살표 함수


    ⭐ 화살표 함수와 this

    • function 함수나 메서드의 동적 바인딩과 다르게 동작
    • 함수가 어디서 선언되었는가에 따름 - ⭐️ 가장 근접한 상위 스코프에 바인딩 고정
    • 즉 this를 정적으로 바인딩

    1. 객체 리터럴에서

    • ⚠️ 객체 리터럴의 화살표 함수는 가리키는 기본 스코프가 나머지 둘과 다름
    const obj = {
    x: 1,
    y: 2,
    func1: function () {
    console.log('1.', this);
    },
    func2 () {
    console.log('2.', this);
    },
    func3: () => {
    console.log('3.', this);
    }
    }
     
    // this가 해당 객체를 가리킴
    obj.func1();
    obj.func2();
    // 💡 this가 상위 스코프를 가리킴
    obj.func3();



    const outer = {
    a: true,
    b: false,
    func: function () {
    const inner = {
    x: 1,
    y: 2,
    func1: function () {
    console.log('1.', this);
    },
    func2 () {
    console.log('2.', this);
    },
    func3: () => {
    console.log('3.', this);
    }
    }
    // this가 inner를 가리킴
    inner.func1();
    inner.func2();
    // this가 outer를 가리킴
    inner.func3();
    }
    }
    outer.func();


    2. 생성자 함수와 클래스에서

    • 기본적으로는 가리키는 대상이 동일 (해당 인스턴스)

    ⭐ 동적으로 바인딩하는 타 방식과의 차이 확인

    • 찌개는 function 선언 함수와 메서드로
    • 볶음밥은 화살표 함수로

    function Korean () {
    this.favorite = '김치';
    this.makeStew = function (isHot) {
    return `${isHot ? '매운' : '순한'} ${this.favorite}찌개`;
    };
    this.fryRice = (isHot) => {
    return `${isHot ? '매운' : '순한'} ${this.favorite}볶음밥`;
    };
    }
    function Italian () {
    this.favorite = '피자';
    }
    const korean = new Korean();
    const italian = new Italian();
    console.log(korean.makeStew(true));
    console.log(korean.fryRice(true));
     
    italian.makeStew = korean.makeStew;
    italian.fryRice = korean.fryRice;
    console.log(italian.makeStew(false));
    console.log(italian.fryRice(false));



    // ♻️ 새로고침 후 실행
    class Korean {
    constructor () {
    this.favorite = '김치';
    this.fryRice = (isHot) => {
    return `${isHot ? '매운' : '순한'} ${this.favorite}볶음밥`;
    }
    }
    makeStew (isHot) {
    return `${isHot ? '매운' : '순한'} ${this.favorite}찌개`;
    }
    }
    class Italian {
    constructor () {
    this.favorite = '피자';
    }
    }
    const korean = new Korean();
    const italian = new Italian();
    console.log(korean.makeStew(true));
    console.log(korean.fryRice(true));
     
    italian.makeStew = korean.makeStew;
    italian.fryRice = korean.fryRice;
    console.log(italian.makeStew(false));
    console.log(italian.fryRice(false));


    💡 call, apply, bind의 this 인자 무시됨

    console.log(
    korean.fryRice.call({favorite: '된장'}, true)
    );
    console.log(
    korean.fryRice.apply({favorite: '된장'}, [true])
    );
    console.log(
    korean.fryRice.bind({favorite: '된장'}, true)()
    );




    💡 Node.js 파일 실행시 빈 객체가 출력된 이유

    • Node.js는 각 파일을 모듈 (추후 다룰 것, 기본적으로 객체) 로 만들어 실행
    • 파일 내 모든 코드는 모듈의 메서드 안으로 들어가 실행됨 - 즉 객체 내 함수의 코드가 됨

    Node.js 코드파일로 실행해 볼 것

    // this가 모듈 객체(현재 비어있음)를 가리킴
    console.log('0.', this);
    function func1 () {
    // this가 전역 객체를 가리킴
    console.log('1.', this);
    }
    function func2 () {
    'use strict';
     
    // this가 undefined를 가리킴
    console.log('2.', this);
    }
    const func3 = () => {
    // 💡 this가 모듈 객체(이 함수의 상위 스코프)를 가리킴
    console.log('3.', this);
    }
    func1();
    func2();
    func3();
     
Designed by Tistory.