본문 바로가기

[자바스크립트] 명시적으로 this를 바인딩하는 방법 (feat 코어 자바스크립트)

 

 

들어가며

많은 기업들이 타입스크립트와 nest.js를 활용해서 서버 개발을 하곤 합니다. 취업을 하려면, 타입스크립트와 nest.js를 공부해서 실무를 익히는 것이 중요할 것입니다. 하지만 아직 자바스크립트의 기초도 없는 상태에서 타입스크립트와 nest.js를 공부하는 것이 맞을까 하는 생각이 들었습니다. 빠르게 기술변화를 적응하고, 러닝 커브를 줄이기 위해 빠르게 공부해야 하는 것도 맞겠지만, 그전에 언어의 기반이 되는 자바스크립트부터 제대로 알아야 하지 않을까 하는 생각이 들었습니다. 이 기회에 자바스크립트의 기본에 대해 정리해보고자 합니다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

this에 별도의 대상을 바인딩하는 방법이 있습니다. 이 방법들에 대해 알아보겠습니다.

 

 

 

 

 

 

1. call 메서드

call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령입니다. 이때 call 메서드의 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 합니다. 즉 call 메서드를 통해 임의의 객체를 this로 지정할 수 있습니다.

 

var func = function (a, b, c){
	console.log(this, a, b, c);
}

func(1, 2, 3); // Window{ ... } 1 2 3
func.call({x : 1}, 4, 5, 6); // { x : 1 } 4 5 6

 

함수 뿐 아니라 메서드에서도 만약 객체의 메서드를 단순 호출하면 this는 객체를 참조하지만 call 메서드를 이용하면 임의의 객체를 this로 지정할 수 있습니다.

 

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

 

 

 

 

 

 

 

 

2. apply 메서드

apply 메서드는 call 메서드와 기능적으로 동일하지만 매개변수를 지정하는 방식에서 차이가 존재합니다. call 메서드는 첫 번째 인자를 제외한 나머지 모든 인자들을 호출할 함수의 매개변수로 지정하는 반면, apply 메서드는 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다는 점에서만 차이가 있습니다. 

 

 

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

 

 

 

 

 

 

 

 

3. call / apply 메서드의 활용

call이나 apply 메서드를 잘 활용하면 자바스크립트를 더욱 다채롭게 사용할 수 있습니다. 몇 가지 활용 사례를 소개합니다.

 

 

 

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

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']

객체에는 배열 메서드를 직접 적용할 수 없습니다. 그러나 키가 0 또는 양의 정수인 프로퍼티가 존재하고 length 프로퍼티의 값이 0 또는 양의 정수인 객체, 즉 배열의 구조와 유사한 객체의 경우(유사배열객체) call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있습니다. 7번째 줄에서는 배열 메서드인 push를 객체 obj에 적용해 프로퍼티 3에 'd'를 추가했습니다.

 

9번째 줄에서는 slice 메서드를 적용해 객체를 배열로 전환했습니다. slice 메서드는 원래 시작 인덱스값과 마지막 인덱스값을 받아 시작값부터 마지막값의 앞 부분까지의 배열 요소를 추출하는 메서드인데, 매개변수를 아무것도 넘기지 않을 경우에는 그냥 원본 배열의 얕은 복사본을 반환합니다. 그러니까 call 메서드를 이용해 원본인 유사배열객체의 얕은 복사를 수행한 것인데, slice 메서드가 배열 메서드이기 때문에 복사본은 배열로 반환하게 된 것입니다. 

 

그 밖에도 유사배열객체에는 call/apply 메서드를 이용해 모든 배열 메서드를 적용할 수 있습니다. 하지만 call/apply를 이용해서 형변환을 하는 것은 'this'를 원하는 값으로 지정해서 호출한다'라는 본래의 메서드의 의도와는 다소 동떨어진 활용법입니다. 이에 ES6에서는 유사배열객체 또는 순회 가능한 모든 종류의 데이터 타입을 배열로 전환하는 Array.from 메서드를 새로 도입했습니다.

 

 

var obj = {
	0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};
var arr = Array.from(obj);
console.log(arr); // ['a', 'b', 'c']

 

 

 

 

생성자 내부에서 다른 생성자를 호출

생성자 내부에 다른 생성자와 공통된 내용이 있을 경우 call 또는 apply를 이용해 다른 생성자를 호출하면 간단하게 반복을 줄일 수 있습니다. Student, Employee 생성자 함수 내부에서 Person 생성자 함수를 호출해서 인스턴스의 속성을 정의하도록 구현했습니다.

 

