📖
자바스크립트 this
2022.12.01
자바스크립트 this

우테코 코드리뷰를 하면서 다른 분들의 코드에서 this 활용을 봤다. 기존 내 코드에서는 기본 바인딩으로만 사용하여 전역변수처럼 활용하고 있었는데, 경우에 따른 다양한 this를 활용해보고자 공부하게 되었다.
What is this?👀
this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수이다.this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메소드를 참조할 수 있다.- 함수를 호출하면 인자와
this가 암묵적으로 함수 내부에 전달된다. this는 코드 어디서든 참조할 수 있다.- 함수 내부에서 인자를 지역변수처럼 사용할 수 있는 것과 같이,
this도 지역 변수처럼 사용할 수 있다. - 종류 : 기본 바인딩, 암시적 바인딩, 명시적 바인딩, new 바인딩, 화살표함수(es6)
- 우선순위 : new 바인딩 > 명시적 바인딩 > 암시적 바인딩 > 기본 바인딩
1. 기본 바인딩(Default binding)
- 기본 바인딩이 적용된 경우,
this는 전역 객체에 바인딩된다. - 브라우저 환경인 경우
window, 노드JS 환경인 경우global - 하지만, 엄격 모드인 경우에는 기본 바인딩 대상에서 전역객체 제외된다.
// 1
function foo() {
const a = 10;
console.log(this.a);
}
foo(); // undefined
// 2
// 엄격모드인 경우 undefined로 출력된다.
window.a = 10;
function foo() {
console.log(this.a);
}
foo(); // 102. 암시적 바인딩(Implicit binding)
- 함수가 객체의 프로퍼티로 접근해서 실행한다.
// 1
function hello() {
console.log(this.name);
}
const obj = {
name: 'bae',
hello: hello,
};
obj.hello(); // 'bae'
// 2
const foo = {
a: 20,
bar: function () {
console.log(this.a);
},
};
foo.bar(); // 20
// setTimeout(foo.bar, 1); ==> undefined
// setTimeout 함수 안 콜백은 bar라는 함수의 레퍼런스 부분, foo의 콘텍스트를 가지고 있지 않음3. 명시적 바인딩(Explicit binding)
- 자바스크립트의 모든 function은
call(),apply(),bind()라는 프로토타입 메소드를 갖고 있음. - 이 3가지 메서드 중 하나를 호출함으로 this바인딩을 코드에서 명시하는 것. 이때 this는 내가 명시한 객체에 바인딩된다.
call(): 매개변수의 목록apply(): 배열을 받음bind(): call, apply와 다르게 함수를 실행하지 않고, 새로운 함수를 만들어 리턴을 해준다.
// 1
function add(a, b){
return a + b;
}
app.call(null, 1, 2); // 3
app.apply(null, [1,2]); // 3
// 2
const obj = { name : 'seongkyu' };
const say = function(city){
console.log(`hello, my name is ${this.name}, I live in ${city}`);
};
say('seoul'); // hello, my name is , I live in seoul
say.call(obj, 'seoul'); // hello, my name is seongkyu, I live in seoul
say.apply(obj, ['seoul'); // hello, my name is seongkyu, I live in seoul
// 3
// call을 사용하면 함수를 실행하고 함수의 첫 번째 인자로 전달하는 값에 this를 바인딩
function logName(a, b, c) {
console.log(this.name);
console.log(a + b + c);
}
const person = {
name : 'seongkyu',
};
logName.call(person, 1, 2, 3);
/* 실행 결과
seongkyu
6
*/
// 4
// apply를 사용하면 함수를 실행하고 함수의 첫 번째 인자로 전다하는 값에 this를 바인딩
// call과의 차이점은 인자를 배열로 전달(인자로 배열 자체를 전달하는 것이 아닌 배열의 요소들이 값으로 전달된다. )
logName.apply(person, [1,2,3]);
/* 실행 결과
seongkyu
6
*/
// 5
// bind 함수 첫 번째 인자에 this를 바인딩한다는 점은 같으나, 함수를 실행하지 않고 새로운 함수를 반환한다. (반환된 새로운 함수를 실행해야 원본함수가 실행됨)
function logAge(a,b,c){
console.log(this.age);
console.log(this.name);
console.log(a);
console.log(b);
console.log(c);
console.log(a + b + c);
}
const person = {
age : 81,
name : 'seongkyu',
}
const myAge = logAge.bind(person, 1); // myAge를 실행시키면 person에 해당하는 값들이 바인딩 됨. 2번째 인자에 있는 1이 a의 위치에 들어간다.
myAge(2,3); // 2가 logAge의 b 파라미터로, 3이 logAge의 c파라미터로 들어간다.4. new를 통한 바인딩
new를 통해 생성하게되면this는 새로 생성된 객체를 가르키게 된다. a. 새로운 객체 생성 b. 새로 생성된 객체의 [[Prototype]]이 연결된다. c. 새로 생성된 객체는 해당 함수 호출 시 this로 바인딩된다.(new바인딩) d. 함수가 자신의 또 다른 객체를 반환하지 않는 한, new와 함께 호출된 함수는 자동으로 새로 생성된 객체를 반환한다.
class Man {
constructor(name) {
this.name = name;
}
hello() {
console.log('hello ' + this.name);
}
}
const john = new Man('john'); // new 키워드를 붙이지 않을 경우, this가 해당 객체로 바인딩 되지 않기 때문에 window 객체를 건드리게된다.
john.hello();5. 화살표 함수(Arrow function)
- ECMAScript 6에서 새로 추가된 함수를 축약해서 사용할 수 있는 문법
- 무조건 익명함수로만 사용할 수 있음 / 메소드나 생성자 함수로 사용할 수 없음
- this를 외부 스코프에서 정적으로 바인딩된 문맥(정적 컨텍스트, Lexical context)을 갖는다는 특징을 가지고 있다.
- 화살표 함수의 랙시컬 바인딩은 절대 오버라이드 할 수 없음
- lexical binding : 화살표 함수를 정의한 시점의 코드 문맥에서 상위 환경과 동일한 this를 가리킨다.
- 자바스크립트에서는 어떤 식별자(변수)를 찾을 때 현재 환경에서 그 변수가 없으면 바로 상위 환경을 검색. 점점 상위 환경으로 타고올라가다 변수를 찾거나 가장 상위환경에 도달하면 그만두게 된다. 화살표 함수에서의 this 바인딩도 이와 유사.
const cat = {
name: 'meow',
foo1: function () {
const foo2 = function () {
console.log(this.name);
};
foo2();
},
};
cat.foo1();
/**
cat.foo1() 메소드 호출 시 내부 함수 foo2 실행
함수가 호출 됐으므로 foo2 배우의 this는 지정되지 않아서 전역 객체를 가리킴
전역객체에는 name 속성이 없으므로 undefined
*/const cat = {
name: 'meow',
foo1: function () {
const foo2 = () => {
console.log(this.name); // meow
};
foo2();
},
};
cat.foo1();
// 화살표 함수에는 this가 아예 없음❎화살표함수를 쓰면 안되는 경우❎
- 메소드
const cat = {
name : 'meow';
callName : () => console.log(this.name); // undefined
}
cat.callName();
/**
위와 같은 경우의 callName 메소드의 this는 자신을 호출한 객체 cat이 아닌 함수 선언 시점의 상위 스코프인 전역객체를 가리킨다.
*/- 생성자 함수
const Foo = () => {};
const foo = new Foo(); // 타입에러
// 화살표함수를 생성자로 사용하면 에러 발생한다.- addEventListener()의 콜백함수
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this); // window
this.innerHTML = 'clicked';
});
button.addEventListener('click', function () {
console.log(this); // button엘리먼트
this.innerHTML = 'clicked';
});
/**
addEventListener의 콜백함수에서는 this에 해당 이벤트 리스너가 호출된 엘리먼트가 바인딩되도록 정의되어 있다.
화살표 함수를 사용하면 기존 바인딩 값이 사라지고 상위 스코프가 바인딩 된다.
*/👇 도움이 되셨다면 👇