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를 고정하여 문제를 해결할 수 있다.
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)
새로운 객체를 반환한다
새로운 객체는 객체의 메소드 호출시 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와 바인딩된다.
// 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를 레퍼런스로 참조하면 기본 바인딩이 적용되어 전역 변수를 참조하거나 최악으로는 변경하는 예기치 못한 일이 발생할 수 있다. 이러한 돌발 상황은 다양한 버그를 양산할 수 있다. 그래서 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 : 함수 안에 변수 및 변수들에 값을 할당
var name = 'Global Variable';
console.log(this.name); // 'Global Variable'
function foo(){
console.log(this.name); // "Global Variable"
}
foo(); // function invocation
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;
function F(v) {
this.val = v;
}
var f = new F('WooHoo!'); // this는 f 인스턴스이다.
console.log(f.val); // WooHoo!
console.log(val); //ReferenceError
//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를 사용하지 않는 것을 의미