[2024.04.22 TIL] 내일배움캠프 6일차 (데이터 타입, 실행 컨텍스트, this)

My_Code·2024년 4월 22일
0

TIL

목록 보기
8/112
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.
(강의보면서 따로 정리가 필요해 보이는 내용을 정리한 내용)


💻 TIL(Today I Learned)

📌 Today I Done

✏️ 3주차 (데이터 타입(심화), 실행 컨텍스트, this)

1) 데이터 타입의 종류(기본형과 참조형)


2) 변수 선언과 데이터 할당

  • 값을 바로 변수에 대입하지 않는 이유는?
    => 자유로운 데이터 변환
    => 메모리의 효율적인 관리
// 배열의 경우
/** 선언과 할당을 풀어 쓴 방식 */
var str;
str = 'test!';

/** 선언과 할당을 붙여 쓴 방식 */
var str = 'test!';


3) 기본형 데이터

  • 변수 vs 상수
    => 변수 : 변수 영역 메모리를 변경할 수 있음
    => 상수 : 변수 영역 메모리를 변경할 수 없음
  • 불변하다 vs 불변하지 않다
    => 불변하다 : 데이터 영역 메모리를 변경할 수 없음
    => 불변하지 않다 : 데이터 영역 메모리를 변경할 수 있음
// a라는 변수가 abc에서 abcdef가 되는 과정을 통해 불변성을 유추해봅시다!

// 'abc'라는 값이 데이터영역의 @5002라는 주소에 들어갔다고 가정할게요.
var a = 'abc';

// 'def'라는 값이 @5002라는 주소에 추가되는 것이 아니죠!
// @5003에 별도로 'abcdef'라는 값이 생기고 a라는 변수는 @5002 -> @5003
// 즉, "a는 변수이고 불변하다." 라고 할 수 있습니다.
// 이 때, @5002는 더 이상 사용되지 않기 때문에 가비지컬렉터의 수거 대상이 됩니다.
a = a + 'def';
  • a는 변수이고 불변하다라고 할 수 있음


4) 참조형 데이터

  • obj1도 데이터 메모리 영역이라고 할 수 있음
  • 그렇기에 데이터 메모리 영역이 변했기 때문에 참조형 데이터는 불변하지 않다(가변하다)라고 할 수 있음
var obj1 = {
	a: 1,
	b: 'bbb',
};

// 데이터를 변경해봅시다.
obj1.a = 2;
  • 참조형 데이터의 변수 할당
  • 데이터를 변경했을 때

5) 중첩 객체의 할당

  • 객체는 배열, 함수 등을 포함하는 상위개념이기 때문에 아래의 객체는 중첩 객체라도 할 수 있음
var obj = {
	x: 3,
	arr: [3, 4, 5],
}

// obj.arr[1]의 탐색과정은 어떻게 될까요? 작성하신 표에서 한번 찾아가보세요!


6) 변수의 복사

  • 객체는 배열, 함수 등을 포함하는 상위개념이기 때문에 아래의 객체는 중첩 객체라도 할 수 있음
// STEP01. 쭉 선언을 먼저 해볼께요.
var a = 10; //기본형
var obj1 = { c: 10, d: 'ddd' }; //참조형

// STEP02. 복사를 수행해볼께요.
var b = a; //기본형
var obj2 = obj1; //참조형

// obj.arr[1]의 탐색과정은 어떻게 될까요? 작성하신 표에서 한번 찾아가보세요!

  • 복사 이후 값 변경(객체의 프로퍼티 변경)
    => 기본형 데이터 : a와 b는 서로 다른 데이터 영역의 주소를 바라보고 있기 때문에 영향 없음
    => obj1도 똑같은 주소를 바라보고 있기 때문에 obj1까지 변경이 됨
// STEP01. 쭉 선언을 먼저 해볼께요.
var a = 10; //기본형
var obj1 = { c: 10, d: 'ddd' }; //참조형

// STEP02. 복사를 수행해볼께요.
var b = a; //기본형
var obj2 = obj1; //참조형

b = 15;
obj2.c = 20;


7) 얕은 복사

8) 깊은 복사

  • 내부의 모든 값들을 하나하나 다 찾아서 모두 복사하는 방법
  • 결론 : 객체의 프로퍼티 중, 기본형 데이터는 그대로 복사 + 참조형 데이터는 다시 그 내부의 프로퍼티를 복사 ⇒ 재귀적 수행!
