자바스크립트를 좀 더 깊게 이해해보기 위해 Modern Javascript Deep dive를 이용하여 정리해보고자 합니다.
Ajax는 Asynchronous Javascript and XML의 약자로 서버와 브라우저가 비동기 방식으로 데이터를 교환 할 수 있는 통신 기능입니다. Ajax의 등장으로 웹 페이지에서 변경할 필요가 없는 부분은 리렌더링 하지 않고 , 서버로부터 필요한 데이터만 전송받아 변경해야 하는 부분만 한정적으로 렌더링하는 방식이 가능해진 것입니다.(SPA) 최근엔 axios를 많이 사용합니다.
렌더링이란 HTML , CSS , JS로 작성된 문서를 해석하여 브러우저에 시각적으로 출력하는 것을 의미합니다. 때로는 서버에서 데이터를 HTML로 변환해서 브라우저에게 전달하는 과정 (Server Side Rendering)을 가리키기도 합니다.
CSR과 SSR의 차이점 자세한 건 참고 자료로
CSR은 클라이언트 측에서 정적 리소스들을 렌더링하고 , SSR은 서버 측에서 정적 리소스들을 렌더링하여 클라이언트 측에서 보내는 방식입니다.
웹 브라우저에서 동작하는 유일한 프로그래밍 언어로 , 개발자가 별도의 컴파일 작업을 수행하지 않는 인터프리터 언어입니다. 프로토타입 기반 객체 지향 언어이기도 하죠.
컴파일이란 무엇인가 ?

