Class: 제로베이스
Created: December 20, 2022 5:43 AM
Type: Javascript
강의 명: 초심자도 빈틈없이 학습하는 자바스크립트
함수 호출 방법에 따라 값이 결정되는 것. 선언된 함수를 어떤 방법에 따라 혼출하는지에 따라 값이 결정되는 것. 바인딩이라고 부른다.
자바스크립트는 함수 호출 방식에 의해 this에 바인딩될 객체가 결정된다.
함수 실행 컨텍스트에서는 this는 함수가 어떻게 호출됐냐 (how the function is called)에 집중해서 파악하면된다.
아래의 코드를 보고 간단히 이해해보자
const person ={
name:'kyle',
sayHello:function(){
console.log('hello',this.name)
}
}
person.sayHello() //hello kyle
const callMyName = person.sayHello
callMyName() //hello
이제 함수가 어떻게 호출이 됐느냐(함수선언식의 경우)에 집중해서 아래의 글을 보면 이해가 수월할 것이다.
함수 선언식 안에서 this는 global객체를 의미한다. 함수선언식은 함수가 호출,실행 될 때 this가 동적으로 결정된다.
이런 이유 때문인지 함수 선언식이 내부함수에서 선언될 경우 (콜백함수, 내부함수의 내부함수)는 항상 global,window 처럼 전역객체에 바인딩된다.
이런 방법을 해결하기 위한 방법
const obj = {
name: "kyle",
getName: function () {
console.log(this); //obj
const that = this;
setTimeout(function () {
console.log(this); // window
console.log(that); //obj
});
},
};
모든 객체의 최상위 객체를 말함
자바스크립트에서는 window 객체.
console.log(solution(s)) // 13
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b); // MDN
console.log(b); // MDN
window.str = "window";
function foo() {
console.log(this === obj); //true
console.log(this.str); //10
}
var obj = {
str : 10,
fn : foo
//foo 함수를 obj에 연결해줌으로서 this 값이 obj로 바뀜
}
obj.fn(); // 암시적 바인딩 -> true, 10
foo(); // 기본 바인딩 -> false, "window"
뭔가 따로 명시적으로 바인딩이된 것이 아니라, 사용함에 따라 암시적으로 바인딩 됨.
obj.fn에 foo함수가 할당됨에 따라 this는 obj를 가리키게 됨.
참고 URL(apply, call, blind) :
Function.prototype 객체의 메소드인 apply, call bind 를 통해서 this를 특정 객체에 명시적으로 바인딩 할 수 있다.
func.apply(thisArg, [argsArray])
apply는 함수를 호출하는 함수이다. 주로 유사 배열 객체들을 객체 메소드를 활용할 때 사용된다.
function k() {
console.log(arguments); //[Arguments] { '0': 1, '1': 2, '2': 3 }
console.log(arguments.slice()); // Error
const arr = Array.prototype.slice.apply(arguments);
console.log(arr); //[ 1, 2, 3 ]
}
k(1, 2, 3);
apply는 slice메소드를 호출하는데 this는 arguments로 바인딩하라는 뜻이다. 즉, arguments.slice()
function foo(str2) {
return this.str1 + str2;
}
var obj = {
str1 : '명시적 바인딩 '
}
var bindingFn = function() {
return foo.apply(obj,arguments);
}
console.log(bindingFn("apply 실습하기","1"))
// 명시적 바인딩 apply 실습하기
func.call(thisArg[, arg1[, arg2[, ...]]])
call은 apply와 하는 역할은 같다. 하지만 apply와 문법이 조금 다르다.
apply는 array로 실행시킬 함수의 arguments를 받는 반면 call은 인자를 하나하나 받는다.
window.str = "window";
function foo() {
console.log(this === obj); //true
console.log(this.str); //10
}
var obj = {
str : 10,
fn : foo
//foo 함수를 obj에 연결해줌으로서 this 값이 obj로 바뀜
}
foo.call(obj);
bind는 apply와 call과 다르게 함수를 리턴하고 호출하지는 않는다.
const obj = {
name: "kyle",
sayHello: function () {
console.log(this.name);
},
};
const obj2 = {
name: "kelly",
};
obj.sayHello(); //kyle
obj.sayHello.call(obj2); //kelly
obj.sayHello.bind(obj2)(); //kelly
위의 예제처럼 bind는 함수를 리턴하기 때문에 호출을 따로 해주어야 한다.
function Person(name) {
console.log(this); //Person{}
console.log(name); //kyle
this.name = name; // Person {name: } 에 kyle을 할당해줌.
console.log(this); //Person{name: 'kyle'}
}
const user = new Person("kyle"); //Person { name: 'kyle' }
console.log(user.__proto__); //Person{}
const kelly = Person("kelly"); // global Object - 생성자함수 동작안함
둘의 차이는 각자의 프로토타입 객체가 다르다.
arrow function은 this 바인딩할 객체가 선언할 때 정적으로 결정된다. 즉, 언제나 상위 스코프의 this를 가르킨다 이를 Lexical this 라한다.
참고URL >
const cat = {
name: 'meow',
foo1: function() {
const foo2 = () => {
console.log(this.name);
}
foo2();
}
};
cat.foo1(); // meow
위 코드와 달라진 점은 cat
객체의 내부함수 foo2
가 화살표 함수로 선언됐다는 점 뿐입니다. 그런데 이번엔 우리가 의도한대로 meow
가 잘 찍혔습니다. 어떻게 가능한걸까?
이게 가능한 이유는 화살표 함수에는 ⭐️this가 아예 없기 때문⭐️. 즉, function
으로 선언한 함수를 실행할 땐 this가 존재하긴 하지만 값을 지정하지 않는데, 화살표 함수로 선언한 함수에는 this가 없다.
JavaScript에서는 어떤 식별자(변수)를 찾을 때 현재 환경에서 그 변수가 없으면 바로 상위 환경을 검색합니다. 그렇게 점점 상위 환경으로 타고 타고 올라가다가 변수를 찾거나 가장 상위 환경에 도달하면 그만두게 되는 것이죠. 화살표 함수에서의 this 바인딩 방식도 이와 유사합니다. 화살표 함수에는 this라는 변수 자체가 존재하지 않기 때문에 그 상위 환경에서의 this를 참조하게 됩니다.
더 정확히는, function으로 선언한 함수가 메소드로 호출되냐 함수 자체로 호출되냐에 따라 동적으로 this가 바인딩되는 반면, 화살표 함수는 선언될 시점에서의 상위 스코프가 this로 바인딩.
조금 어렵고 헷갈리실 수 있지만 그냥 편하게 화살표 함수를 쓰면 내가 의도한 바로 그 this가 바인딩되는구나! 하고 생각하셔도 개발하는 데에는 별 지장 없을 것
이렇게 JavaScript의 함수 this 바인딩 문제를 깔끔하게 해결해 준 화살표 함수도 사용해선 안되는 때가 있습니다. 상위 환경의 this를 참조한다는 점이 문제가 될 수도 있거든요. 바로 다음과 같은 경우입니다.
const cat = {
name: 'meow';
callName: () => console.log(this.name);
}
cat.callName(); // undefined
이 같은 경우, callName
메소드의 this
는 자신을 호출한 객체 cat
이 아니라 함수 선언 시점의 상위 스코프인 전역객체를 가리키게 됩니다. 어차피 일반 함수를 사용해도 메소드로 호출하면 자신을 호출한 객체를 가리키기 때문에 메소드에서 화살표 함수를 쓸 필요는 없겠죠?😉
const Foo = () => {};
const foo = new Foo() // TypeError: Foo is not a constructor
화살표 함수를 생성자함수로 사용하면 에러가 납니다. 생성자 함수로는 사용할 수 없게 만들어졌어요!
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this); // Window
this.innerHTML = 'clicked';
});
button.addEventListener('click', function() {
console.log(this); // button 엘리먼트
this.innerHTML = 'clicked';
});
원래 addEventListener
의 콜백함수에서는 this에 해당 이벤트 리스너가 호출된 엘리먼트가 바인딩되도록 정의되어 있습니다. 이처럼 이미 this의 값이 정해져있는 콜백함수의 경우, 화살표 함수를 사용하면 기존 바인딩 값이 사라지고 상위 스코프(이 경우엔 전역 객체)가 바인딩되기 때문에 의도했던대로 동작하지 않을 수 있습니다. 물론 상위 스코프의 속성들을 쓰려고 의도한 경우라면 사용할 수 있습니다.
위 함수 선언식과 같은 예제지만 내부함수임에도 화살표 함수는 상위 스코프의 this를 가르키고 있다. 여기서 상위 스코프는 getName 메소드이다.
잠깐! getName메소드는 함수 선언식이기 때문에 호출하지 않으면 this는 undefined입니다. 아래 코드를 호출했을 때 안했을 때를 구분해서 보세요!
화살표 함수는 콜백함수에서 this를 사용할 때 헷갈리지 않게 사용할 수 있다.
getName()
호출 전let a;
const obj = {
name: "kyle",
getName: function () {
a=this
console.log(this);
setTimeout(() => {
console.log(this);
});
},
};
console.log(this) // 뭘까요~?
/*출력
undefined
*/
getName()
호출 후let a;
const obj = {
name: "kyle",
getName: function () {
a=this
console.log(this);
setTimeout(() => {
console.log(this);
});
},
};
obj.getName()
console.log(this)
/*출력
obj
obj
obj
*/
하지만 arrow function을 조심해야 하는 경우도 있다. addEventListener
, 생성자함수 등 있다.poiemaweb 을 보면 자세하게 나와있다.