var copyObjectDeep = function(target) {
	var result = {};
	if (typeof target === 'object' && target !== null) {
		for (var prop in target) {
			result[pop] = copyObjectDeep(target[prop]);
		}
	} else {
		result = target;
	}
	return result;
}
  • 또 다른 방법 : JSON(=JavaScript Object Notation)을 이용하는 방법

9) 실행 컨텍스트란?

  • 실행 컨텍스트 는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체
  • 동일 환경에 있는 코드를 실행할 때 필요한 환경 정보들을 모아 컨텍스트를 구성하고 이것을 위에서 설명드린 ‘스택’의 한 종류인 콜스택 에 쌓아올림

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

  • VariableEnvironment (VE)
    => 현재 컨텍스트 내의 식별자 정보(=record)를 갖고 있음
    => 외부 환경 정보(=outer)를 갖고 있음
  • LexicalEnvironment (LE)
    => VariableEnvironment와 동일하지만, 변경사항을 실시간으로 반영
  • ThisBinding

10) VariableEnvironment, LexicalEnvironment의 개요

  • VE, LE모두 동일하며, ‘environmentRecord’와 ‘outerEnvironmentReference’로 구성
  • environmentRecord(=record)
    => 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장
    => 함수에 지정된 매개변수 식별자, 함수자체, var로 선언된 변수 식별자 등
  • outerEnvironmentReference(=outer)

11) LexicalEnvironment(1) - environmentRocord(=record)

  • 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장(기록)
  • 컨텍스트 내부를 처음부터 끝까지 순서대로 훑어가며 수집
  • 순서대로 수집한다고 했지, 코드가 실행된다고 하지는 않음

12) 호이스팅

  • 변수 정보 수집 과정을 이해하기 쉽게 설명한 ‘가상 개념’
  • 식별자 = 변수명(var test)
  • 호이스팅 법칙(규칙)
  1. 매개변수 및 변수는 선언부를 호이스팅 함
    => 나의 예상 : 1 -> undefined -> 2
    => 실제 결과 : 1 -> 1 -> 2
    => 호이스팅이라는 개념을 모르면 예측이 불가능한 어려운 결과임
// <적용전>
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기

function a (x) {
	console.log(x);
	var x;
	console.log(x);
	var x = 2;
	console.log(x);
}
a(1);
// <호이스팅 적용>
//action point 1 : 매개변수 다시 쓰기(JS 엔진은 똑같이 이해한다)
//action point 2 : 결과 예상하기
//action point 3 : hoisting 적용해본 후 결과를 다시 예상해보기

function a () {
	var x;
	var x;
	var x;
	x = 1;
	console.log(x);
	console.log(x);
	x = 2;
	console.log(x);
}
a(1);
  1. 함수 선언은 전체를 호이스팅함
    => 나의 예상 : 에러(또는 undefined), ‘bbb’, b함수
    => 실제 결과 : b함수, ‘bbb’, ‘bbb’
    => 함수 정의 방식에 따라서 다르게 실행되어 적용될 수 있음
    => 함수 정의 방식 : 함수 선언문, 함수 표현식 (익명함수, 가명함수)
    => 호이스팅 시 함수 선언문은 함수 전체가 위로 올라감
    => 호이스팅 시 함수 표현식은 선언부만 위로 올라감
    => 그렇기에 그 함수가 영향을 미치는 범위가 달라짐
// <적용전>
//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기

function a () {
	console.log(b);
	var b = 'bbb';
	console.log(b);
	function b() { }
	console.log(b);
}
a();
// <호이스팅 적용>
//action point 1 : 결과 값 예상해보기
//action point 2 : hoisting 적용해본 후 결과를 다시 예상해보기

function a () {
	var b;  // 변수 선언부 호이스팅
	function b() { }  // 함수 선언은 전체를 호이스팅
	console.log(b);
	b = 'bbb';  // 변수의 할당부는 원래 자리에
	console.log(b);
	console.log(b);
}
a();

13) LexicalEnvironment(2) - 스코프, 스코프 체인, outerEnvironmentReference(=outer)

  • 스코프 : 식별자에 대한 유효범위를 의미
  • 스코프 체인 : 식별자의 유효범위를 안에서 밖으로 차례대로 검색하는 것
    => outer는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조
    => 가장 가까운 요소부터 차례대로 접근 가능
  • outer : 스코프 체인이 가능토록 하는 것 (외부 환경의 참조정보)

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


