본문 바로가기
javascript

[자바스크립트] this 바인딩 포스팅 AS - 바인딩 종류

by jinbro 2017. 5. 20.
[자바스크립트 this]
- 자바스크립트 this는 다른 언어들과 다름 : 컴파일 때 this가 정해지지만, 자바스크립트는 실행하면서 정해짐(동적)
- 자바스크립트 코드 실행 가능 영역 분류 : global(전역), function, eval
- this 는 현재 실행 코드의 환경(변수객체)을 지칭함 : 기본적으로 전역객체(브라우저 : window, nodejs : global)를 가리킴

- 쉽게 말해 어떤 변수객체로 호출하고 있는가에 따라 this가 다름
- this를 명시적으로 변경 가능함


[자바스크립트 this 결정조건]
1) 어떤 영역에서 수행되는가 : global / function / eval
2) 호출자(호출 객체)가 누구인가 : 가장 가까이에 있는 객체

- 첫번째 조건에서 같은 function 영역이라도 2번째 조건에 따라 달리 this가 바인딩 됨
- global 영역에 정의되어있는 변수, 함수는 전역객체(window)의 멤버로 저장
- function 영역에서는 function call에 따라 다르게 this가 바인딩됨


[자바스크립트 function 영역 호출자에 따른 this 바인딩]
1) 간단한 함수 호출(strict mode 사용X) : 전역객체(window) 멤버함수 호출 
1
2
3
4
5
6
7
8
var a = 10;
 
function foo(){
     var b = 20;
     return this;
}
 
foo(); // 10, window 객체의 멤버변수 a가 호출됨
cs


2) 객체의 메서드로서 함수 호출 : 객체의 멤버함수(메서드) 호출
1
2
3
4
5
6
7
8
9
10
var prop = 20;
 
var obj = {
     prop: 10,
     foo: function(){
          return this.prop;
     }
}
 
console.log(obj.foo()); // 10
cs
- 가장 가까운, 자신을 호출한 객체(obj)를 this로 바인딩

1
2
3
4
5
6
7
8
9
10
var obj = {
     prop: 10
}
 
function independent(){
     return this.prop;
}
 
obj.foo = independent;
obj.foo(); // 10
cs
- 위와 같은 동작이지만, 객체와 함수를 따로 정의해두고 함수를 객체의 멤버함수로 추가시킨 후 호출하는 것

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
     prop: 10,
     innerObj: {
          prop: 20,
          foo: function(){
               return this.prop;
          }
     }
}
 
obj.innerObj.foo(); // 20
 
cs
- 가장 가까이, 호출하고 있는 객체를 this로 바인딩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj1 = {
     a: 10,
     b: 20,
 
     foo: function(){
 
          return this.a + ", " + this.b;
     }
}
 
var obj2 = Object.create(obj1); 
obj2.a = 20;
obj2.b = 10;
obj2.foo(); // 20, 10
 
cs
- 다른 객체의 메서드를 호출하는 것도 가능 : this가 교체되겠지
- Object.create는 내부적으로 obj2.__proto__ = obj1; (Object 와 obj1)


3) 생성자로서 호출 : new 키워드를 통해 생성되는 객체를 this 값으로 바인딩함
1
2
3
4
5
6
7
8
function Person(name, age, sex){
     this.name = name;
     this.age = age;
     this.sex = sex;
}
 
var jinbro = new Person("jinbro"26"male");
 
cs
- 일반 함수 호출이었다면 this가 window 객체의 프로퍼티 값으로 등록되었음
- 일반 함수 호출이 아닌 생성자로서 호출되었으므로 new를 통해 만들어진 객체가 this로 바인딩
- 일반 함수로의 호출 시와 생성자 함수로서 호출 시 window 프로퍼티(name, age, sex)의 값 변화를 보면 알 것


[자바스크립트 function 영역 명시적 this 바인딩]
- 명시적 바인딩을 하는 이유 : 다른 함수의  멤버변수, 함수를 빌려쓰기위해 this를 변경하면 자기것처럼 사용가능

