this

Andy·2023년 8월 12일
0

자바스크립트

목록 보기
3/39
post-thumbnail

자바스크립트에서 this는 함수를 호출할때 결정됩니다.

자바스크립트의 모든 변수는 실은 특정 객체의 프로퍼티로 동작합니다.
this에는 호출할 주체에 대한 정보가 담김니다.
전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당합니다.

함수 vs 메서드

어떤 함수를 실행하는 방법은 크게 2가지로 나뉘어져 있는데 첫번째는 함수로서 호출하는것이고, 두번째는 메서드로서 호출하는 경우입니다.이 둘을 구분하는 유일한 차이는 독립성 입니다.
함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행합니다.

변수에 담아 호출할 경우와 객체의 프로퍼티에 할당해서 호출한 경우에 this가 달라집니다.

함수로써 호출할 때 그 함수 내부에서의 this

1.) 함수 내부에서의 this

var func= function(){
    console.log(this);
}
func(); //전역 객체를 호출

2.) 메서드의 내부함수에서의 this;

var obj={
    this: function(){
        console.log(this); //obj객체를 호출
    }
};
obj.this();
var obj={
    func: function(){
        var innerFunc= function(){
            console.log(this); //전역 객체를 호출
        }
        innerFunc();
    }
}
obj.func();

❗️이처럼 this 바인딩에 관해서는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지 등)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지 없는지가 중요하다.

메서드 내부 함수에서의 this를 우회하는 법

var obj={
    inner: function(){
        console.log(this);
        var self= this;
        var innerFunc= function(){
            console.log(self); //원래는 함수를 호출 하는 구문 앞에 점이 없음으로 전역 객체를 가리켜야 하지만 변수를 선언해 우회함
        }
        innerFunc();
    }
}
obj.inner();

this를 바인딩 하지 않는 함수

ES6에서 추가된 것으로 화살표 함수를 만들어 this를 바인딩 하지 않게 만듬

var obj={
    inner: function(){
        var innerFunc=()=>{
            console.log(this); //화살표 함수를 선언해서 this 바인딩 과정 자체를 빠지게 함
        }
        innerFunc();
    }
}
obj.inner();

3.) 콜백 함수 호출 시 그 함수 내부에서의 this

setTimeout(function(){
    console.log(this); //1초후에 전역 객체를 호출함
},1000)
[1,2,3,4,5].forEach(function(x){
    console.log(this,x); //1부터 5까지 전역 객체를 호출함
})

document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(value){
    console.log(this, console.log(value)); //자신의 this를 상속함
})
/* addEventListner는 지정한 HTML 엘리먼트에 'click' 이벤트가 발생할 때마다 그 이벤트 정보를 콜백 함수의
첫 번째 인자로 삼아 함수를 실행하는 명령입니다. 버튼을 클릭하면 앞서 지정한 엘리먼트와 클릭 이벤트에 관한 정보가 담긴 객체가 출력됩니다.
*/

❗️이처럼 콜백 함수에서의 this는 '무조건 이거다'라고 정의할 수 없습니다. 콜백 함수의 제어권을 가지는 함수(메서드)가 콜백 함수에서의 this를 무엇으로 할지를 결정하며, 특별히 정의하지 않은 경우에는 기본적으로 함수와 마찬가지로 전역객체를 바라본다.

4.) 생성자 함수 내부에서의 this

어떤 함수가 생성자 함수로서 호출된 경우 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 입니다.

var cat = function(name,age){
    this.bark="야옹";
    this.name=name;
    this.age= age;
};
var choco= new cat('초코',2);
console.log(choco);

명시적으로 this를 바인딩하는 법

1.call 메서드: 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령어. 첫번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 합니다.

var func= function(a,b,c){
    console.log(this,a,b,c);
}
func(1,2,3); //전역 객체, 1,2,3호출
func.call({x:1},1,2,3); // {x:1},1,2,3 호출

2.apply 메서드: 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정합니다.

var func =function(a,b,c){
    console.log(this,a,b);
}
func.apply({x:1},[2,3]); //{x:1},2,3 호출

call/apply 메서드의 활용

1.) 유사배열객체에 배열 메서드를 적용

var array={
    0: 'andy',
    1: 25,
    length: 2,
}
var arr= Array.prototype.slice.call(array);
console.log(arr); //['andy',25]출력

2.) arguments에 배열 메서드를 적용

function args(){
    var arr= Array.prototype.slice.call(arguments);
    console.log(arr); //[1,2,3] 출력
    arr.forEach(function(number){
        console.log(number);// 순차적으로 1,2,3 출력
    })
}
args(1,2,3);

3.)querySelectorAll 등의 Node 선택자로 선택한 결과인 nodeList에 배열 메서드를 적용

document.body.innerHTML='<div>a</div><div>b</div><div>c</div>';
var nodeList= document.querySelectorAll('div');
var arr= Array.prototype.slice.call(nodeList);
arr.forEach(function(number){
    console.log(number); //순차적으로 <div>태그 안에 a,b,c 출력
}) 

4.)문자열을 이용한 배열 메서드를 적용

var str ="andy";
var check=Array.prototype.every.call(str,function(char){
    return char==="a";
});
console.log(check); //false 출력
var str ="andy";
var check=Array.prototype.some.call(str,function(char){
    return char==="a"; // true 출력
});
console.log(check);
var map =Array.prototype.map.call(str, function(char){
    return char+'1'; //['a1', 'n1', 'd1', 'y1'] 출력
})