function Person(name, gender) {
	this.name = name;
    this.gender = gender;
}

function Student(name, gender, school) {
	Person.call(this, name, gender);
    this.school = school;
}

function Employee(name, gender, company) {
	Person.apply(this, [name, gender]);
    this.company = company;
}

var by = new Student('보영', 'female', '단국대');
var jn = new Employee('재난', 'male', '구골');

 

 

 

 

 

4. bind 메서드

bind 메서드는 ES5에서 추가된 기능으로 call과 비슷하지만 즉시 호출하지는 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드입니다. 다시 새로운 함수를 호출할 때 인수를 넘기면 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록됩니다. 즉 bind 메서드는 함수에 this를 미리 적용하는 것과 부분 적용 함수를 구현하는 두 가지 목적을 모두 지닙니다. 예제를 통해 확인해보겠습니다.

 

var func = function(a, b, c, d) {
	console.log(this, a, b, c, d);
}
func(1, 2, 3, 4); // Window{ ... } 1 2 3 4

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);
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9

 

6번째 줄에서 bindFunc1 변수에는 func에 this를 { x: 1 }로 지정한 새로운 함수가 담깁니다. 이제 7번째 줄에서 bindFunc1을 호출하면 원하는 결과를 얻을 수 있게 됩니다. 한편 9번째 줄의 bindFunc2 변수에는 func에 this를 { x: 1 }로 지정하고, 앞에서부터 두 개의 인수를 각각 4, 5로 지정한 새로운 함수를 담았습니다. 이후 10번째 줄에서 매개변수로 6, 7을 넘기면 this 값이 바뀐 것을 제외하고는 최초 func 함수에 4, 5, 6, 7을 넘긴 것과 같은 동작을 합니다. 11번째 줄에서도 마찬가지입니다. 6번째 줄의 bind는 this만을 지정한 것이고, 9번째 줄의 bind는 this 지정과 함께 부분 적용 함수를 구현한 것입니다. 

 

 

 

 

name 프로퍼티

bind 메서드를 적용해서 새로 만든 함수는 한 가지 독특한 성질이 있습니다. 바로 name 프로퍼티에 동사 bind의 수동태인 'bound'라는 접두어가 붙는다는 점입니다. 어떤 함수의 name 프로퍼티가 'bound xx'라면 이는 곧 함수명이 xxx인 원본 함수에 bind 메서드를 적용한 새로운 함수라는 의미가 되므로 기존의 call이나 apply보다 코드를 추적하기에 더 수월해진 면이 있습니다.

 

var func = function (a, b, c, d){
	console.log(this, a, b, c, d);
}
var bindFunc = func.bind({ x: 1 }, 4, 5);
console.log(func.name); // func
console.log(bindFunc.name); // bound func

 

 

 

 

 

5. 화살표 함수의 예외사항

ES6에 도입된 화살표 함수는 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외됐습니다. 즉 이 함수 내부에는 this가 없으며 접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 됩니다.

 

 

var obj = {
	outer: function() {
    	console.log(this);
        var innerFunc = () => {
        console.log(this);
    };
    innerFunc();
}
obj.outer();

 

화살표 함수를 활용하면 별도의 변수로 this를 우회하거나 call, apply, bind를 적용할 필요가 없어 더욱 간결하고 편리하게 사용할 수 있습니다. 

 

 

 

 

 

 

 

 

6. 정리

다음 규칙은 명시적 this 바인딩이 없는 한 늘 성립합니다. 

- 전역공간에서의 this는 전역객체(브라우저에서는 window, Node.js에서는 global)를 참조합니다.

- 어떤 함수를 메서드로서 호출한 경우 this는 메서드 호출 주체(메서드명 앞의 객체)를 참조합니다.

- 어떤 함수를 함수로서 호출한 경우 this는 전역객체를 참조합니다. 메서드의 내부함수에서도 같습니다.

- 콜백 함수 내부에서의 this는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않은 경우에는 전역객체를 참조합니다.

- 생성자 함수에서의 this는 생성될 인스턴스를 참조합니다. 

 

 

 

다음 규칙은 명시적 this 바인딩에 대한 경우입니다.

- call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출합니다. 

- bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만듭니다. 

- 요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 합니다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

마치며

자바스크립트의 기본에 대해 공부하면서, 기본도 정확하게 알지 못하고, 앞으로 나아가려 했구나 하는 생각이 들었습니다. 기초이기에 지금 공부하는 내용을 더 잘 이해해야겠다고 생각했습니다. 기본에 충실할 수 있는 개발자가 되고 싶습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처

 

싸니까 믿으니까 인터파크도서

 

book.interpark.com