[책 요약] 코어 자바스크립트

mementomori·2021년 4월 14일
0

목차 별 내용 정리


데이터 타입

1. 종류

(1) primitive(원시형) : number, string, boolean, null, undefined ...
(2) reference(참조형) : object, Array, Function, Date, RegExp...

원시형은 값이 담긴 주소값을 바로 복제 & 불변성(immutability)
참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주소값을 복제

2. 데이터 타입에 대한 배경지식

  • 0이나 1인 비트(bit)는 하나의 메모리 조각
  • 메모리의 위치(주소)를 표현하기 위해서는 일정 수준의 비트 묶음(=바이트)이 필요
    (묶음 단위가 너무 작으면 표현할 수 있는 값이 적고, 너무 크면 낭비되는 비트가 발생)
  • 1 바이트(byte) = 8 비트(bit)
  • 메모리 용량이 제한적이던 예전에는 타입 별로 최소한의 용량으로 할당해 놓고, 초과하게 되는 경우 형변환 등을 해야 하는 불편 있었지만, 자바스크립트는 메모리 용량이 충분히 많아진 환경에서 탄생하여 이러한 제약에서 자유로움
  • 모든 데이터는 바이트 단위의 식별자(= 메모리 주솟값)을 통해 서로 구분하고 연결할 수 있음
  • 변수(variable)는 '변할 수 있는 데이터 자체'이고, 식별자(identifier)는 '어떤 데이터를 식별하는데 사용하는 이름(= 변수명)' 임

3. 변수 선언과 데이터 할당

  • 변수 영역에 값을 직접 입력하지 않고, 데이터 영역이라는 한 단계를 더 거치는 이유
    : 데이터 변환을 자유롭게 할 수 있도록 함과 동시에, 메모리를 더욱 효율적으로 관리하기 위함
    (이미 입력된 값이 변경되어, 차지하는 공간이 늘어나는 경우, 이미 저장된 데이터의 위치를 전부 이동시키고, 이동시킨 주소를 각 식별자에 다시 연결해야 하는 작업에 불필요한 연산 발생)
    : 따라서, 입력한 변수가 변경되는 경우에는 기존의 동일 공간에서 할당하지 않고, 새로운 공간에 저장하고, 그 새로운 주소를 기존 식별자(변수명)에 연결

4. 기본형(원시형)데이터와 참조형 데이터

  • 기본형은 불변값, 참조형은 가변값
  • 참조형의 기본형과의 차이점은 객체의 변수(프로퍼티) 영역이 별도로 존재하는점
  • 참조 카운트가 0인 메모리 주소는 garbage collector가 수거하여, 새로운 값을 할당할 수 있는 빈공간이 됨

변수의 복사 비교

var a = 10;
var b = a;

var obj1 = {c:10, d:'ddd'};
var obj2 = obj1;

b = 15
obj2.c = 20

(1) 기본형 데이터 복사의 경우

  • 재할당 되는 변경된 값의 메모리 주소가 변경되어, 기존 변수(a)에 영향 X

(2) 참조형 데이터 복사의 경우

  • c의 값이 재할당 되더라도, 해당 객체(obj2) 내 값들이 저장된 주소들의 정보가 저장된 주소 값은 변경되지 않고 유지되므로, obj2.c의 값이 변경되면, obj1.c의 값도 함께 변경됨
  • 다만, 아래와 같이 obj2 객체 자체를 변경하는 경우에는 기본형과 같이 기존 객체에 영향을 주지 않고 복사됨
...위와 동일
b = 15;
obj2= {c:20, d:'ddd'}

5. 불변객체

  • 객체를 복사하면서도, 상호 간 영향을 주지 않아야 하는 경우 발생하고, 이때 불변객체의 필요성 발생

불변객체 만드는 법

  • 새로운 객체를 반환하는 함수(책에서는 changeName)을 만들면 서로 다른 객체 생성

얕은 복사 VS 깊은 복사

  • 얕은 복사(shallow copy)
    : 중첩된 객체에서, 바로 아래 단계의 값만 복사
    : 얕은 복사하게 되면, 바로 아래 단계 이외에는 값들의 주소가 서로 공유 및 연결되어 있어서, 변경하면 상호 영향을 받게 됨