1) call 함수 : 함수의 인수로 객체를 전달, 전달한 객체가 this로 설정됨, 그 뒤 인수는 함수에 필요한 파라미터
1
2
3
4
5
6
7
8
9
10
function add(c, d){
     return this.a + this.b + c + d;
}
 
var obj = {
     a: 1,
     b: 2
}
 
add.call(obj, 34); // 10
cs
- 인수로 전달받은 객체를 this로 사용할 수 있음
- 원래라면 위의 add 함수의 this는 전역객체지만, call 함수의 인수로 전달받은 객체를 명시적으로 this에 바인딩 후 함수 콜
- call 함수는 Function.prototype 객체 내 멤버함수 : add 함수는 Function의 객체
- 아래는 Function.prototype.call : Function 함수의 프로토타입 객체가 가진 call 멤버 함수의 내부 모습
- add 함수의 this를 일시적으로 변경하여 call 하는 것


2) apply 함수 : call 함수와 인자로 전달하는 객체가 this 되는 것은 마찬가지, 그 뒤 apply 인수에는 배열을 넘김(인수가 많을 때)
1
2
3
4
5
6
7
8
9
10
11
12
13
function foo(){
     console.log(this);
     console.log(foo.arguments);
}
 
var obj = {
     var1: "1",
     var2: "2"
};
 
foo.apply(obj, [1,2,3,4,5,6]);
// Object { var1: "1", var2: "2" }    
// [1, 2, 3, 4, 5, callee: function, Symbol(Symbol.iterator): function]
cs
- this로 바인딩할 객체 뒤 배열은 함수의 arguments(유사 배열)로 전달 및 저장 : arguments로 활용가능 
- 함수는 실행될 때 컨텍스트를 생성하며, 컨텍스트 내부에는 AO(AO의 arguments), this, [[Scopes]]가 있음
- 아래 이미지는 함수를 일반 호출 했을 때와 apply 함수를 통해 객체를 넘긴 후 호출했을 때 this 차이를 알아본 것임

- foo 함수의 this를 일시적으로 변경하여 call 하는 것


3) bind 함수 : bind 함수의 인자로 넘긴 객체를 this로 바인딩하고, foo 함수를 래핑한 함수(바운드 함수)를 생성함
1
2
3
4
5
6
7
8
9
10
11
12
13
function foo(){
     return this.a;
}
 
var obj = {
     a: "hello"
}
 
var bar = foo.bind(obj); // return 값은 function
 
bar(); // "hello"
 
console.log(typeof(bar)); // function : foo 함수와 동일
cs

- foo 함수를 래핑한 함수를 function bound(바운드 함수)라 함, ECMA6 명세에 있음
- 바운드 함수는 일반 함수와는 다름, bind를 호출한 함수를 래핑한 함수, this는 bind 함수의 인수로 넘긴 객체로 영구적 변경
- 바운드 함수를 호출하면 foo 함수를 호출한 것이지만, this는 바운드 함수 this로 호출
- 바운드 함수는 내부적으로 일반 함수 객체와 같은 내부 슬롯(프로퍼티)을 가지지 않음, 대신 아래와 같은 내부 슬롯을 가짐
- [[TargetFunction]] : foo 함수를 가리킴
- [[BoundThis]] : bind 함수의 인자로 넘긴 객체를 가리킴, this 바인딩된 객체
[[BoundArguments]] : 바운드 함수를 생성할 때 bind 함수의 인자(객체 제외) 값을 저장하는 유사 배열 형태


- 일시적으로 this를 변경하여 함수를 실행하는 call, apply와는 다름


[자바스크립트 this 바인딩 우선 순위]
1) 명시적 바인딩 - call, apply, bind
2) new 키워드 생성 객체 
3) 일반적인 this 바인딩 : window(일반적인 전역함수 호출) 혹은 객체의 메서드로서 호출


[참고자료]

- MDN, call 함수 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/call

- MDN, apply 함수 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

- MDN, bind 함수 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/bind



댓글