JavaScript - This

lsjoon·2022년 12월 14일
0

JavaScript

목록 보기
13/32

This

· JS 에서 this = "동적으로" 결정됨
· 다른 객체지향 언어에서 this = "정적으로" 자신의 인스턴스 주소를 칭함

This 바인딩

바인딩 = "식별자""값" 을 연결하는 과정

this 바인딩 = "this""this가 가리킬 Object" 를 연결하는 과정

일반함수(중첩 함수, 콜백 함수) this
👉 window(Global) Object

생성자(method) 함수 this
👉 생성된 인스턴스 Object

Event this
👉 Event Object

화살표 함수
👉 렉시컬 this ( = 상위 스코프의 this Object )


동적 바인딩 (dynamic binding)

this 가 환경에 따라 바뀌는 것 = "동적 바인딩(dynamic binding)"

· Function 은 다양한 상황(환경)에서 호출 될 수 있으며, 호출 환경에 따라 this 는 "동적" 으로 세팅
· apply, call, bind 등으로 this가 가리키는 것을 조작할 수 있음

⚪ 함수 호출 : 함수를 직접 호출
⚪ 메소드 호출 : 객체의 메서드를 호출
⚪ 생성자 함수 호출 : 생성자 함수를 호출
⚪ 간접 호출 : apply / call / bind 등으로 함수를 간접 호출
⚪ 그 외 콜백 함수의 호출

<script>

