Language/javascript

[JavaScript] this, call, apply, bind

다닿 2023. 10. 17. 16:01

this 따로  지정해주지 않으면 지금의 실행 컨텍스트가 활성화된 객체를 바라본다고 볼 수 있을것같다.

 

전역 환경에서의 this는 node환경에선 global객체, 브라우저 환경에서는 window객체이다.

 

함수와 메서드

함수는 전역에서 선언되어 어디서든 호출 가능 함수의 this는 전역 객체

// 호출 방법
함수();

메서드는 자신을 가진 객체를 통해 호출 해야함 메서드의 this는 호출의 주체

// 호출 방법
객체.메서드();

 

 

var o1 = {
    outer: function() { // this는 o1
        console.log(this);	
        var innerfunc = function() {
            console.log(this); // this는 global
        }
        innerfunc();

        var o2 = { 
            innermethod : innerfunc // this는 o2
        }
        o2.innermethod();
    }
}
o1.outer();

outer도 함수를 참조한 메서드라서 outer에 참조한 함수가 호출될 때 this는 o1인가보다.

직접 호출 할수 있는 innerfunc의 this는 global로 나온다.

innerfunc 함수를 참조한 innermethod의 this는 o2로 나온다.

 

메서드로 만들지 않은 함수에서도 메서드의 this와 같은 값을 사용하고 싶을 때 내부 스코프에 존재하는 this를 별도의 변수에 할당하여 사용하는 방법이 있다.

var o1 = {
    outer: function() {
        console.log(this);

        var self = this; // o1을 저장해 놓고
        var innerfunc = function() {
            console.log(this);
            console.log(self); // innerfunc에서 o1호출
        }
        innerfunc();
    }
}
o1.outer();

self라는 변수를 만들어 this를 할당 해주면 innerfunc에서도 o1를 사용 할 수 있다.

 

일반 함수와 화살표 함수의 this

var o1 = {
    outer: function() {
        console.log(this);  // this o1

        var innerfunc1 = function() {   // 일반 함수 
            console.log(this);          // this global
        }
        innerfunc1();

        var innerfunc2 = () => {        // 화살표 함수
            console.log(this);          // this o1
        }
        innerfunc2();
    }
}
o1.outer();

화살표 함수에서는 this가 함수가 선언된 객체를 바라본다.

화살표 함수는 this를 binding 기능을 이미 하고 있다. binding은 아래에 설명 할 것.

 

생성자 함수 내부에서의 this

var 변수 = new 함수();

함수안의 this는 변수 object(함수도 object)를 new로 생성해서 할당 하면 this가 알아서 object를 binding 하는 것 같다.


명시적 this binding

call, apply, bind

 

  • call : this를 넘겨주면서 함수를 즉시실행한다.

call을 이용한 명시적 binding

var func = function(a,b,c) {
    console.log(this,a,b,c);
}

func(10,20,30); // this는 global
func.call({},50,60,70); // this는 {}

func({},20) 실행시

var obj = {
    a : 1,
    method : function(num) {
        console.log(this.a,num);
    }
}
obj.method(2);  // this는 obj
obj.method.call({a:10},20); // this는 {a:10}

유사배열객체에서 사용 (배열은아니지만 배열처럼 쓸 수 있는것 key값이 숫자고 length를 가지고 있으면 된다.)

var obj = {
    0:'a',
    1:'b',
    2:'c',
    length:3
}

Array.prototype.push.call(obj,'d');
console.log(obj);

var arr = Array.prototype.slice.call(obj);
console.log(arr);

var arr2 = Array.from(obj); // object to array
console.log(arr2);

생성자에서 사용하는 예제

function Person(name, gender) {
    this.name = name;
    this.gender = gender;
}

function Student(name,gender,school) {
    Person.call(this,name,gender);
    this.school = school;
}


function Employee(name,gender,company) {
    Person.call(this,name,gender);
    this.company = company;
}

var sp = Student("ㅁㅁㅁ","m","sky");
var sp = Employee("ㄴㄴㄴ","m","sam");

 

 

  • apply 

사용법은 call과 같다 다만 매개변수를 배열로 묶어서 참조해줘야 한다. apply도 즉시실행

var func = function(a,b,c) {
    console.log(this,a,b,c);
}

func(10,20,30); // this는 global
func.apply({},[50,60,70]); // this는 {}

결과는 위의 call을 사용했을 때와 같음.

사용예제

var numbers = [10,20,30,40];
var max = Math.max(...numbers);
console.log(max);
var max2 = Math.max.apply(null,numbers);
console.log(max);

 

 

  • bind : 즉시 실행하지 않음, this를 미리 적용해 놓음
var func = function(a,b,c,d) {
    console.log(this,a,b,c,d);
}
func(1,2,3,4); // global

// bind로 미리 적용 해 놓기
var bindFunc = func.bind({});
bindFunc(5,6,7,8); // {}

// bind로 부분 적용해 놓기
var bindFunc2 = func.bind({},9,10);
bindFunc2(7,8); // {}

// bind된 함수인지 알수 있다.
// name 프로퍼티
console.log(func.name);
console.log(bindFunc.name);
console.log(bindFunc2.name);


응용

 

위의 self를 이용하여 this를 사용한 예제를 call을 이용하여 수정

var o1 = {
    outer: function() {
        console.log(this);

        var innerfunc = function() {
            console.log(this);
        }
        innerfunc.call(this); // <------
    }
}
o1.outer();

this가 o1이 된 것을 확인 할 수 있다.

 

bind를 사용하여 수정

var o1 = {
    outer: function() {
        console.log(this);

        var innerfunc = function() {
            console.log(this);
        }.bind(this) // <------
        innerfunc();
    }
}
o1.outer();

결과는 같다.