this

백승용·2020년 10월 8일

Javascript

목록 보기
1/4

this와 bind

this의 값은 함수를 호출한 방법에 의해 결정된다. 함수를 어떻게 호출했는지 상관하지 않고 this값을 설정할 수 있는 bind가 있다. this는 런타임 때 바인딩 된다. 함수를 호출할 때 바인딩된다.

규칙

기본 바인딩

엄격모드에서는 전역 객체가 기본 바인딩 대상에서 제외되기 때문에 this는 undefined가 된다. this가 window 객체를 가르킨다.

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

const a = 2;
foo(); //2

-----------------------
function foo() {
  "use strict"
  console.log(this.a)
}
const a = 2;
foo(); // 타입 에러

암시적 바인딩

function foo() {
  console.log(this.a)
}
const obj = {
  a: 2,
  foo: foo
}
obj.foo() // 2
foo() // undefined

----------------------
// 객체의 속성으로 함수를 할당해도 this가 window로 바인딩되는 경우되는 경우
function foo() {
    console.log(this.a);
}

const obj = {
    a:1,
    foo: foo
}

//reference case 1
const foo2 = obj.foo;
foo2(); // undefined

//callback case 2
setTimeout (  obj.foo ,  100  ); // undefined

암시적 바인딩의 문제점 어떤 이유로 this가 바뀌게 됐든 콜백 함수의 레퍼런스를 마음대로 통제할 수 없으니 각자의 입맛에 맛게 호출부를 조정할 수 없기 때문에 this를 고정하여 문제를 해결할 수 있다.

명시적 바인딩

  • call(호출), apply(호출), bind(생성)함수를 사용하여 this를 원하는 대상에 바인딩 시켜준다.
  • this에 객체 대신 단순 원시 값(문자열, boolean, 숫자)을 인자로 전달하면 원시 값에 대응(new String(), new Boolean(), new Number())되는 객체로 래핑되고 이를 박싱이라고 한다.
function foo(){
  console.log(this.a)
}

const obj = {
  a:2
}
foo.call(obj) //2, this에 obj를 바인딩, this는 obj가 된다.

------------------------------------
// 하드 바인딩
function foo() {
  console.log(this.a)
}

const obj = {
  a:2
}

const bar = function() {
  foo.call(obj)
}
bar()
setTimeout(bar,100) // 2
bar.call(window) //2
function foo(something){
  console.log(this.a, something)
  return this.a + something
}

const obj = {
  a:2
}

const bar = function() {
  return foo.apply(obj,arguments)
}

const b = bar(3) // 2 3
console.log(b) // 5
-------------------------------------
function foo(something){
  console.log(this.a, something)
  return this.a + something
}
function bind(fn, obj) {
    return function() {
        console.log(arguments)
        return fn.apply(obj, arguments)
    }
}
const obj = {
  a:2
}

const bar = bind(foo, obj) // bind 함수를 선언하지 않아도 foo.bind(obj)와 같이 작성해도 동일하다.

const b = bar(3) // 2 3
console.log(b) // 5
// bind 커링
function minus(a, b) {
  return a + b
}

const minus5 = minus.bind(null,5)
minus5(2) //3
// API 호출 컨텍스트
function foo(el) {
  console.log(el, this.id)
}
const obj = {
  id: "멋진 남자"
}
// foo() 호출시 obj를 this로 사용
[1,2,3].forEach(foo,obj)

new 바인딩

새로운 객체를 반환한다
새로운 객체는 객체의 메소드 호출시 this로 바인딩 된다

function Person(name) {
  this.name = name
}
Person.prototype.hello() {
  console.log(this.name)
}
const obj = new Person('chris');
obj.hello();

새로운 객체를 반환하고 이것이 obj에 할당된다. hello() 함수는 obj객체를 this와 바인딩된다.

순서

  1. new 바인딩
  2. 명시적 바인딩
  3. 암시적 바인딩
  4. 기본 바인딩
// new 바인딩 > 명시적 바인딩
function hello(name){
  this.name = name
}
const obj1={}
const helloFn=hello.bind(obj1)
helloFn("chris") 
console.log(obj1.name) // chris

const obj2 = new helloFn("alice")
console.log(obj1.name) // chris
console.log(obj2.name) // alice

-----------------------------------------
  // new 바인딩 > 암시적 바인딩
 function hello(name) {
  this.name = name
}

var obj1 = {
  hello: hello,
}

obj1.hello("chris")
console.log(obj1.name) // chris

var obj2 = new obj.hello("alice")
console.log(obj1.name) // chris
console.log(obj2.name) // alice

명시적 바인드 할 때 this를 null로 바인딩하는 것