var foo = function() {
	console.dir(this);

<< 함수 호출 >>
foo();								// window

<< 메소드 호출 >>
var obj = { foo: foo };
obj.foo();							// object
	
<< 생성자 함수 호출 >>
var instance = new foo();			// instance

<< apply, call, bind 호출 >>
var bar = { name: 'bar' };
foo.call(bar);						// bar
foo.apply(bar);						// bar
foo.bind(bar)();					// bar

</script>

콜백 함수

· callback function 은 특정 동작 이후 불려지는 함수
· 보통 다른 함수의 인자로 보내지는 함수


This 바인딩 - 함수 호출

· 기본적으로 this 는 "전역객체 바인딩"
· 전역객체는 모든 object 의 "유일한 최상위 object"
👉 일반적으로 브라우저에서는 "window", 서버에서는 "global 객체"를 의미
· 전역객체는 전역 스코프를 갖는 전역변수를 property 로 소유

"전역함수", "내부함수", "콜백함수" 의 this
👉 "전역객체 바인딩"

⚪ "내부함수" 의 this
👉 선언 위치(일반함수, 메소드, 콜백) 와 상관없이 모두 "전역객체 바인딩"

This 바인딩 - 메소드 호출

· function 이 object 의 property value 이면 method 로서 호출
👉 Method 내부의 this 는 해당 method 를 호출한 object에 바인딩

<script>

var obj1 = {
	name: "Lee",
    sayName: function() {
    	console.log(this.name);
    }
}

var obj2 = {
	name: "Kim";
}

obj2.sayName = obj1.sayName;

obj1.sayName();					// Lee
obj2.sayName();					// Kim

</script>

This 바인딩 - 생성자 호출

· JS 에서 생성자는 object 를 생성하는 것
👉 기존 function 에 new 연산자를 붙여 호출

· 생성자 함수로 선언된 function 에서 생성된 object 로서 반환되기 위해서는 선언한 variable(또는 property) 에 this 를 사용해야함
👉 this 를 사용하지 않으면 function 외부에서 해당 property 접근 불가

<script>

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

var me = new Person('Lee');
console.log(me);						// Person&nbsp; {name: "Lee"}

<< new 연산자와 함께 생성자 함수를 호출하지 않으면 동작 X >>
var you = Person('Kim);
console.log(you);						// undifined

</script>

This 바인딩 - apply, call, bind 호출

apply(a, b)

👉 a = this로 사용할 객체
👉 b = arguments ( 배열 or 유사 배열 객체 )

call(a, b)

👉 a = this로 사용할 객체
👉 b = arguments list ( " , " 로 구분하여 전달 )

bind(a)()

👉 a = this로 사용할 객체

⚪ apply, call
· 본질적인 기능은 "function 호출" (1회성)
· function 호출 시, < 첫 번째 parameter(a) 로 전달한 특정 object > 를 "호출한 함수의 this" 에 바인딩
· 두 번째 parameter(b) 를 function 에 전달하는 방식만 다를 뿐, apply 와 call 의 기능은 동일

⚪ bind
· function 의 this value 영구적 변경 가능
· 본질적인 기능은 "this 로 사용할 객체"만 전달(return)
👉 function 호출 X
· < method 의 this > 와 < method 내부의 중첩 함수 or 콜백 함수의 this > 가 불일치 시 사용

<script>

var vaule = 1;

var obj = {
	value = 100,
    foo: function() {
    	console.log("foo's this: ", this);					// obj
    	console.log("foo's this.value: ", this.value);		// 100
        
        function bar(a, b) {
	    	console.log("bar's this: ", this);				// obj 
	    	console.log("bar's this.value: ", this.value);	// 100
	    	console.log("bar's arguments: ", arguments)
		}
		bar.apply(obj, [1, 2]);
		bar.apply(obj, 1, 2);
		bar.bind(obj)(1, 2)
	}
};

<< result는 모두 동일 >>
  foo's this: 				{ value: 100, foo: [Function: foo] }
  foo's this.value: 		100
  bar's this: 				{ value: 100, foo: [Function: foo] }
  bar's this.value: 		100
  bar's this.arguments: 	[Arguments] { '0': 1, '1': 2 }
  
</script>

화살표 함수와 일반 함수의 this

let 0 = {
	name: "Daniel",
    f1: () => {
	    console.log("[f1] this : ", this);
    },
>
    f2: function () {
    	console.log("[f2] this : ", this);
    },
};
>
o.f1(); 		// global
o.f2();			// o

f1은 화살표 함수로 호출 시 this 는 함수가 생성된 환경을 가리키도록 고정 ( 호출된 함수를 둘러싼 실행 컨텍스트 )
f2는 일반 함수로 this 는 함수가 호출된 환경을 가리키며 this 는 동적으로 바뀔 수 있음 ( 새롭게 생성된 실행 컨텍스트 )

화살표 함수의 this는 정해지면 바꿀 수 없음 (call, bind, apply 적용 X)
setTimeout 등 this가 바뀌는 상황에서 유용


일반 함수 호출 시 call, apply, bind

let 0 = {
	name: "Daniel",
    printName: function() {
    	console.log("내 이름은 ", this.name);
    },
};
>
o.printName();								// 내이름은 Daniel

setTimeout(o.printName, 10); 				// 내 이름은 undefined
setTimeout(o.printName.bind(o), 20);		// 내 이름은 Daniel

bind, call, apply 등의 함수로 this를 조작한다.
setTimeout 은 함수 호출과는 다른 콜백 호출임.
printName 메서드는 bind 함수를 이용해 this 변수가 o 를 가리키도록 컨텍스트를 동적 바인딩함

const o = {
    name: "Elice",
}

function myFunc() {
  console.log(this);
}

myFunc()              // window {...}
myFunc.bind(o)()      // {name: 'Elice'}
myFunc.call(o, null)  // {name: 'Elice'}
myFunc.apply(o, null) // {name: 'Elice'}

This 고정하기

함수에서 사용되는 this

const name = "elice";
console.log(this); // window {...}
function myFunc() {
  const name = "elice";
  console.log(this);
}

myFunc(); 		// 일반 함수 호출시 window {...}

전역 컨텍스트에서 thiswindow 를 가리킴
일반 함수 호출에서도 thiswindow 를 가리킴

생성자 함수 호출

function myFunc(name) {
  this.name = name;
  this.getName = function () {
    console.log("getName this:", this);
    return this.name;
  };

  console.log("myFunc this:", this);
  return this; 		//	생략 가능합니다.
}

const o = new myFunc("elice"); // myFunc this: myFunc {...}
o.getName() // myFunc this: myFunc {...}

생성자 함수 내부의 thisnew 키워드를 통해 앞으로 만들어질 인스턴스 객체를 가리킴.
즉, 일반적으로 함수를 호출할 때는 this가 모두 window를 가리키지만,
함수를 객체로 생성하는 방법에만 this가 생성하는 함수 내부를 가리킴.

객체에서 사용되는 this

const o = {
  name: "elice",
  myFunc: function () {
    console.log(this);
  },
};

o.myFunc(); 			// {name: 'elice', myFunc: ƒ}
const o = {
  name: "elice",
  myFunc: function () {
    return function () { console.log(this) }
  },
};

o.myFunc()(); 			// window {...}

객체에서의 this 는 메소드를 호출한 객체를 가리킴
하지만 객체 안 함수의 내부 함수에 this 는 동적 바인딩으로 인해 전역 객체를 가리킴

1. 화살표 함수로 고정하기

/* 생성자 함수 방식 */
function createObject() {
  this.myFunc = function () {
    console.log("myFunc this:", this);
    return function () { console.log("myFunc return this:", this) 
    ((수정)) return () => { console.log("myFunc return this:", this) };
    
  };
}

const o = new createObject();
o.myFunc()(); 
// myFunc        this: createObject {...}
// myFunc return this: window {...}
((수정 후))
// myFunc        this: createObject {...}
// myFunc return this: createObject {...}
/* 객체 리터럴 방식 */
const o = {
  myFunc: function () {
    console.log("myFunc this:", this)
    return function () { console.log("myFunc return this:", this)
    ((수정)) return () => { console.log("myFunc return this:", this)}
  },
};

o.myFunc()();
// myFunc        this: myFunc {...}
// myFunc return this: window {...}
((수정 후))
// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}

생성자 함수로 만든 객체, 객체 리터럴 방식으로 만든 객체 모두 내부의 this 는 자시 자신을 가리키고 있음.
하지만 내부에서 함수를 만들고 해당 함수에서 this 를 출력 시 window 를 가리킴.

이 때, 화살표 함수를 사용하면 객체 최상위 스코프를 가리키도록 유지

2. call, apply, bind 메서드로 고정하기

bind 메서드

/* 생성자 함수 방식 */
function createObject() {
  this.myFunc = function () {
    console.log("myFunc this:", this);
    return function () { console.log("myFunc return this:", this) };
  };
}

const o = new createObject();
o.myFunc().bind(o)(); // bind 메서드를 사용해 o객체로 고정시킵니다.
// myFunc        this: createObject {...}
// myFunc return this: createObject {...}
/* 객체 리터럴 방식 */
const o = {
  myFunc: function () {
    console.log("myFunc this:", this)
    return function () { console.log("myFunc return this:", this) }
  },
};

o.myFunc().bind(o)(); // bind 메서드를 사용해 o객체로 고정 그리고 함수를 실행
// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}

apply, call 메서드

두 메서드의 차이는 call 메서드는 인수 목록을 받고 apply 메서드는 인수 배열을 하나 받음.
ex) call(this, var1, var2, var3, …) / apply(this, [ el, el2, el3, … ])

/* 생성자 함수 방식 */
function createObject() {
  this.myFunc = function () {
    console.log("myFunc this:", this);
    return function () { console.log("myFunc return this:", this) };
  };
}

const o = new createObject();
o.myFunc().call(o, null);  // call  메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc        this: createObject {...}
// myFunc return this: createObject {...}

o.myFunc().apply(o, null); // apply 메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc        this: createObject {...}
// myFunc return this: createObject {...}
/* 객체 리터럴 방식 */
const o = {
  myFunc: function () {
    console.log("myFunc this:", this)
    return function () { console.log("myFunc return this:", this) }
  },
};

o.myFunc().call(o, null);  // call  메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}

o.myFunc().apply(o, null); // apply 메서드를 사용해 o객체로 고정시킨 후 함수 실행
// myFunc        this: myFunc {...}
// myFunc return this: myFunc {...}
profile
중요한 것은 꺾여도 그냥 하는 마음

0개의 댓글