var copyObject = function(target){
	var result ={};
    for (var prop in target) {
    	result[prop] = target[prop];
    }
    return result;
  • 깊은 복사(deep copy)
    : 중첩된 객체에서 내부 모든 값들을 복사
    : 아래의 3번째 줄에서, target이 객체인 경우 내부 프로퍼티들은 순회하며 copyObjectDeep함수를 재귀적으로 호출하게 되므로 깊은 곳까지 복사 가능
var copyObjectDeep = functon(target){
	var result = {};
    if (typeof target === 'object' && target !== null) {
    	for (var prop in target) {
        	result[prop] = copyObjectDeep(target[prop]);
            }
        } else {
        	result = target;
        }
        return result;
 };

깊은 복사를 간단하게 할 수 있는 방법

객체를 JSON 문법으로 표현된 문자열로 전환 후, 다시 JSON객체로 변경

var copyObjectViaJSON = function (target) {
	return JSON.parse(JSON.stringify(target));
};

6. undefined와 null

  • 공통점 : 둘 모두 "값 없음"을 나타내기 위해 사용
  • 차이점 : undefined는 사용자가 명시적으로 지정할 수 있지만, 변수가 선언만 되고 값이 지정되지 않아도 자바스크립트 엔지이 자동으로 부여
  • 혼동을 피하는 법 (predictable)
    : 값이 없음을 나타내기 위해서는 "null"만 사용할 것
    : "undefined"는 자동으로 부여되는 경우로만 제한

실행 컨텍스트

1. 실행 컨텍스트(excution context)란?

실행할 코드에 제공할 환경 정보들을 모아놓은 객체
(javascript의 hoisting, this 속성과 관련)
흔히 실행 컨텍스트를 구성하는 방법은 함수를 실행 하는 것

  • 실행 컨텍스트에 담기는 정보s
  1. VariableEnvironment: 현재 컨텍스트 내의 식별자들에 대한 정보 + 외부환경 정보, 선언 시점의 LexicalEnvironment의 스냅샷으로, 변경사항은 반영X
  2. LexicalEnvironment: 처음에는 VariableEnvironmnet와 같지만 변경 사항이 실시간으로 반영됨
  3. ThisBinding: this 식별자가 바라봐야할 대상 객체

stack VS queue
stack은 출입구가 하나인 깊은 우물같은 데이터 구조
queue는 양쪽이 열려있는 파이프과 같은 데이터 구조

2. VariableEnvironment

  • LexicalEnvironment와 동일하지만, 최초 실행시의 스냅샷을 유지한다는 차이
  • environmentRecord와 outer-EnvironmentReference로 구성

3. LexicalEnvironment

(1) environmentRecord와 호이스팅

  • environmentRecord에는 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장 (컨텍스트를 구성하는 함수에 지정된 매개변수 식별자, 선언한 함수 자체, var로 선언된 변수의 식별자)
  • environmentRecord는 현재 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에 대한 관심이 있고, 어떤 값이 할당될 지에 는 관심이 없음 => 따라서, 변수를 호이스팅 할 때 변수명만 끌어올리고 할당 과정은 원래 그자리에 그대로 두게 됨 / 단, 함수선언은 함수 전체를 끌어올림

함수 선언문과 함수 표현식(익명, 기명)
: 함수를 새롭게 정의할때 쓰이는 방식 2가지
: 호이스팅하는 경우, 함수 선언문은 전체를 호이스팅 하지만, 함수 표현식의 경우에는 식별자(변수명) 부분만 호이스팅하고 실제 함수는 그자리에 둠

실무에서, 여러사람이 작업하는 긴 코드 내에서 동일한 변수명이 포함되는 경우, 호이스팅 때문에 계속 override되어 마지막에 정의한 함수가 실행되는 오류 발생가능성 있음 => 따라서, 함수 표현식으로 함수를 정의하면 각각의 위치에서 함수가 다르게 작동가능하므로 상대적으로 함수 표현식이 안전

// 함수 선언문
function a () {}
//함수 표현식(익명)
var b = function (){}
//함수 표현식(기명)
var c = function d(){}

(2) 스코프, 스코프 체인, outerEnvironmentReference

Scope란?
: 식별자에 대한 유효범위

Scope Chain이란?
: 식별자의 유효범위를 안에서부터 바깥으로 차례로 검색해 나가는 것
: 이를 가능케 하는 것이 LexicalEnvironmet의 outerEvironmentReference임

  • outerEnvironmentReference는 현재 호출된 함수가 선언될 당시의 LexicalEnvironment를 참조

4. this

  • 실행 컨텍스트의 thisBindingdpsms this로 지정된 객체가 저장됨

this

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

  • 컨텍스트는 함수를 호출할 때 생성되므로, 함수를 호출하는 시점에서 this가 결정됨
  • 전역공간에서의 this = 전역 객체 (브라우저 환경의 window 객체, Node.js 환경의 global 객체)
  • 자바스크립트의 모든 변수는 정확히는 특정 객체의 프로퍼티로 동작(전역공간에서 a를 선언하면, 전역객체의 a라는 프로퍼티로 할당)

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

함수를 실행한는 2가지 방법

  • 함수로서 호출하는 경우 : 그 자체로 독립적 기능 수행
  • 메서드로 호출하는 경우 : 자신을 호출한 대상 객체에 대한 동작을 수행 (obj.~ 식으로 호출하는 경우)

(2) 함수로서 호출할때 그 함수 내부에서의 this

실행 컨택스트를 활성화 할 당시(함수가 호출되는 시점) this가 별도로 지정되지 않으면, this는 전역 객체를 가리킴

  • 메서드 내부 함수에서 this를 우회하는 방법

메서드 내부 함수에서 this를 변수(ex> var self = this)에 담은 다음 호출하도록 하면, 동일하게 메서드 내부 함수를 함수로 호출하더라도 전역 객체가 아닌 해당 객체를 반환

  • this를 바인딩 하지 않는 함수 = "arrow function"
    : ES6에서 도입한 arrow function을 사용하면, 위의 우회법 필요 x
var obj = {
	outer: function() {
    	console.log(this); 			// {outer: f}
        var innerFunc = () =>{
        	console.log(this);		// {outer: f}
        };
        innerFunc()
     }
 };
 obj.outer();

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

  • setTimeout 함수, forEach와 같은 메서드는 내부 콜백함수를 호출시, 대상이 될 this를 지정하지 않아, 콜백함수 내부에서 this는 전역객체를 참조
  • but, addEventListener 함수 내부의 콜백함수를 호출할때는 자신의 this를 상속하게 되어 있음

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

new 명령어와 함께 함수를 호출하면 생성자로 동작하고, 리렇게 생성자로 호출된 경우 내부에서의 this는 곧 새로 만들 구체적인 인스턴스 자신을 가리킴

2. 명시적으로 this를 바인딩하는 방법

(1) call 메서드

Function.prototype.call(thisArg,[ arg1,[, arg2[,...]]])

  • call 메서드는 호출 주체인 함수를 즉시 실행하도록 하는 함수
  • 첫번째 인자를 this로 바인딩하고, 이후 인자들은 호출할 함수의 매개변수로 적용
var func = function(a, b, c) {
	console.log(this, a, b, c);
};
func(1, 2, 3) 					// Window{...}, 1, 2, 3
func.call({x:1}, 4, 5, 6);		// {x:1} 4, 5, 6

(2)apply 메서드

Function.prototype.apply(thisArg[, argsArray])

  • call 메서드와 큰차이 없지만, 두번째 인자를 배열로 받는 다는 차이

(3) call/apply 메서드의 활용

1. 유사배열 객체에 배열 메서드 사용

  • 원칙적으로 object에는 array method를 사용할 수 없지만, 유사배열객체 (키가 0또는 양의 정수인 프로퍼티가 존재하고, length 프로퍼티의 값이 0또는 양의 정수인 객체)는 call or apply method 이용하여 array method 적용가능
  • 문자열도 가능하지만, 문자열의 경우 length property가 읽기 전용이기 때문에 원본 문자열에 변경을 가하는 method는 에러
  • call/apply를 이용한 형변환은 this를 원하는 값으로 지정하여 호출한다는 원래 의도에서 벗어난 활용법임
    => 이에, ES6에서는 유사배열 객체 또는 순환가능 모든 종류의 데이터 타입을 배열로 전환하는 Array.from 메서드가 새로 도입됨
var obj ={
	0: 'a',
    1: 'b',
    2: 'c',
    length: 3
   };
   Array.prototype.push.call(obj, 'd') 
   console.log(obj)   		// {0: 'a', 1: 'b', 2:'c', 3:'d', length: 4}
   var arr = Array.prototype.slice.call(obj);
   console.log(arr);		// ['a', 'b', 'c', 'd']
   //얕은 복사 결과물로 array 반환

** 2. 생성자 내부에서 다른생성자를 호출 **
: 생성자 내부에 다른 생성자와 공통된 내용이 있는 경우, call or apply를 이용해 다른 생성자 호출하면 간단히 반복을 줄일 수 있음

** 3. 여러 인수를 묶어 하나의 배열로 전달하고 싶을 때 - apply**
: (예시-아래) 최소 및 최대값 구하기
: 원래 Math.max(a, b, c, d..) 식으로 들어가야 함
: ES6의 spread syntax 활용하면 더 간단

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);  		// 45, 3

(4) bind 메서드
Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])

  • call과 비슷하지만, 즉시 호출하지 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하는 method
  • 이때 반환되는 새로운 함수는 name property에 bound라는 접두어가 붙음 ('bound xxx'라면, xxx라는 원본 함수에 bind method 반환 함수라는 뜻)
  • ES6에서 도입된 Arrow function을 이용하면, 스코프체인상 가장 가까운 this에 접근하게 됨

(5) 별도의 인자로 this를 받는 경우(콜백함수 내에서의 this)

  • 대표적으로 forEach
  • 콜백함 수 내부에서 this 값을 원하는 대로 변경할 수 있음

콜백함수와 함께 thisArg를 인자로 받는 메서드

Array.prototype.forEach(callback[, thisArg])
map, filter, some, every, find, findIndex, flatMap, from..


콜백함수

콜백함수는 다른 코드의 인자로 넘겨주는 함수

  • '제어권'과 관련이 깊음

제어권

호출시점


클로저


프로토타입


클래스


profile
21c Carpenter

관심 있을 만한 포스트

0개의 댓글