티스토리 뷰

https://coding-god.tistory.com/73 - 에서 this는 간단하게 말해서 코드가 작성된 시점에서 바인딩 되는 것이 아닌,

코드가 실행되는 호출부(함수가 어떻게 호출됐는가?)에서 함수를 호출할 때 바인딩 된다는 것을 알 수 있었다.

 

따라서 호출부를 꼼꼼이 살펴보고, this의 네가지 바인딩 규칙 중 어떤 것이 맞는지 잘 살펴보아야 한다.

 

1. 기본 바인딩

 

function foo() {
	console.log( this.a );
}

var a = 2;

foo(); // 2

 

기본적으로 단독 함수 실행을 하는 경우에는 항상 전역 객체를 참조한다.

하지만 엄격모드에서는 전역 객체가 기본 바인딩 대상에서 제외된다.

function foo() {
	"use strict";

	console.log( this.a );
}

var a = 2;

foo(); // TypeError: `this` is `undefined`

 

2. 암시적 바인딩

 

두번째 규칙은 호출부에 콘텍스트 객체가 있는지, 즉 객체의 소유 / 포함 여부를 확인하는 것이다.

 

function foo() {
	console.log( this.a );
}

var obj = {
	a: 2,
	foo: foo
};

obj.foo(); // 2

다음 예제처럼 객체 프로퍼티 참조가 체이닝 된 형태라면 최상위 / 최하위 수준의 정보만 호출부와 연관된다.

function foo() {
	console.log( this.a );
}

var obj2 = {
	a: 42,
	foo: foo
};

var obj1 = {
	a: 2,
	obj2: obj2
};

obj1.obj2.foo(); // 42

 

하지만 암시적 바인딩의 가장 큰 문제 중 하나는 암시적 소실이다. 암시적으로 바인딩 된 함수에서 바인딩이 중간에 소실되는 경우가 있는데, 이것 때문에 this가 많이 헷갈린다. 엄격 모드 여부에 따라 전역 객체나 undefined 중 한가지로 기본 바인딩 된다.

 

function foo() {
	console.log( this.a );
}

var obj = {
	a: 2,
	foo: foo
};

var bar = obj.foo; // function reference/alias!

var a = "oops, global"; // `a` also property on global object

bar(); // "oops, global"
function foo() {
	console.log( this.a );
}

function doFoo(fn) {
	// `fn` is just another reference to `foo`

	fn(); // <-- call-site!
}

var obj = {
	a: 2,
	foo: foo
};

var a = "oops, global"; // `a` also property on global object

doFoo( obj.foo ); // "oops, global"

콜백 함수에서도 헷갈릴 경우가 있어서, 암시적 바인딩 때문에 예기치 않게 this가 바뀌게 되면 문제가 생기게 된다.

따라서 어떤 객체를 this 바인딩에 이용하겠다는 의지를 코드에 명확히 밝힐 방도를 찾게되었고

모든 자바스크립트 함수가 함께 사용할 수 있는 아주 적당한 유틸리티가 바로 call()과 apply()이다.

call 과 apply는 아주 약간 다르지만 이것은 나중에 알아보기로 하고, 사용법에 대해서 알아보자 

 

3. 명시적 바인딩

function foo() {
	console.log( this.a );
}

var obj = {
	a: 2
};

foo.call( obj ); // 2

 

foo.call()에서 명시적으로 obj를 바인딩하므로 this는 반드시 obj가 된다

 

하지만 아쉽게도 이렇게 명시적으로 바인딩을 한다해도 앞에서 언급한 this바인딩이 도중에 소실되거나 프레임워크가 임의로 덮어써 버리는 문제는 해결할 수 없다.

 

따라서 명시적 바인딩을 약간 변형한 꼼수가 있다.

 

하드 바인딩

function foo() {
	console.log( this.a );
}

var obj = {
	a: 2
};

var bar = function() {
	foo.call( obj );
};

bar(); // 2
setTimeout( bar, 100 ); // 2

// `bar` hard binds `foo`'s `this` to `obj`
// so that it cannot be overriden
bar.call( window ); // 2

 

하드 바인딩은 매우 자주 쓰는 패턴이어서 ES5 내장 유틸리니 Function.prototype.bind 역시 다음과 같이 구현되어 있다.

function foo(something) {
	console.log( this.a, something );
	return this.a + something;
}

var obj = {
	a: 2
};

var bar = foo.bind( obj );

var b = bar( 3 ); // 2 3
console.log( b ); // 5

 

API 호출 콘텍스트

 

많은 라이브러리 함수와 자바스크립트 언어 및 호스트 환경에 내장된 여러 새로운 함수는 대게 콘텍스트라 불리는 선택적인 인자를 제공한다. 이는 bind를 써서 콜백 함수의 this를 지정할 수 없는 경우를 대비한 일종의 예비책이다.

function foo(el) {
	console.log( el, this.id );
}

var obj = {
	id: "awesome"
};

// use `obj` as `this` for `foo(..)` calls
[1, 2, 3].forEach( foo, obj ); // 1 awesome  2 awesome  3 awesome

 

 

참조 - https://github.com/getify/You-Dont-Know-JS

'Javascript' 카테고리의 다른 글

WebView의 디버깅  (0) 2021.01.20
최상위 window의 parent  (0) 2021.01.19
Promise 내의 async  (1) 2021.01.07
javascript의 closure  (0) 2021.01.04
Node.js의 논블로킹 I/O 방식의 장점, 단점  (0) 2019.08.08
[YOU DON'T KNOW JS] 3장 객체 (1)  (0) 2019.07.31
XHR(XML Http Request)의 동작방식과 호출 방법  (0) 2019.07.12
Javascript의 this에 대하여 (1)  (0) 2019.06.21
댓글