14) 상황에 따라 달라지는 this

  • this는 실행 컨텍스트가 생성될 때 결정됨
  • 즉, this는 함수가 호출될 때 결정된다라고 할 수 있음
  • 전역 환경에서의 this => 전역 객체를 가리킴
  • 함수 vs 메서드
    => 함수 : 그 자체로 독립적인 기능 수행, this -> 전역객체
    => 메서드 : 실행(호출)의 주체가 필요, this -> 호출 주체, ex) 객체.메서드명( )
  • 메서드로서 호출할 때 그 메서드 내부에서의 this
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
  • 함수로서 호출할 때 그 함수 내부에서의 this
    => 호출 주체를 알 수 없음
    => '독립적으로' 호출할 때 this는 전역객체를 가리킴
    => 메서드의 내부라고 해도, 함수로서 호출한다면 this는 전역 객체를 의미
var obj1 = {
	outer: function() {
		console.log(this);  // obj1 객체를 가리킴
		var innerFunc = function() {
			console.log(this);  
		}
		innerFunc();  // 전역 객체를 가리킴
		var obj2 = {
			innerMethod: innerFunc
		};
		obj2.innerMethod();  // obj2 객체를 가리킴
	}
};

obj1.outer();

15) this 우회 방법

  • 변수를 활용하는 방법
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();
  • 화살표 함수(=this를 바인딩하지 않는 함수)
    => ES6에서 함수 내부에서 this를 바라보는 문제 때문에 생겼다고 해도 과언이 아님
    => 일반 함수와 화살표 함수의 가장 큰 차이점은? this binding 여부
var obj = {
    outer: () => {
        console.log(this);  // (1) obj
        var innerFunc = () => {
            console.log(this);  // (2) obj
        };
        innerFunc();
    }
}
obj.outer();

16) 콜백 함수 호출 시 그 함수 내부에서의 this

  • 콜백 함수도 함수
  • 기본적으로 콜백 함수에서의 this는 전역 객체를 가리킴
  • 단, 예외는 존재
    => addEventListner 메서드는 콜백 함수 호출 시, 자신의 this를 상속하므로, this는 addEventListner의 앞부분(button 태그)
// 별도 지정 없음 : 전역객체
setTimeout(function () { console.log(this) }, 300);

// 별도 지정 없음 : 전역객체
[1, 2, 3, 4, 5].forEach(function (x) {
    console.log(this, x);
});

// addListener 안에서의 this는 항상 호출한 주체의 element를 return하도록 설계되었음
// 따라서 this는 button을 의미함
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function (e) {
    console.log(this, e);
});

17) 생성자 함수 내부에서의 this

  • 객체 생성 시, 생성된 객체를 가리킴
var Cat = function (name, age) {
    this.bark = '야옹';
    this.name = name;
    this.age = age;
};

var choco = new Cat('초코', 7);  //this : choco
var nabi = new Cat('나비', 5);  //this : nabi

18) 명시적 this binding

  • 자동으로 부여되는 상황별 this의 규칙을 깨고 this에 별도의 값을 저장하는 방법
  • call, apply, bind 메서드들을 활용

19) call 메서드

  • 첫 번째 매개변수에 this로 binding할 객체를 넣어주면 명시적으로 binding
  • 즉시 호출 메서드
var obj = {
    a: 1,
    method: function (x, y) {
        console.log(this.a, x, y);
    }
};

obj.method(2, 3);  // 1 2 3
obj.method.call({ a: 4 }, 5, 6);  // 4 5 6

20) apply 메서드

  • call 메서드와 거의 동일한 형태
  • 객체 다음에 넣는 매개변수를 배열 형태로 만들어서 사용
  • 즉시 호출 메서드
var obj = {
    a: 1,
    method: function (x, y) {
        console.log(this.a, x, y);
    }
};

obj.method(2, 3);  // 1 2 3
obj.method.apply({ a: 4 }, [5, 6]);  // 4 5 6

21) 유사배열객체

  • 원래는 call 메서드를 이용해서 배열로 만듦
  • 하지만 ES6에서 Array.from()메서드로 객체를 배열로 만들 수 있게 함
  • Array.from() 메서드를 사용해도 인덱스 번호인 key와 길이 값은 필요함
// 유사배열
var obj = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};

// 객체 -> 배열
var arr = Array.from(obj);

// 찍어보면 배열이 출력됩니다.
console.log(arr);

22) 생성자 내부에서 다른 생성자를 호출(공통된 내용의 반복 제거)

  • 공통인 부분을 따로 생성자 함수로 만들어서 공유해서 사용
