명시적 바인딩 (call / apply / bind)

Jtiiin:K·2023년 10월 16일
0
post-thumbnail

명시적 바인딩

자동으로 부여되는 상황별 this의 규칙을 깨고 this에 별도의 값을 저장하는 방법

✅ call 메서드

  • 호출 주체인 함수를 즉시 실행하는 명령어
  • 첫 번째 매개변수에 this로 binding 할 객체를 넣어주면 됨
var func = function (a, b, c) {
	console.log(this, a, b, c);
};

// no binding
func(1, 2, 3); // Window{ ... } 1 2 3

// 명시적 binding
// func 안에 this에는 {x: 1}이 binding
func.call({ x: 1 }, 4, 5, 6}; // { x: 1 } 4 5 6
var obj = {
	a: 1,
	method: function (x, y) {
		console.log(this.a, x, y);
	}
};

obj.method(2, 3); // 1 2 3
obj.method.call({ a: 4 }, 5, 6); // 4 5 6

✅ apply 메서드

  • call메서드와 완전히 동일하지만 this에 binding 할 객체는 똑같이 넣어주되 나머지 부분만 배열 형태로 넘겨줌
var func = function (a, b, c) {
	console.log(this, a, b, c);
};
func.apply({ x: 1 }, [4, 5, 6]); // { x: 1 } 4 5 6

var obj = {
	a: 1,
	method: function (x, y) {
		console.log(this.a, x, y);
	}
};

obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6

✅ call / apply 메서드 활용

✔ 유사배열객체(array-like-object)에 배열 메서드 적용

📝유사배열의 조건
1. 반드시 length가 필요 / 이 조건은 필수이며 없으면 유사배열이라고 인식하지 않음
2. index 번호가 0부터 시작해서 1씩 증가해야 함 (안그래도 되지만 예상치 못한 결과 발생)

//객체에는 배열 메서드를 직접 적용할 수 없음
//유사배열객체에는 call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있음
var obj = {
	0: 'a',
	1: 'b',
	2: 'c',
	length: 3
};
Array.prototype.push.call(obj, 'd');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }

var arr = Array.prototype.slice.call(obj);
console.log(arr); // [ 'a', 'b', 'c', 'd' ]

✔ Array.from 메서드(ES6)

// 유사배열
var obj = {
	0: 'a',
	1: 'b',
	2: 'c',
	length: 3
};

// 객체 -> 배열
var arr = Array.from(obj);
console.log(arr); // ['a', 'b', 'c']

✔ 생성자 내부에서 다른 생성자를 호출(공통된 내용의 반복 제거)

  • Student, Employee 의 중복된 내용을 Person을 만들어 제거 => '구조화'에 도움
function Person(name, gender) {
	this.name = name;
	this.gender = gender;
}
function Student(name, gender, school) {
	Person.**call**(this, name, gender); // 여기서 this는 student 인스턴스!
	this.school = school;
}
function Employee(name, gender, company) {
	Person.**apply**(this, [name, gender]); // 여기서 this는 employee 인스턴스!
	this.company = company;
}
var kd = new Student('길동', 'male', '서울대');
var ks = new Employee('길순', 'female', '삼성');

✔ 여러 인수를 묶어 하나의 배열로 전달할 때 apply 사용

//비효율
var numbers = [10, 20, 3, 16, 45];
var max = min = numbers[0];
numbers.forEach(function(number) {
	// 현재 돌아가는 숫자가 max값 보다 큰 경우
	if (number > max) {
		// max 값을 교체
		max = number;
	}

	// 현재 돌아가는 숫자가 min값 보다 작은 경우
	if (number < min) {
		// min 값을 교체
		min = number;
	}
});

console.log(max, min);
//효율
var numbers = [10, 20, 3, 16, 45];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min);

// 펼치기 연산자(Spread Operation)를 통하면 더 간편하게 해결도 가능해요
const numbers = [10, 20, 3, 16, 45];
const max = Math.max(...numbers);
const min = Math.min(...numbers);
console.log(max min);

✅ bind 메서드

  • call과 비슷하지만 즉시 호출 되지 않고 넘겨받은 this 및 인수들을 바탕으로 새로운 함수를 반환
  • 함수에 미리 this를 적용
  • 부분 적용 함수 구현 시 유용
//효율
var func = function (a, b, c, d) {
	console.log(this, a, b, c, d);
};
func(1, 2, 3, 4); // window객체

// 함수에 this 미리 적용
var bindFunc1 = func.bind({ x: 1 }); // 바로 호출되지는 않아요! 그 외에는 같아요.
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8

// 부분 적용 함수 구현
var bindFunc2 = func.bind({ x: 1 }, 4, 5); // 4와 5를 미리 적용
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9

✔ name 프로퍼티

  • bind 메서드를 적용해서 새로 만든 함수는 name 프로퍼티에 'bound' 라는 접두어가 붙음
    (추적하기 용이)
var func = function (a, b, c, d) {
	console.log(this, a, b, c, d);
};
var bindFunc = func.bind({ x:1 }, 4, 5);

// func와 bindFunc의 name 프로퍼티의 차이를 살펴보세요!
console.log(func.name); // func
console.log(bindFunc.name); // bound func

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

내부함수

  • 변수를 활용한 우회법보다 call, apply, bind를 사용하면 깔끔
// call
var obj = {
	outer: function() {
		console.log(this); // obj
		var innerFunc = function () {
			console.log(this);
		};

		// call을 이용해서 즉시실행하면서 this를 넘겨주었습니다
		innerFunc.call(this); // obj
	}
};
obj.outer();
// bind
var obj = {
	outer: function() {
		console.log(this);
		var innerFunc = function () {
			console.log(this);
		}.bind(this); // innerFunc에 this를 결합한 새로운 함수를 할당
		innerFunc();
	}
};
obj.outer();

콜백함수

  • 콜백함수도 함수이기 때문에 함수가 인자로 전달될 때는 함수 자체로 전달
var obj = {
	outer: function() {
		console.log(this); // obj
		var innerFunc = function () {
			console.log(this);
		};

		// call을 이용해서 즉시실행하면서 this를 넘겨주었습니다
		innerFunc.call(this); // obj
	}
};
obj.outer();

✅ 화살표 함수의 예외사항

  • 화살표 함수는 실행 컨텐스트 생성 시 this를 바인딩하는 과정이 제외됨
    => 💡 접근하고자 하면 스코프 체인상 가장 가까운 this에 접근하게 됨
  • this 우회, call, apply, bind 보다 편한 방법
var obj = {
	outer: function () {
		console.log(this);
		var innerFunc = () => {
			console.log(this);
		};
		innerFunc();
	};
};
obj.outer();
profile
호기심 많은 귀차니즘의 공부 일기

0개의 댓글