5.) 생성자 내부에서 다른 생성자를 호출
생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하면 간단하게 반복을 줄일수 있다.

function Person(name, gender){
    this.name=name;
    this.gender=gender;
};
function Student(name, gender,major){
    Person.call(this,name, gender)
    this.major=major;
}
var andy = new Student("andy", 'male', 'computerScience'); 

6.) 여러 인수를 묶어 하나의 배열로 전달하고 싶을 때 -apply 활용

//최대,최소값을 구하는 코드를 직접 구현
var numbers=[10,20,3,16,45];
var max= min=numbers[0];
numbers.forEach(function(number){
    if(number>max){
        max=number;
    }
    if(number<min){
        min=number;
    }
})
console.log(min, max);

이와같은 코드 보다는 math.max/math.min 메서드에 apply를 적용하면 훨씬 간단해진다.

var array=[2,123,121,12];
var min= Math.min.apply(null,array);
var max= Math.max.apply(null,array);
console.log(min, max);
//ES6의 펼치기 연산자 활용
var numbers=[123,122,1,12,-1];
var min = Math.min(...numbers);
var max= Math.max(...numbers);
console.log(min,max);

bind메서드

❗️bind메서드는 call 과 비슷하지만 즉시 호출하지는 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드

var func= function(a,b,c,d){
    console.log(this,a,b,c);
}
func.call(0,2,3,4); //[number:0],2,3,4 출력
//bind를 활용한 this지정 
var bindFunc = func.bind({x:1});
bindFunc(9,8,7); // {x:1},9,8,7 출력
//bind를 활용한 부분적용 함수 
var bind2Func = func.bind(0,3,4);
bind2Func(9,8,7); //[numbrt:0],3,4,9 출력 8,7,이 나오지 않는 이유는 위의 함수에서 매개변수로 a,b,c 이렇게 3개만 받게 하였기 때문  

❗️name프로퍼티

bind메서드를 적용해서 새로 만든 함수는 한 가지 독특한 성질이 있다. 바로 name프로퍼티에 동사 bind의 수동태인 bound라는 접두어가 붙는다는 점!
어떤 함수의 name프로퍼티가 bound ... 이라면 이는 곧 함수명이 ... 인 원본 함수에 bind메서드를 적용한 새로운 함수라는 의미

var func=function(a,b,c){
    console.log(this,a,b,c);
}
func.call(1,2,3,4); //[number:1],2,3,4 출력
console.log(func.name); //func 출력
var bind= func.bind(1);
bind(2,3,4);
console.log(bind.name); //bound func출력

위와 같이 func라는 원본 함수에 bind메서드를 적용한 새로운 새로운 함수

상위 컨텍스트의 내부함수나 콜백 함수에 전달하기

위에서 메서드의 this를 그대로 바라보게 하기 위한 방법으로 self 등의 변수를 활용한 우회법을 소개했는데, call또는 bind메서드를 이용한 방법도 있다.

//내부함수에 this전달-call 활용
var obj={
    inner: function(){
        console.log(this);
        var innerFunc= function(){
            console.log(this); //obj객체를 명시함
        }
        innerFunc.call(this); 
    }
};
obj.inner();
//내부함수에 this전달-apply 활용
var obj={
    inner: function(){
        console.log(this);
        var innerFunc=function(){
            console.log(this); //obj객체를 명시함
        }.bind(this); 
        innerFunc();
    }
}
obj.inner();

콜백 함수 내에서의 this에 관여하는 함수 또는 메서드에 대해서도 bind 메서드를 이용하면 this값을 사용자가 명시할 수 있음

var obj ={
    func: function(){
        console.log(this);
    },
    func2: function(){
        setTimeout(this.func,1000); //1초후에 전역 객체를 명시함
    },
    func3: function(){
        setTimeout(this.func.bind(this),2000); //2초후에 obj 객체를 가리킴
    },
    func4: function(){
        console.log(this);
    }
};
obj.func2();
obj.func3();

❗️별도의 인자로 this를 받는 경우(콜백 함수 내에서의 this)

콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 있다. 이러한 메서드의 thisArg값을 지정하면 콜백 함수 내부에서 this값을 원하는 대로 변경 가능. 거의 대부분 배열 메서드에 많이 포함되어있음.

var report ={
    sum :0,
    count: 0,
    add: function(){
        var args=Array.prototype.slice.call(arguments);
        args.forEach(function(number){
            this.sum+=number;
            ++this.count;
        },this);//콜백함수에서의 this는 forEach함수의 두 번째 인자로 전달해준 this가 바인딩 됩니다.
    },
    average: function(){
        console.log("이거:",this)
        return this.sum/this.count;
    }
}
report.add(1,3,9,3);
console.log(report.sum, report.count, report.average());
var obj ={
    count: 0,
    func: function(){
        var array = Array.prototype.slice.call(arguments);
        array.map(function(x){
            this.count++;
        },this)//콜백함수에서의 this는 map함수의 두 번째 인자로 전달해준 this가 바인딩 됩니다.
    },
    result: function(){
        console.log(this.count);
    }
};
obj.func(1,2,3);
obj.result();

이밖에도 thisArg를 인자로 받는 메서드는 많습니다.

Array.prototype.forEach();
Array.prototype.map();
Array.prototype.filter();
Array.prototype.some();
Array.prototype.every();
Array.prototype.find();
Array.prototype.find();
Array.prototype.flatMap();
Array.prototype.from();
Set.prototype.forEach();
Map.prototype.forEach();
profile
열정으로 가득 찬 개발자 꿈나무 입니다

0개의 댓글