함수가 내부적으로 this를 레퍼런스로 참조하면 기본 바인딩이 적용되어 전역 변수를 참조하거나 최악으로는 변경하는 예기치 못한 일이 발생할 수 있다. 이러한 돌발 상황은 다양한 버그를 양산할 수 있다. 그래서 Object.create(null)로 Object.prototype을 위임하지 않은 객체를 생성하여 this를 바인딩한다.

실행 컨택스트

자바스크립트가 실행되기 위해서는 변수, 스코프 정의, this값 정의가 필요한데 이것을 실행 컨택스트라고 한다.
Global execution context : 자바스크립트 코드 시작시 실행되는 실행 컨택스트(전역 변수), 한 개만 존재한다. 이유는 싱글 스레드이기 때문이다.
Functional execution context : 함수마다 스코프(지역 스코프)를 가지고 있다. 실행되고 있는 함수에 대해서 execution context, 함수 내부의 this값, 함수가 필요로 하는 지역 변수들을 포함한 것
eval : eval function을 호출할 때 eval execution context 실행되고 사용할 일이 거의 없다.

실행 컨택스트는 Creation Phase와 Execution Phase로 두 단계를 거쳐 생성된다
1) Creation Phase : 변수 및 함수 안에 변수를 생성하고 값은 할당하지 않는 단계
이때, this를 바인딩한다.

2) Execution Phase : 함수 안에 변수 및 변수들에 값을 할당

참고사이트1
참고사이트2
유튜브

5 Patterns of Binding 'this'

1/2. Global & function invocation : 여러번 scope여도 this는 window다.

  • var 키워드로 변수 선언
  • 함수 호출
  • this는 window이다.
  • scope를 몇개를 해도 this는 window이다.
var name = 'Global Variable';
console.log(this.name); // 'Global Variable'

function foo(){
  console.log(this.name); // "Global Variable"
}

foo(); // function invocation

3. Methods invocation : 부모 object, 객체에 담긴 함수를 메서드라고 한다.

  • this는 부모 object이다.
  • 부모가 같으면 true이다.
let obj = {
  foo: function() { console.log(this); }
}
obj.foo() //method invocation, this는 부모 object를 가져온다. 부모가 window이다.
// 실행되는 시점에서 부모가 누구인지 판단한다.
var obj = {
    fn: function(a, b) {
        return this;
    }
};

var obj2 = {
    method: obj.fn
}

console.log( obj2.method() === obj2 ); // true;
console.log( obj.fn() === obj);  // true;

4. Construction invocation : 생성자 함수

  • new 키워드로 만든 클래스의 인스턴트이다.
function F(v) {
  this.val = v;
}

var f = new F('WooHoo!'); // this는 f 인스턴스이다. 

console.log(f.val); // WooHoo!
console.log(val); //ReferenceError

5. .call or .apply 호출: call, apply의 첫번째 인자로 명시된 객체

  • call과 apply의 차이점은 두번째 인자가 배열로 오면 apply이고 각각의 인자로 오는 것은 call이다.
//call
function identify(){
  return this.name.toUpperCase();
}
function speak() {
  var greeting = "Hello, I'm " + identify.call(this);
  console.log(greeting);
}
var me = { name: "Kyle" };
var you ={ name: "Reader" };

identify.call( me ); //KYLE
identify.call( you ); //READER
speak.call( me ); //Hello, I'm KYLE
speak.call( you ); //Hello, I'm READER

--------------------------------------------

var obj = { hello:'world' }
function bar() {
  console.log(this);
}

bar.call(obj); // { hello:'world' }
bar.call({a: 1}); // {a: 1}
--------------------------------------------

//apply
var add = function (x, y) {
  this.val = x + y;
}
var obj = {
  val: 0
};

add.apply(obj, [2,8]); //this는 obj이고 x = 2, y = 8
console.log(obj.val); // 10
add.call(obj,2,8);
console.log(obj.val); // 10


//apply 사용법
Math.max(15,3,2,8,99,156); //결과 : 156

let arr = [15,3,2,8,99,156]
// arr의 가장 큰 수를 출력하는 방법
Math.max.apply(null,arr); // null은 this를 사용하지 않는 것을 의미

Global execution context

  • 전역 메모리 테이블
  • 전역의 실행 context를 생성
  • 전역 메모리 생성

Local excution context

  • lexical scope에 의존한다.
  • execution context의 구성 요소 중 하나로, 함수가 실행되는 동안 이용할 수 있다.

excution context

  • 보통 this를 execution context라고 한다.
  • 블록 단위는 생성하지 않고 함수 단위로 context를 생성한다.
  • 함수 실행에 대한 정보를 담고 있다.
  • 함수 호출 1회당 하나의 실행 컨텍스트가 생성된다.

0개의 댓글