[TIL] [JavaScript] This

이나현·2021년 10월 19일
0

JavaScript

목록 보기
11/13
post-thumbnail

코어 자바스크립트의 this 부분을 읽다가 도저히 이해가 안되어 this를 공부해야겠다고 생각했다.

javascript_this

mdn을 살펴보면, "this는 javascript에서는 다르게 동작한다."라는 문장이 있다. 알아보니 다른 언어에서의 this는 클래스 인스턴스의 래퍼런스 변수라고 한다.

자바스크립트의 this는? 함수를 호출한 방법에 의해 결정된다.

내가 참고한 블로그에서는 this를 '현재 실행 문맥'이라고 정리했다.
결국, 이 실행 문맥도 호출자가 누구인가에 따라 결정된다.

전역 문맥

전역 실행 맥락에서 this는 엄격 모드(use strict)에 관계없이 전역 객체를 참조한다.

console.log(this === window);
a = 37;
console.log(window.a) // 37

함수 문맥

함수 내부에서 this의 값은 호출 방법에 의해 좌우된다.
이 때에는, 엄격모드와 비엄격모드가 구분된다.

//엄격모드
function strictMode() {
	"use strict";
    return this;
}

strictMode() === undefined// true
//비엄격모드
function nonStrictMode(){
	return this;
}

nonStrictMode() === window // true

call(), apply()

this의 값을 한 문맥에서 다른 문맥으로 넘기려면 두 메소드를 사용하면 된다.

let obj = {a: "나현"};
let a = "이나바리";

function useMethod(){
	return this.a;
}

useMethod(); // "이나바리"(전역객체에서 this가 실행됨) 
//obj이라는 문맥에서 값을 받고 싶다면?
useMethod.call(obj); // "나현"
useMethod.apply(obj); // "나현"

객체의 메서드로서

함수를 어떤 객체의 메서드로 호출하면 this의 값은 그 객체를 사용한다.

let o = {
	prop: 37,
    f: function() {
    	return this.prop;
    }
}

console.log(o.f()); // 37

생성자로서

생성자는 new로 객체를 만들어서 사용하는 방식이다.

function NewObject(name, color) {
	this.name = name;
    this.color = color;
    this.isWindow = function() {
    	return this === window
    }
}

const newObj = new NewObject('나현', 'blue')
console.log(newObj.name) //"나현"
console.log(newObj.color) //"blue"
console.log(newObj,isWindow) // false

해당 예제는 https://blueshw.github.io/2018/03/12/this/를 참고하였습니다.

단 여기서 new를 빼먹으면 모든 console.log의 값이 'error'가 나온다. 그 이유는 this는 window의 객체가 되지만, 값은 newObj에 할당되므로 각 property를 가져올 수 없다.

그렇다면 일반 객체에서는?

const person = {
	name: '나현',
    age: 28,
    nickname: '나바리',
    getName: function() {
    	return this.name 
    }
}

console.log(person.getName()) // "나현"

const otherPerson = person
otherPerson.name = "슬기"
console.log(person.getName) //"슬기"
console.log(otherPerson.getName) //"슬기"

위의 예제 중, console.log(person.getName) //"슬기" 이 부분을 눈 여겨 봐야한다. otherPerson은 person의 레퍼런스 변수이므로 otherPerson을 변경하면 person도 변경된다.

이를 피하기 위해서는 Object.assign()메서드를 이용하여 완전히 별도의 객체로 만들어야한다.

Object.assign()? 객체들의 모든 열거 가능한 자체 속성을 복사해 대상 객체에 붙여넣은 후 대상 객체를 반환한다.

const person = {
	name: '나현',
    age: 28,
    nickname: '나바리',
    getName: function() {
    	return this.name 
    }
}

const otherPerson = Object.assign({}, person)
otherPerson.name = "슬기"
console.log(person.getName) //"나현"
console.log(otherPerson.getName) //"슬기"

bind, arrow function

생성자 함수 안에서 또 다른 함수가 있는 경우(서브루틴)

function Family(firstName) {
	this.firstName = firstName
    const names = ['나현', '나경', '준오']
    names.map(function(lastName, index){
    	console.log(lastName + ' ' + this.firstName)
        console.log(this)
    })
}
const lees = new Family('lee')
//나현 undefined
//window 
.
.
.

map 메서드의 서브루틴은 호출될 때 map의 context(this)로 바인드 되지 않았기에 실행문맥이 전역객체이기 때문에 undefined가 반환된다.

이를 해결하기 위해서는? this를 변수에 선언해주면 된다~

function Family(firstName) {
	this.firstName = firstName
    const names = ['나현', '나경', '준오']
    const that = this;
    names.map(function(lastName, index){
    	console.log(lastName + ' ' + that.firstName)
    })
}
const lees = new Family('lee')
// 나현 lee

그럼 서브루틴의 서브루틴이 만들어지면 변수를 여러개 선언해야할까?
이 때, 해결방법이 bind()메서드라고 한다.

function Family(firstName) {
	this.firstName = firstName
    const names = ['나현', '나경', '준오']
    names.map(function(lastName, index){
    	console.log(lastName + ' ' + this.firstName)
    }.bind(this)
  )
}
const lees = new Family('lee')
// 나현 lee

하지만 arrow function을 사용하면 변수 선언과 bind()를 사용하지 않아도 된다.

function Family(firstName) {
	this.firstName = firstName
    const names = ['나현', '나경', '준오'];
    
    names.map((lastName, index) => {
    	console.log(lastName + ' ' + this.firstName)
    })
}
const lees = new Family('lee')
// 나현 lee

서브루틴의 바깥 쪽에서 this를 사용하고 싶다면 arrow function을 사용하는 것도 좋은 방법이다.

참고자료

profile
technology blog

0개의 댓글