this 너무 어려워 이해도 안돼 그래서 책 보고,, 각 잡고,, 공부해보려고 한다 아자자!
📌 this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다.
📌 자바스크립트 엔진에 의해 생성되며 코드 어디서든 참조 가능하다.
📌 this 바인딩은 함수호출방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.
//1. 전역에서는 전역객체인 window 를 가리킨다.
console.log(this) //window
//2. 일반 함수 내부에서 this는 window 를 가리킨다.
function square(num){
console.log(this);//window
return num*num;
}
//3. 메서드 내부에서 this 는 메서드를 호출한 객체를 가리킨다.
const person={
name:'Brie',
getName(){
console.log(this); //{name:"Brie", getName:f()}
return this.name;
}
}
//4. 생성자 함수 내부에서 This는 생성자 함수가 생성할 인스턴스를 가리킨다.
fuction Person(name){
this.name=name;
console.log(this); //Person{name:"brie"}
}
const me=new Person('brie');
함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위스코프를 결정하지만 this 바인딩은 호출 시점에 결정된다.
주의할 점은 동일한 함수도 다양한 방식으로 호출 할 수 있다는 것이다.
함수 호출 방법은 아래와 같다.
- 일반함수호출
- 메서드 호출
- 생성자 함수 호출
- apply, call, bind 메서드에 의한 간접호출
function foo(){
console.log(this);
}
//동일한 함수도 다양한 방식으로 호출할 수 있다.
//1. 일반 함수 호출
//foo 함수를 일반적인 방식으로 호출함
//foo 함수 내부의 this 는 전역 객체를 가리킴
foo(); //window
//2. 메서드 호출
// foo 함수를 프로퍼티 값으로 할당하여 호출함
// foo 함수 내부의 this 는 메서드를 호출한 객체 obj 를 가리킨다.
const obj = {foo};
obj.foo(); // obj
//3. 생성자 함수 호출
//생성자 함수가 생성한 인스턴스를 가리킴
new foo(); //foo{}
//4. 간접 호출
const bar={name:'bar'};
foo.call(bar);
foo.apply(bar);
foo.bind(bar)();
기본적으로 this 에는 전역 객체가 바인딩된다.
function foo(){
console.log(this)//window
function bar(){
console.log(this)//window
}
}
전역 함수는 물론 중첩 함수를 일반 함수로 호출하면 함수 내부의 this 에는 전역 객체가 바인딩된다.
var value=1;
const obj={
value:100,
foo(){
console.log(this); //{value:100, foo:f()}
console.log(this.value)//100
function bar(){
console.log(this);//window
console.log(this.value)//1
}
bar();
}
};
obj.foo();
콜백함수가 일반 함수로 호출돠면 콜백 함수 내부의 this 에도 전역 객체가 바인딩된다.
var value=1;
const obj={
value:100,
foo(){
console.log(this); //{value:100, foo:f()}
console.log(this.value)//100
setTimeout(function (){
console.log(this);//window
console.log(this.value)//1
},100);
}
};
obj.foo();
setTimeout 내부에 this 를 찍어도 같은결과가 나온다. 만약 객체를 참조하게 하고 싶다면?
var value=1;
const obj={
value:100,
foo(){
const that=this; //this 바인딩(obj) 을 that 에 저장한다.
setTimeout(function (){
//this 대신 that 을 참조한다.
console.log(that);//window
console.log(that.value)//1
},100);
}
};
obj.foo();
이러한 방법 외에 this 를 명시적으로 바인딩할 수 있는 apply, bind, call 을 제공한다. 이는 추후에 더 자세히 기술할 예정
혹은 화살표 함수를 통해 this 바인딩을 시킬 수 있다 ? arrow function 은 상위 스코프의 this 를 가리킨다.
메서드 내부의 this 에는 메서드를 호출한 객체, 즉 메서드를 호출할 때 메서드 이름 앞의 마침표 앞에 기술한 객체가 바인딩된다. 즉 메서드를 호출한 객체에 바인딩 되는 것이다.
const Person={
name:"Lee",
getName(){
return this.name;
}
};
console.log(person.getName());//Lee
const anotherPerson={
name:"Park"
}
//Person의 getName 메서드를 anotherPerson 메서드로 할당
anotherPerson.getName=person.getName;
console.log(anotherPerson.getName());//Park
const getName=Person.getName;
console.log(getName());// ' '
왜 마지막 같은 경우에는 Lee 가 아니라 빈 값이 출력되었을까? 일반함수로 호출했기 때문에 window 객체를 참조하고 있고, window의 name을 출력하는 것과 같기 때문이다.
생성자 함수 내부의 this 에는 생성자 함수가 생성할 인스턴스가 바인딩된다.
function Circle(radius){
this.radius=radius;
this.getDiameter=function(){
return 2*this.radius;
}
}
const Circle1=new Circle(5);
const Circle1=new Circle(10);
console.log(Circle1.getDiameter());//10
console.log(Circle2.getDiameter());//20
//new로 생성하지 않고 일반함수로 할당한다면?
const circle3= Circle(15);
//일반 함수로 호출된 Circle 에는 반환문이 없으므로?
console.log(circle3);//undefined
// 일반 함수로 호출된 CIrcle의 내부는 전역객체를 가리킨다.
console.log(radius)//15
apply와 call 은 this로 사용할 객체와 인수 리스트를 인수로 전달받는다.
function getThisBinding(){
return this;
}
//this로 사용할 객체
const thisArg={a:1};
console.log(getThisBinding());//window. 당연함 일반함수 호출이니까
//get어쩌구 함수를 호출하면서 인수로 전달한 객체를 get어쩌구 함수의 this에 바인딩하는 코드임
console.log(getThisBinding.apply(thisArg));//{a:1}
console.log(getThisBinding.call(thisArg));//{a:1}
apply와 call 메서드의 본질적인 기능은 함수를 호출하는 것이다. apply, call 은 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩한다.
위의 예제는 호출할 함수(getThisBinding)에 인수를 전달하지 않는다. 함수를 호출하면서 인수를 전달하는 예제를 살펴보자.
function getThisBinding(){
console.log(arguments); //들어온 인자를 출력하는 것임 argument
return this;
}
//this로 사용할 객체
const thisArg={a:1};
//1.apply
// get어쩌구 함수를 호출하면서 인수로 전달한 객체를 get 함수의 this에 바인딩한다.
//apply 메서드는 호출하 함수의 인수를 배열로 묶어 전달한다.
console.log(getThisBinding.apply(thisArg, [1,2,3]));
//Argument(3) [1,2,3] | {a:1}
//2. call
//호출할 함수의 인수를 쉽표로 구분한 리스트 형식으로 전달
console.log(getThisBinding.call(thisArg, 1,2,3))
이처럼 apply는 인수를 배열로 전달하고 call 은 인수를 리스트로 전달하는 방식만 다를 뿐 객체를 전달하면서 함수를 호출하는 것은 동일하다.
bind는 함수를 호출하지 않는다. 다만 첫번째 인수로 전달한 겂으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.
funciton getThisBinding(){
return this;
}
const thisArg={a:1};
//bind 메서드는 thisArg로 this 바인딩이 교체된 getThisBinding함수를 새로 생성하여 반환한다.
console.log(getThisBinding.bind(thisArg));
//함수를 호출하려면 명시적으로 호출해야함
console.log(getThisBinding.bind(thisArg)());
bind 메서드는 메서드의 this 와 메서드 내부의 중첩 함수 또는 콜백 함수의 this 가 불일치 하는 문제를 해결하기 위해 유용하게 사용된다.
아래 코드를 보자.
const person={
name: 'Lee',
foo(callback){
//1번 시점
setTimeout(callback,100);
//binding 사용하여 변경
setTimeout(callback.bind(this),100);
}
}
person.foo(function(){
//2번 시점
console.log('Hi, my name is ${this.name}');
})//일반함수로 호출되었기 때문에 this.name은 window.name 과 같다.
1번시점 : this 는 foo 를 호출한 객체인 person 객체를 가리킨다.
2번시점 : person.foo 의 콜백함수가 일반함수로서 호출된 2번 시점에서는 this 는 전역객체 window 를 가리킨다.