function Person(name, gender) {
    this.name = name;
    this.gender = gender;
}

function Student(name, gender, school) {
    Person.call(this, name, gender); // 여기서 this는 student 인스턴스!
    this.school = school;
}

function Employee(name, gender, company) {
    Person.apply(this, [name, gender]); // 여기서 this는 employee 인스턴스!
    this.company = company;
}

var kd = new Student('길동', 'male', '서울대');
var ks = new Employee('길순', 'female', '삼성');

23) apply와 spread 연산자를 이용한 최소, 최대값

  • 원래는 반복문과 조건문을 이요해서 배열의 최소, 최대값을 구함
  • 하지만 apply와 spread 연산자로 간단히 구할 수 있음
//효율
var numbers = [10, 20, 3, 16, 45];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max, min);

// 펼치기 연산자(Spread Operation)를 통하면 더 간편하게 해결도 가능해요
const numbers = [10, 20, 3, 16, 45]
const max = Math.max(...numbers);
const min = Math.min(...numbers);
console.log(max, min);

24) bind() 메서드

  • call과 apply와는 다르게 즉시 호출하지 않음
  • bind() 메서드의 목적
    => 함수에 this를 미리 적용
    => 부분 적용 함수 구현할 때 용이
var func = function (a, b, c, d) {
    console.log(this, a, b, c, d);
};
func(1, 2, 3, 4);  // global

// 함수에 this 미리 적용
var bindFunc1 = func.bind({ x: 1 }); // 바로 호출되지는 않아요! 그 외에는 같아요.
bindFunc1(5, 6, 7, 8); // { x: 1 } 5 6 7 8

// 부분 적용 함수 구현
var bindFunc2 = func.bind({ x: 1 }, 4, 5); // 4와 5를 미리 적용
bindFunc2(6, 7); // { x: 1 } 4 5 6 7
bindFunc2(8, 9); // { x: 1 } 4 5 8 9


✏️ 3주차 숙제

  • 실행 결과를 작성
  • 왜 그렇게 출력되는지 설명
var fullname = 'Ciryl Gane'

var fighter = {
    fullname: 'John Jones',
    opponent: {
        fullname: 'Francis Ngannou',
        getFullname: function () {
            return this.fullname;
        }
    },
    getName: function () {
        return this.fullname;
    },
    getFirstName: () => {
        return this.fullname.split(' ')[0];
    },
    getLastName: (function () {
        return this.fullname.split(' ')[1];
    })()
}

console.log('Not', fighter.opponent.getFullname(), 'VS', fighter.getName());
console.log('It is', fighter.getName(), 'VS', fighter.getFirstName(), fighter.getLastName);

< 출력 >
Not Francis Ngannou VS John Jones
It is John Jones VS Ciryl Gane

< 이유 >
1. vscode에서 실행하면 에러 발생 => node의 전역 객체인 global에서 fullname을 찾으려고 하면 없기 때문에
2. 크롬 개발자 도구에서 실행하면 정상 실행 => 전역 객체인 window에서는 fullname을 찾을 수 있기 때문에
3. fighter.opponent.getFullname() 에서 this.fullname의 this는 opponent 객체를 가리키기 때문에
4. fighter.getName() 에서 반환하는 this.fullname의 this는 fighter 객체를 가리키기 때문에
5. fighter.getFirstName() 메서드는 화살표 함수를 사용하기 때문에 전역에서 fullname이라는 변수를 찾기 때문에
6. fighter.getLastName의 안에 있는 표현은 즉시 실행하라는 의미로, 함수로써 동작하기에 this는 전역 객체를 가리킴


📌 Tomorrow's Goal

✏️ 남아있는 강의 모두 시청하기

  • 현재 절반정도 강의를 시청함
  • 나머지 절반을 끝내고 그 다음날부터 개인과제에 들어갈 예정
  • 오늘처럼 강의를 시청하고 잘 모르는 부분을 따로 메모해뒀다가 정리


📌 Today's Goal I Done

✔️ 자바스크립트 문법 강의 시청(개인 공부)

  • 생각보다 제공받은 강의에서 자바스크립에 대해서 구체적으로 설명함
  • 기본적인 내용을 알기에 빠르게 보면서 잘 모르는 부분만 반복 학습
  • 여기에 작성한 내용들은 강의를 보면서 처음보거나 헷갈리는 내용을 정리함

profile
조금씩 정리하자!!!

0개의 댓글