[자바스크립트 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, 3, 4); // 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(일반적인 전역함수 호출) 혹은 객체의 메서드로서 호출
[참고자료]
- ECMAScript6 명세 : http://www.ecma-international.org/ecma-262/6.0/
- 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
'javascript' 카테고리의 다른 글
[웹기본개념] 집에서 웹서버 운영하기 (0) | 2017.05.22 |
---|---|
[자바스크립트 자료구조] 배열 (0) | 2017.05.22 |
[자바스크립트] 잠깐 쉬어가기 : 자바스크립트 특징 (0) | 2017.05.18 |
[자바스크립트] 객체 그리고 프로토타입 (0) | 2017.05.17 |
[자바스크립트] this 바인딩 (0) | 2017.05.16 |
댓글