변수란 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름입니다. 변수는 컴파일러 또는 인터프리터에 의해 값이 저장된 메모리 공간의 주소로 치환되어 실행됩니다. 변수는 또한 식별자라고도 불리는데 , 식별자는 값이 저장되어 있는 메모리 주소와 매핑 관례를 맞으며 , 이 매핑 정보도 메모리에 저장되어야 합니다.
메모리는 데이터를 저장할 수 있는 메모리 셀의 집합체입니다. 메모리 셀 하나의 크기는 1byte이며 , 컴퓨터는 메모리 셀의 크기 , 즉 , 1byte 단위로 데이터를 저장하거나 읽어들입니다. 또한 , 각 셀은 고유의 메모리 주소를 갖습니다.
변수 선언은 값을 저장하기 위한 메모리 공간을 확보하고 변수 이름과 확보된 메모리 공간의 주소를 연결하여 값을 저장할 수 있게 준비하는 것입니다. let , const ,var가 변수 선언을 위한 키워드입니다.
변수 할당은 변수에 값을 저장하는 것입니다.
변수 참조는 변수에 저장된 값을 읽어들이는 것입니다.
키워드란 자바스크립트 코드를 해석하고 실행하는 자바스크립트 엔진이 수행할 동적을 규정한 일종의 명령입니다. JS엔진은 키워드를 만나면 자신이 수행해야 할 약속된 동작을 수행합니다.
변수 이름을 비롯한 모든 식별자는 실행 컨텍스트에 등록됩니다. 실행 콘텍스트란 자바스크립트 엔진이 소스 코드를 평가하고 실행하기 위해 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역입니다. JS 엔진은 실행 콘텍스트를 통해 식별자(함수명 , 변수명 , 속성명 , 메소드명)와 스코프를 관리합니다.
변수 이름과 변수 값은 실행 컨텍스트 내에 키/값 형식인 객체로 등록되어 관리됩니다.
변수 선언 (let score , var score)은 런타임 이전 단계에서 먼저 실행된다.
console.log(score); //undefined
var score;
런타임이란 ?
런타임이란 프로그램이 서버에 올라가서 실행되는 시간을 뜻합니다. 실제 프로그래밍 언어가 구동되는 뜻하는데요. JS의 런타임 환경은 Node.js입니다.
JS 엔진은 소스 코드를 한 줄씩 순차적으로 실행하기에 앞서 (인터프리트) 소스 코드의 평가 과정을 거치면서 소스 코드를 실행하기 위한 준비를 합니다. 소스 코드의 평가 과정에서 JS 엔진은 변수 선언을 포함한 모든 선언문 (변수 선언문 , 함수선언문 등)을 소스 코드에서 찾아내 먼저 실행합니다. 그 이후 소스 코드를 한 줄씩 순차적으로 실행합니다.
이러한 JS 엔진의 동작 과정 때문에 ,
console.log(score); //undefined
var score;
와 같이 참조 에러가 나지 않고 undefined가 출력되는 것처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 JS 고유의 특징을 변수 호이스팅이라고 합니다. (변수 선언뿐만 아니라 모든 식별자(var,let,const,function,function*, class)는 호이스팅됩니다.)
값의 할당은 런타임 과정에서 실행됩니다.
console.log(score) //undefined
var score=80;
console.log(score) // 80
값의 재할당과정은 해당 메모리 공간을 지우고 새로 저장하는 것이 아닌 새로운 메모리 공간을 확보하고 그 메모리 공간에 값을 재할당하는 것입니다.
var 키워드를 사용한 변수 선언은 선언 단계와 초기화 단계가 동시에 진행됩니다.
var score;
선언 단계를 통해 변수 이름 score를 실행 콘텍스트에 등록하고 , 초기화 단계를 통해 암묵적으로 undefined를 할당해 초기화됩니다.
JS는 원시 타입 (숫자,문자열,boolean,undefined,null,symbol) , 객체 타입 (객체,함수,배열 등)의 7가지 타입을 제공합니다.
null은 값이 없다는 것을 의도적으로 명시할 때 사용하는 값이고 , undefined는 값이 없다는 것이 아니라 값이 할당된 적이 없다는 것을 의미합니다. undefined는 또한 자바스크립트에 의해 암묵적으로 (var 키워드를 통한 변수 선언과 같이) 저장된 값을 의미합니다.
숫자 타입의 값은 배정밀도 64비트 부동 소수점 형식을 따릅니다.
64비트 부동 소수점이 무엇일까요 ? 일단 오해하지 말아야 할 점은 부동(不動)이 아닌 부동(浮動)-> 떠다닐 부 , 움직일 동. 즉 , 소수점이 떠서 움직인다라는 얘기입니다.
소수점 위치가 고정되어 있어 실수부와 소수부를 나눌 수 있는 고정 소수점과 달리 부동 소수점은 지수와 가수를 이용하여 실수를 표현하는 방식입니다.
배정밀도(64bit)는 단정밀도(32bit)의 정밀도를 가진다는 뜻입니다. 정밀도는 컴퓨터에서 유효 자리 숫자를 의미하는데요. 배정밀도는 단정밀도보다 더 많은 수를 표현할 수 있다라는 것을 알 수 있습니다.
자세한 건 링크를 통해 확인해주세요.
소수점을 표현하는 알고리즘 문제를 풀다보면 특정 부분에서 오류가 발생하는 것을 볼 수 있는데 , 이는 부동 소수점이 이진법을 이용하기 때문에 특정 소수를 정확하게 표현하지 못하기 때문입니다. (ex. 1/3, 0,1+0.2 -> 해보시면 어처구니 없는 답이 나옵니다..) 따라서 , 반올림 오차(toFixed())나 비교 오차가 발생할 수 있는데
JS 엔진은 값의 종류에 따라 정해진 크기의 메모리 공간을 확보합니다. 이는 데이터 타입에 따라 확보해야 할 메모리 공간의 크기가 결정된다는 것입니다.
이후 , 변수를 참조할 때 JS 엔진은 메모리 공간의 선두 메모리 셀의 주소를 찾습니다. 메모리 셀의 개수에 따라 데이터 타입을 인식하고 , 해당 타입에 따라 메모리 공간에 저장된 값을 읽어들입니다.
요약하자면 ,
var 키워드와 let,const 키워드의 중요한 차이는 어떤 스코프를 가지느냐에 있습니다. 모든 식별자 (앞서 설명한 함수 , 변수 , 클래스 등등)들은 자신이 선언된 위치에 의해 다른 식별자가 자신을 참조할 수 있는 유효 범위가 결정됩니다. 따라서 스코프란 JS 엔진이 식별자를 검색할 때 사용하는 규칙이라고 할 수 있습니다. (간단하게 말하면 , 지역 변수나 전역 변수냐의 차이로 이해할 수 있습니다.)
스코프의 종류에는 지역 스코프와 전역 스코프로 나눌 수 있습니다. 전역 변수는 어디서든지 참조할 수 있습니다. 지역이란 함수 몸체 내부를 뜻하는데요. 지역은 지역 스코프를 만들고 , 지역 변수는 자신의 지역 스코프와 하위 지역 스코프에서 유효합니다.
함수는 중첩될 수 있으므로 (지역은 함수 몸체 내부를 뜻하기 때문) 지역 스코프도 중첩될 수 있습니다. 이는 스코프가 함수의 중첩에 의해 계층적 구조를 갖는다는 것을 의미합니다. 이렇게 스코프가 계층적으로 연결된 것을 스코프 체인이라고 합니다.
JS 엔진은 변수르 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색합니다. (상위에서 하위로 내려가는 일은 X) 이는 상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조할 수 있지만 하위 스코프에서 유효한 변수를 상위 스코프에서 참조할 수 없다는 것을 의미합니다.
// 전역 스코프
// 전역 스코프는 logA의 상위 스코프
const a = 10;
// 전역 스코프의 중첩 함수
function logA() {
const b = 10;
console.log(a);
}
logA(); // 10
console.log(b); //ReferenceError : b is not defined
이를 JS 엔진은 어떻게 찾아내고 구현할까 ?
코드의 맥락 (코드가 실행되는 방식, 상위 하위에 어떤 변수가 있는지 찾아내기) 파악은 실행 컨텍스트가 구현한 Lexical 환경에서 이루어집니다..
JS 엔진은 위와 같은 자료 구조인 렉시컬 환경을 실제로 생성합니다.
위에서 말한 것처럼 JS 엔진은 식별자에 대한 렉시컬 환경을 실제로 만든다고 설명했는데요. 그럼 렉시컬 환경은 언제 생성될까요 ?
전역 렉시컬 환경은 코드가 로드되면 곧바로 생성되고 , 함수의 렉시컬 환경은 함수가 호출되면 곧바로 생성됩니다. (선언될 때가 아닌 호출될 때입니다.)
이제 본론으로 돌아와 그래서 var와 let,const의 차이는 무엇일지 알아보겠습니다.
결론적으로 var는 함수 레벨 스코프이고 , let과 const는 블록 레벨 스코프를 지원합니다. var 키워드로 선언된 변수는 오로지 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정하는 반면 ,let과 const는 if , for , while , try/catch등 모든 코드 블록이 지역 스코프로 인정합니다.
코드의 맥락을 읽는 실행 컨텍스트에 의해 구현된 Lexical 환경에서 만들어지며 , Lexical 환경이 만들어지는 시기도 알겠는데 그럼 상위 스코프는 언제 결정될까요 ?
결론적으로 말하자면 JS에서는 함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정합니다.
따라서 , 함수가 호출되면 함수가 정의된 위치를 찾고 상위 스코프를 결정하며 스코프 체인을 형성하는 것입니다.
종종 JS로 코드를 작성하다보면 의도치 않게 타입이 자동 변환되기도 합니다. ex(10+"" //"10") 이를 JS 엔진에 의한 암묵적 타입 변환이라고 합니다.
10+"" // "10"
1+"2" // "12"
+"" //0
+"0" //0
이런 암묵적 타입 변환이 유용할 때도 있지만 , 예기치 않은 동작을 유발할 수 있기 때문에 타입을 명시적으로 정의(String() , Number() , Boolean() 등)하여 반환해주는 것이 중요합니다. (TS의 등장 배경)
인스턴스란 클래스에 의해 생성되어 메모리에 저장된 실체를 말합니다. 클래스는 인스턴스를 생성하기 위한 템플릿의 역할을 하는데요. 객체 지향 프로그래밍에서 객체는 클래스와 인스턴스를 포함한 개념입니다.
원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장됩니다. 이에 비해 객체를 변수에 할당하면 변수(확보된 메모리 공간)에는 참조 값이 저장됩니다.
변수에 원시 값을 갖는 변수를 할당하면 할당받는 변수에는 할당되는 변수의 원시 값이 복사되어 전달됩니다. 이를 값에 의한 전달이라고 하는데요.
변수에 원시 값을 재할당 할 때는 해당 메모리 공간의 값이 바뀌는 것이 아니라 새로운 메모리 공간을 확보한 후 확보된 메모리 공간에 값을 저장하는 것입니다.
객체는 프로퍼티의 개수가 정해져 있지 않고 , 동적으로 추가 삭제가 되기 때문에 원시 값과 같이 확보해야 할 메모리 공간의 크기를 사전에 정해둘 수 없습니다.
따라서 객체를 변수에 저장할 때는 변수의 메모리 공간에 실제 객체가 저장되는 것이 아닌 객체의 참조 값이 저장되게 됩니다.
메모리 공간에 실제 객체가 저장되는 것이 아니라 참조 값이 저장되는 이유는 객체를 복사하거나 수정할 때 새로운 메모리 공간에 해당 객체를 새로 저장하는 것은 비효율적인 일이기 때문입니다. 
얕은 복사는 복사본의 참조 값이 원본의 참조 값과 같은 것을 의미합니다. 위 "객체의 할당 이미지"를 볼 때 0x00001332를 person1이라는 변수에 넣어주는 것과 같습니다. 동일한 메모리 주소를 참조하는 것이죠.
동일한 메모리 주소(참조 값)를 공유하고 있기 때문에 원본과 복사본 둘 중 하나라도 변경된다면 같은 메모리 주소를 참조하고 있는 변수들 또한 변경된 값을 참조하게 됩니다.
깊은 복사는 원본의 메모리 값을 복사해 동일한 메모리 주소를 참조하는 것이 아닌 원본의 객체 상태와 같지만 새로운 메모리 주소에 할당하는 것을 말합니다. 즉 , 원본의 변동이 복사본의 변동으로 이어지지 않는다는 것이죠.
다만 조금 헷갈리는 것은 얕은 복사는 최상위 레벨의 값들은 참조 값이 아닌 값 그 자체를 복사하지만 중첩된 객체는 참조 값을 복사해옵니다. 전부 다 참조 값으로 가져오는 것이 아니라는 것이죠.
let originalObject = {
a: 1,
b: {
c: 2
}
};
let shallowCopy = Object.assign({}, originalObject);
// 최상위 레벨의 속성 변경
originalObject.a = 10;
//c는 참조 값으로 가져오기 때문에 원본 , 복사본 둘 중 하나라도 바뀌면 전부 바뀜.
console.log(originalObject); // { a: 10, b: { c: 2 } }
console.log(shallowCopy); // { a: 1, b: { c: 2 } }
깊은 복사 얕은 복사하는 법 (spread 연산자 지옥에 빠지지 않도록 주의..)
위에서 var 키워드와 let,const 키워드의 차이는 scope 범위의 차이라고 설명했습니다. var 키워드와의 차이와 더불어 let과 const에도 차이가 있습니다.
var 키워드는 런타임 이전에(할당이 일어나는 단계) JS 엔진에 의해 선언 단계(var person)와 초기화 단계(undefined)가 한 번에 진행된다고 설명했습니다. 즉 , 선언 단계에서 렉시컬 환경에 의해 스코프에 식별자를 등록하는 것인데요.
let 키워드는 var 키워드와 달리 선언 단계와 초기화 단계가 분리되어 진행됩니다. 즉 , 런타임 이전에 JS 엔진에 의해 암묵적으로 선언 단계가 먼저 실행되지만 초기화 단계는 런타임 과정에서 변수 선언문에 도달했을 때 실행됩니다. 그래서 var 키워드와 달리 초기화 단계가 실행 이전에 변수에 접근하면 참조 에러가 발생합니다.
스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 일시적 사각지대(Temporal DeadZone -> TDZ)라고 부릅니다.

console.log(foo)
// ReferenceError:foo is not defined
// ReferenceError: Cannot access 'foo' before initialization
let foo; // 변수 선언문에 도달했을 때 초기화 단계가 실행
console.log(foo) // undefined
foo=1;
console.log(foo) // 1
이는 마치 let 키워드는 호이스팅이 발생하지 않는 것처럼 보이는데 , 사실은 그렇지 않습니다.
let foo=1;
{
console.log(foo); // ReferenceError : Cannot access "foo" before initialization
let foo=2;
}
// 함수 레벨 스코프로 들어와 foo가 선언은 되었으나 초기화 단계가 진행되지 않았기 때문에 참조 오류
오류문을 보면 , 런타임 이전에 함수 레벨 스코프에서 변수는 선언되었으나 , 선언문(초기화 단계)에 도달하지 못하고 변수에 접근을 하기 때문에 초기화되지 않았다는 오류문을 뱉는 것입니다. 즉, 호이스팅이 발생하고 있다는 얘기죠.
JS 엔진은 let,const를 포함해서 모든 선언 (var,let,const,function,class 등)을 모두 호이스팅합니다. 다만 , let ,const ,class를 사용한 선언문은 호이스팅이 발생하지 않는 것처럼 동작합니다.
const 키워드는 let과 달리 변수 선언과 초기화를 반드시 동시에 진행해줘야 합니다. 왜냐하면 , const는 상수를 의미하기 때문에 undefined -> 할당한 값으로 바뀌는 불변성 법칙에 어긋나기 때문입니다.
요약하자면 , let과 const는 동일하게 호이스팅이 발생한다. 다만 , 선언과 초기화의 시기가 다르다라고 볼 수 있습니다.
생성자 함수란 new 연산자와 함께 호출하여 객체를 생성하는 함수를 말합니다. 생성자 함수에 의해 생성된 객체를 인스턴스라 합니다.
생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿으로서 동작합니다. 즉, 인스턴스를 생성하고, 생성된 인스턴스를 초기화하는 것입니다.
new 연산자로 생성자 함수를 호출하면 자바스크립트 내부에서 어떻게 동작할까요 ?
인스턴스 생성과 this 바인딩
암묵적으로 빈 객체가 생성됩니다. 이 빈 객체가 바로 생성자 함수가 생성한 인스턴스입니다.
암묵적으로 생성된 빈 객체 , 즉 인스턴스는 this에 바인딩됩니다.
인스턴스 초기화
this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하고 생성자 함수가 인수로 전달받은 초깃값을 인스턴스 프로퍼티에 할당하여 초기화하거나 고정 값을 할당합니다.
인스턴스 반환
생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환됩니다. 만약 이 과정에서 this가 아닌 다른 객체를 명시적으로 반환하면 (return문 사용) this가 반한되지 못하고 return문에 명시한 객체가 반환됩니다. (하지만 , 명시적으로 원시 값을 반환하면 원시 값 반환은 무시되고 암묵적으로 this가 반환됩니다. 제멋대로 동작) 생성자 함수 내부에 return문을 반환하는 것은 생성자 함수의 기본 동작을 훼손하기 때문에 return문은 생략해야 합니다.
클래스 내부의 메서드는 자신이 속한 객체의 상태 , 즉 프로퍼티를 참조하고 변경할 수 있어야 합니다. 그러려면 , 먼저 자신이 속한 객체를 가리키는 식별자를 참조할 수 있어야 하는데 , 이 때 this를 사용합니다.
this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수입니다. this를 통해 프로퍼티나 메서드를 참조할 수 있는 것이죠.
// 객체 리터럴
const circle = {
radius: 5,
getDiameter() {
// this는 메서드를 호출한 객체를 가리킨다.
return 2 * this.radius;
}
};
console.log(circle.getDiameter()); // 10
메서드를 호출하면 arguments 객체(mdn arguments object)와 this가 암묵적으로 함수 내부에 전달됩니다. 단, this가 가리키는 값 , 즉 this 바인딩은 함수 호출 방식에 의해 동적으로 결정됩니다. 즉 , 함수를 어떻게 호출했느냐에 따라 this가 어떤 곳을 참조하는지 달라진다는 것이죠.
function callThis() {
console.log(this);
}
callThis();
//<ref *1> Object [global] {
global: [Circular *1],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
structuredClone: [Function: structuredClone],
atob: [Getter/Setter],
btoa: [Getter/Setter],
performance: [Getter/Setter],
navigator: [Getter],
fetch: [Function: fetch],
crypto: [Getter]
}
const obj = {
// 메서드 내부에서 this 호출
foo() {
console.log(this);
},
};
// 메서드를 호출한 객체에 바인딩
obj.foo();
// { foo: [Function: foo] }
function Person(name, age) {
this.name = name;
this.age = age;
console.log(this);
}
const person1 = new Person("John", 30);
// Person { name: 'John', age: 30 }
1) Function.prototype.apply
/**
* 주어진 this 바인딩과 인수 리스트 배열을 사용하여 함수를 호출한다.
* @param thisArg - this로 사용할 객체
* @param argsArray - 함수에게 전달할 인수 리스트의 배열 또는 유사 배열 객체
* @returns 호출된 함수의 반환 값
* */
Function.prototype.apply(thisArg, [...argsArray]);
2) Function.prototype.call
/**
* 주어진 this 바인딩과 ,로 구분된 인수 리스트를 사용하여 함수를 호출한다.
* @param thisArg
* @param arg1,arg2 ... - 함수에게 전달할 인수 리스트
* @returns 호출된 함수의 반환 값
*/
Function.prototype.call(thisArg, [arg1[arg2]]);
function getThisBinding() {
return this;
}
const thisArg = { a: 1 };
console.log(getThisBinding());
console.log(getThisBinding.apply(thisArg));
console.log(getThisBinding.call(thisArg));
{
/* <ref *1> Object [global] {
global: [Circular *1],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Getter]
},
queueMicrotask: [Function: queueMicrotask],
structuredClone: [Function: structuredClone],
atob: [Getter/Setter],
btoa: [Getter/Setter],
performance: [Getter/Setter],
navigator: [Getter],
fetch: [Function: fetch],
crypto: [Getter]
}
{ a: 1 }
{ a: 1 } */
}
apply와 call 메서드의 본질적인 기능은 함수를 호출하는 것입니다. apply와 call 메서드는 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩합니다.
이와 달리
3) Function.prototype.bind 메서드는 함수를 호출하지 않고 this로 사용할 객체만 전달합니다. 이는 메서드의 this와 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 불일치하는 문제를 해결하기 위해 유용하게 사용됩니다.
예시를 들어봅시다.
const person = {
name: "Lee",
foo(callback) {
setTimeout(callback, 100);
},
};
person.foo(function () {
// 일반 함수로 호출된 콜백 함수 내부의 this는 window를 가리킨다.
console.log(`Hi my name is ${this.name}`); // window.name은 undefined
});
우리가 의도한 코드는 person 객체 내부의 name(Lee)을 출력하고 싶은데 , window.name이 바인딩되는 바람에 undefined가 출력됩니다.
이런 문제를 해결하기 위해
const person = {
name: "Lee",
foo(callback) {
// 이 시점에서의 this는 객체를 foo 메서드를 호출한 객체 , person을 가리킨다.
setTimeout(callback.bind(this), 100);
},
};
person.foo(function () {
// 일반 함수로 호출된 콜백 함수 내부의 this는 window를 가리킨다.
console.log(`Hi my name is ${this.name}`);
});
// Hi my name is Lee
strict mode는 js 문법을 좀 더 엄격히 적용하여 오류를 발생시킬 가능성이 높거나 JS 엔진의 최적화 작업에 문제를 일으킬 수 있는는 코드에 대해 명시적인 에러를 발생시킵니다. -> ESLint 사용하면 해결됨 .
(계속 업데이트 할 예정입니다.)