-
[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를 사용한 함수 호출
- 👉 MDN 문서 보기
- this의 대상과 인자들을 나열
console.log(italian.makeStew.call(korean, false, 2));2. apply를 사용한 함수 호출
- 👉 MDN 문서 보기
- this의 대상 뒤의 인자들을 배열로
console.log(italian.makeStew.apply(korean, [false, 2]));3. ⭐ bind를 사용한 this 대상 고정
- 👉 MDN 문서 보기
- 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 함수 구분
생성자 역할프로토타입argumentssuperfunction 선언 함수 ✅ ✅ ✅ ❌ 메서드 ❌ ❌ ✅ ✅ 화살표 함수 ❌ ❌ ❌ ❌
⭐ 화살표 함수와 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();'웹 > JavaScript' 카테고리의 다른 글
[Javascript] 비동기 프로그래밍, 타임아웃, setTimeout, asychronous (0) 2023.01.14 [Javascript] 프로토타입, 개념, 상속, object.assign (0) 2023.01.14 [Javascript] 스코프와 바인딩, 렉시컬, 클로저 (0) 2023.01.14 [Javascript] 옵셔널 체이닝, prop, ?., 옵셔널 체이닝 연산자 (0) 2023.01.13 [Javascript] 엄격모드, strict mode, use strict (0) 2023.01.13