[TIL #6] 231017_실행 컨텍스트, this

Bora.K | 권보라·2023년 10월 17일
0

TIL

목록 보기
6/51
post-thumbnail

오늘 한 일

  • [내배캠] Javascript 문법 3주차 완강
    • 얕은 복사, 깊은 복사 다시 듣기
    • record와 호이스팅, this 함수
  • 알고리즘 특강
  • TIL 특강

배운 것

데이터 타입


1. 참조형 데이터의 가변성을 해결하기 위한 방법

// user 객체 생성
var user = {
	name: 'wonjang',
	gender: 'male',
};

// 이름을 변경하는 함수 'changeName' 정의
var changeName = function (user, newName) {
	var newUser = user; // user를 newUser로 복사
	newUser.name = newName; // 객체의 프로퍼티에 접근해서 이름 변경
	return newUser;
};

// 변경한 user 정보를 user2 변수에 할당
var user2 = changeName(user, 'twojang');

// 가변성 때문에 user1도 변경됨
// user === user2가 되므로 로직 수행 불가
if (user !== user2) {
	console.log('유저 정보가 변경되었습니다.');
}

console.log(user.name, user2.name); // twojang twojang
console.log(user === user2); // true
  • 해결 방법: 객체의 프로퍼티에 접근하는 것이 아니라, 아에 새로운 객체를 반환
    → 불변하므로 user에 영향을 미치지 않는다.
var changeName = function (user, newName) {
	return {
		name: newName,
		gender: user.gender,
	};
};
  • 문제점: 프로퍼티가 수십, 수백개일 때에는 어떻게…?

2. 얕은 복사

  • for ~ in 구문을 이용하여 객체의 모든 프로퍼티에 접근
  • copyObject로 복사한 다음, 복사를 완료한 새로운 객체의 프로퍼티를 변경
  • 복사 후에는 copyObjectDeep과 target은 별도의 오브젝트! (영향을 안줌)
// 얕은 복사 기본 세팅값
var copyObject = function (target) {
	var result = {};

	for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
}
  • 결과 확인
var user = {
	name: 'wonjang',
	gender: 'male',
};

var user2 = copyObject(user);  // copyObject 활용
user2.name = 'twojang';  // 새로운 객체 user2의 이름 값 변경

if (user !== user2) {
	console.log('유저 정보가 변경되었습니다.');  // 정상적으로 수행
}

console.log(user.name, user2.name);
console.log(user === user2);
  • 문제점:
    • 중첩된 객체의 경우 온전히 복사하지 못함.
    • 바로 아래 단계(1뎁스)의 값만 복사

3. 깊은 복사

내부의 모든 값을 하나하나 다 찾아서 복사 (중첩된 객체의 경우 깊은 복사 사용)
재귀적 수행(recursive) :
함수나 알고리즘이 자기 자신을 호출하여 반복적으로 실행되는 것

// 깊은 복사 기본 세팅값

var copyObjectDeep = function(target) {
	var result = {};

	if (typeof target === 'object' && target !== null) {
		for (var prop in target) {
			result[prop] = copyObjectDeep(target[prop]);
		}
	} else {
		result = target;
	}
	return result;
}
  • 결과 확인
var obj = {
	a: 1,
	b: {
		c: null,
		d: [1, 2],
	}
};
var obj2 = copyObjectDeep(obj);

obj2.a = 3;
obj2.b.c = 4;
obj2.b.d[1] = 3;

console.log(obj);
console.log(obj2);

4. undefined와 null

  • undefined
    • 변수에 값이 지정되지 않은 경우
    • .이나 []로 접근하려 할 때, 해당 데이터가 존재하지 않는 경우
    • return문이 없거나 호출되지 않는 함수의 실행 결과
  • null
    • ‘없다’를 명시적으로 표현
    • typeof null = object 자체버그

실행 컨텍스트(스코프, 변수, 객체, 호이스팅)


1. 실행 컨텍스트

실행할 코드에 환경정보들을 모아놓은 객체

  • 선언된 변수를 위로 끌어올림 = 호이스팅(hoisting)
  • 외부 환경 정보 구성
  • this값 설정

스택(Stack) : LIFO, Last in, First out
큐(Queue) : FIFO, First in, First out

  • 콜 스택(call stack)
    실행할 코드에 제공할 환경 정보들을 모아놓은 객체 → 콜 스택에 쌓아둠
    → 코드의 환경 및 순서를 보장

2. 실행 컨텍스트 객체의 실체(담기는 정보)

  • VariableEnvironment

    • record : 현재 컨텍스트 내의 식별자 정보 var a
    • outer : 외부 환경 정보
    • snapshot 유지

  • LexicalEnvironment

    • VE와 동일하지만, 변경사항을 실시간으로 반영(snapshot 유지 x)
    • 결국, 실행 컨텍스트를 생성할 때, VE에 정보를 먼저 담은 다음,
      이를 그대로 복사하여 LE를 만들고 이후에는 주로 LE를 활용
  • ThisBinding
    - this 식별자가 바라봐야할 객체

3. record

식별자 정보들이 저장, record의 수집 과정이 hoisting!

  • 수집 대상 정보: 함수에 지정된 매개변수 식별자, 함수 자체, var로 선언된 변수 식별자 등
  • 컨텍스트 내부를 처음부터 끝까지 순서대로 훑어가며 수집

4. hoisting

호이스팅 : 끌어올리다

법칙 1 : 매개변수 및 변수는 선언부를 호이스팅
법칙 2 : 함수 선언은 함수 전체를 호이스팅

→ 복잡한 코드일 수록 함수표현식 활용
→ 함수 선언부만 호이스팅되어 이후부터의 코드만 영향을 받음

5. outer (outer EnvironmaetReference)

  • 스코프 : 식발자에 대한 유효범위, 대부분의 언어에 존재
  • 스코프 체인 :
    • outer는 현재 호출된 함수가 선언될 당시의 LE를 참조!
    • 가장 가까운 요소부터 차례대로 접근 가능
var a = 1;
var outer = function() {
	var inner = function() {
		console.log(a);  // undefined
    // var a(호이스팅) / console.log(a) / a = 3 순서로 저장
		var a = 3;
	};
	inner();
	console.log(a);  // 1
// inner는 이미 콜스텍에서 사라졌기 때문에 var a = 3 참조X
// outer의 바깥인 전역 영역에서만 가져옴
};
outer();
console.log(a); // 1

각각의 실행 컨텍스트는 LE 안에 recordouter를 가지고 있고, outer 안에는 그 실행 컨텍스트가 선언될 당시의 LE정보가 다 들어있으니 scope chain에 의해 상위 컨텍스트의 record를 읽어올 수 있다.



this


1. 상황에 따라 달라지는 this

  • this 는 함수를 호출할 때 결정된다.
  • 전역 환경에서 this는 노드-global 객체, 브라우저-window 객체
    • 런타임: 코드가 돌아가는 그 환경 : ① 노드 ② 브라우저

  • 함수 vs 메서드 : 독립성의 차이

    함수: 그 자체로서 독립적인 기능을 수행 → this전역 객체
    함수명();

    메서드: 자신을 호출한 대상 객체에 대한 동작을 수행 → this호출의 주체
    객체.메서드명();


(1) 메서드로서 호출할 때 그 메서드 내부에서의 this

// CASE1 : 함수
// 호출 주체를 명시할 수 없기 때문에 this는 전역 객체를 의미
var func = function (x) {
	console.log(this, x);
};
func(1);   // Window { ... } 1

// CASE2 : 메서드
// 호출 주체를 명시할 수 있기 때문에 this는 해당 객체(obj)를 의미
var obj = {
	method: func,
};
obj.method(2);   // { method: f } 2
  • 함수로서의 호출과 메서드로서의 호출 구분 기준 : . []
var obj = {
	methodA: function () { console.log(this) },
	inner: {
		methodB: function() { console.log(this) },
	}
};

obj.methodA();             // this === obj
obj['methodA']();          // this === obj

obj.inner.methodB();       // this === obj.inner
obj.inner['methodB']();    // this === obj.inner
obj['inner'].methodB();    // this === obj.inner
obj['inner']['methodB'](); // this === obj.inner

(2) 함수로서 호출할 때 그 함수 내부에서의 this
함수로서 ‘독립적으로’ 호출할 때 this는 항상 전역객체를 가리킨다.
메서드의 내부라고 해도, 함수로서 호출한다면 this는 예외 없이 전역객체

var obj1 = {
	outer: function() {
		console.log(this); // (1) obj1
		var innerFunc = function() {
			console.log(this); // (2) 전역객체, (3)obj2
		}
		innerFunc();

		var obj2 = {
			innerMethod: innerFunc
		};
		obj2.innerMethod();
	}
};
obj1.outer();

메서드의 내부 함수에서의 this 우회

a. 변수를 활용하는 방법
내부 스코프에 이미 존재하는 this를 별도의 변수(ex : self)에 할당

var obj1 = {
	outer: function() {
		console.log(this); // (1) outer

		// AS-IS
		var innerFunc1 = function() {
			console.log(this); // (2) 전역객체
		}
		innerFunc1();

		// TO-BE
		var self = this;
		var innerFunc2 = function() {
			console.log(self); // (3) outer
		};
		innerFunc2();
	}
};

// 메서드 호출 부분
obj1.outer();

b. 화살표 함수를 활용하는 방법
this를 바인딩하지 않음 → this는 이전의 값(상위값) 유지

var obj = {
	outer: function() {
		console.log(this); // (1) obj
		var innerFunc = () => {
			console.log(this); // (2) obj
		};
		innerFunc();
	}
}

obj.outer();

오늘의 회고

  1. 데이터 처리 과정을 배우니 이해가 잘 되면서도 어느 한편으로는 머리가 너무 아프고 어렵다. 배우면 배울수록 어려운 코딩... 용어도 생소하고 한 번에 이해하긴 어려운 것 같다.

  2. 2주차 숙제 해설을 봐도 이해가 잘 안되어서 오늘 다시 한 번 봤는데, 여러 번 보니까 이해가 된다. 팀원 중 한명이 해설을 봐도 잘 모르겠다고 했는데, 내가 이해한 부분을 알려줬더니 이해가 된다고 했다. 뭔가 뿌듯했다.

  3. 내일 개인과제 발제가 있는데, 그 전에 문법 5주차까지 1회독을 하는 것이 목표였는데, 아직 3주차까지밖에 못 들어서 마음이 조급하다. 이해도 하고, 따로 노션으로 정리도 하면서 들으니 시간이 오래 걸린다. 내 스타일 상 1회독 때 꼼꼼하게 정리하고 이해하고 넘어간 다음 2회독 때 가볍게 듣는게 좋다. 내일은 꼭 1회독 완료해야지..

내일 할 일

  • [내배캠] Javascript 문법 4, 5주차 완강
  • 개인과제 발제
profile
Frontend Engineers

0개의 댓글