[JS] Execution Context, Hoisting, arguments

colki·2021년 5월 6일
1

Udemy_JavaScript: The Advanced JavaScript Concepts (2021) 강의를 바탕으로 메모한 내용입니다.

실행컨텍스트 생성, 정보수집, 콜스택에 담김 이 세가지의 순서는 정확히 모르겠습니다..

Executing Context

Reference javascripttutorial

JavaScript 엔진은 해당 컨텍스트에 관련된 코드들을 실행하는데 필요한 환경 정보들을 수집해서 실행컨텍스트 객체에 저장한다.

정보 수집 후 실행 컨텍스트를 생성하는 단계 Creation Phase
수집한 정보를 바탕으로 실행하는 단계 Execution Phase 로 구성된다.

Creation Phase ( Global Execution Context )


Setup a memory heap for storing variables and function references.

Store the function declarations in the memory heap and variables within the global execution context with the initial values as undefined.

  • Scope 정보

  • ThisBining: 해당 컨텍스트 내의 객체와 this 가 생성된다.

  • 변수와 함수참조가 메모리힙에 저장된다.

  • 호이스팅 : 이때는 무슨 값인지 관심이 없기 때문에 최상단에
    끌어올리기만 하고 값을 할당하는 부분은 그 자리에 남겨둔다.

    • 모든 변수 선언문은 자신이 속한스코프 내에서 최상위로 Hoisting 된다
    • 변수와 함수참조를 정의되지 않은 상태로 초기화 한다.
    • 변수 "선언"이 있어야 호이스팅이 일어난다
  • 코드 실행 전 코드를 쭉 읽어내려가는 단계.

⏩ 함수가 호출되면 해당 함수 컨텍스트가 활성화되면서 Creation Phase단계를 거친다.
이 단계에서 Variable EnvironmentLexical Environment 그리고 ThisBinding 등의 정보를 실행컨텍스트에 담고 나서 Executing Phase에서 코드를 실행한다.

Executing Phase ( Function Execution Context )


Instead of creating the global object, it creates the arguments object that contains a reference to all the parameters passed into the function.

  • 전역객체를 만드는 대신 함수에 전달된 모든 매개변수에 대한 참조를 갖는 arguments object를 생성한다. 그리고 이 arguments object 값을 전역 객체로 설정하고 매개변수를 undefined로 초기화한다.

  • 자바스크립트 엔진이 코드를 한 줄 씩 실행한다.

  • 변수에 값을 할당하고 함수호출을 실행한다.

  • 모든 함수 호출에 대해 JavaScript 엔진은 새로운 함수 실행 컨텍스트를 생성한다.

To keep track of all the execution contexts including the Global Execution Context and Function Execution Contexts, the JavaScript engine uses a data structure named call stack

⏩ 앞서 수집한 정보를 바탕으로 코드를 실행한다. 호이스팅은 앞에서 진행했으니 이제서야 비로소 값을 할당하고 reference라면 주소를 찾아가는 것도 지금 하는 일이다. 코드를 실행하고 임무가 완료되면 콜스택에서 제거된다.

Global Execution Context

whenever code is run in JavaScript it is run inside of an execution context.

Anytime we run code in JavaScript it's always going to be part of an execution context.

JavaScript engine is going to create a global execution context.


가장 맨 처음에 생성되는 실행 컨텍스트이다.

The first thing the JavaScript engine does is to create this global execution context and it gives you two things. (create)

  • Global Execution Context가 생성
  • Global Object (window) 생성
  • ThisBinding: this가 생성되고, this는 window를 가리킨다.

자바스크립트 엔진은 global execution context을 생성하고, 여기에는 Global Object와 this 또한 생성된다.

그래서 JS파일에 아무것도 입력하지 않아도, Global Object는 window,

또한 this는 window를 가리킨다. this === window

왜냐하면 브라우저는 globa execution context가 베이스로 call stack에 깔리고 제일 먼저 실행되기 때문이다.

Hoisting


호이스팅은 const, var, let, 함수 선언문 등을 최상단으로 끌어올려서 먼저 해석하는 행위를 말한다.

무슨 값인지 관심이 없기 때문에 최상단에
끌어올리기만 하고 값을 할당하는 부분은 그 자리에 남겨둔다.

  • 모든 변수 선언문은 자신이 속한스코프 내에서 최상위로 Hoisting 된다
  • 변수와 함수참조를 정의되지 않은 상태로 초기화 한다.
  • 변수 "선언"이 있어야 호이스팅이 일어난다

hoisting is the behavior of moving the variables or function declarations to the top of their respective environments during compilation phase.

it's important to remember that during this creation phase we also have this act of hoisting something.

Compiler allocate space for them in our heap to make sure that the JavaScript engine is ready for the execution.

변수를 선언할 때 사용하는 var, const, let키워드에 따라 호이스팅, 특징이 다르고

Colki Velog_ Hoisting-var-let-const

함수선언문이나 함수표현식이냐에 따라 차이가 있다.

Colki Velog_ 실행컨텍스트에서의-호이스팅


변수와 함수의 호이스팅에는 차이가 있다.

teddy는 undefined인데 sayHi함수는 정상으로 출력됐다.

console.log(teddy); // undefined
console.log(sayHi()); // 'HiHiHi!'

var teddy = 'bear';

function sayHi() {
  console.log('HiHiHi!')
}

variables are partially hoisted

Underneath the hood This is what it might look like with hoisting where Teddy is now undefined.


functions are fully hoisted. ( during the creation phase assigned a location in memory)

함수선언문은 함수 전체가 통째로 끌어올려진다. 메모리에 값이 할당된다.

/** 호이스팅 **/

var teddy = undefined;

function sayHi() { // 메모리에 할당
  console.log('HiHiHi!')
}

console.log(teddy); // undefined

console.log(sayHi()); // 'HiHiHi!'

var teddy = 'bear';

또한 중복되는 변수가 있더라도 마지막 값으로 할당된다.

a(); // bye

function a() {
  console.log('hi'); // lost the ability
}

a(); // bye

function a() {
  console.log('bye'); // rewrite that place in memory
}

a(); // bye

Hoisting ex.

var favoriteFood = 'grapes';

var foodThoughts = function () {
  console.log(
    'Original favorite Food:' + favoriteFood // Original favorite Food:undefined
  );

  var favoriteFood = 'sushi';

  console.log(
    'New Favorite Food:' + favoriteFood // New Favorite Food:sushi
  );
}

foodThoughts();

첫번째 favoritFood 는 왜 undefined가 나올까?

/* start executing the code. */

var favoriteFood = undefined;
favoriteFood = 'grapes'; // assign

var foodThoughts = undefined;
foodThoughts = function () {
-----------creation phase is done ----

// => 2. context hoisting happens during the creation phase.

  favoritFood = undefined;

  console.log(
    'Original favorite Food:' + favoriteFood 
    // Original favorite Food:undefined
  );

  favoriteFood = 'sushi';

  console.log(
    'New Favorite Food:' + favoriteFood 
    // New Favorite Food:sushi
  );
}

foodThoughts(); //=> 1. "()" New Exection context is created

만약 foodThoughts함수 내에서
var favoriteFood = 'sushi'; 가 없었다면 콘솔에는 둘다 grapes가 출력됐을 것이다.

하지만 지금은 함수 내부에 favoritFood 변수가 존재하기 때문에 전역객체에서 찾지 않는다.
undefined도 값은 값이기 때문에 undefined를 뱉는 것이다.😑

사실 이렇게 undefined로 출력되는 건 에러는 아니기 때문에
자칫 지나쳐버릴 수도 있다. 이런 곤란한 상황을 캐치하려면 어떻게 해야 할까?

이때, 키워드를 var가 아닌 const / let으로 바꾸면 개선할 수 있다.

const favoriteFood = 'grapes';

const foodThoughts = function () {
  console.log(
    'Original favorite Food:' + favoriteFood 
    // ReferenceError
    // Cannot access 'favoriteFood' before initialization
  );

  const favoriteFood = 'sushi';

  console.log(
    'New Favorite Food:' + favoriteFood Food:sushi
  );
}

foodThoughts();

우린 콘솔에서 error을 쉽게 발견할 수 있기 때문에 코드를 수정해서 해결할 수 있게 된다.

Function Invocation 함수호출


var canada = () => {
  console.log('cold');
}

function india() { // 이미 
  console.log('warm');
}

함수표현식

var canada

함수가 변수에 값으로써 할당되어 있는 형태이므로, var a = 와 같은 방식으로 호이스팅 된다.

때문에 호이스팅 됐을 때 undefined로 초기화되고, 코드가 실행되면 그때서야

함수가 변수에 담긴다.

var키워드가 아니라 let, const라면 다른 양상을 보일 것이다.

https://velog.io/@colki/JS-Scope-Hoisting-var-let-const

함수선언문

india

함수선언문은 통째로 호이스팅 되기 때문에 undefined가 될 일이 없다!

when the compiler initially looks at the code and starts hoisting and allocating memory

india함수는 컴파일러가 처음에 코드를 보고 호이스팅을 시작하고 메모리에 할당할 때 먼저 정의된다.

이 함수의 실행컨텍스트가 열리면 이 안에도 역시 this가 생성된다.

그런데 다른 객체가 또 있다. 바로 arguments이다.

arguments


function marry(person1, person2) {
  console.log(arguments);
  // {0: "jingoo", 1: "hani"}
  return `${person1} is married fo ${person2}`;
  //
}

console.log(marry('jingoo', 'hani'));
// jingoo is married fo hani

console.log(arguments); // arguments is not defined

함수스코프 밖 전역에서 arguments 객체는 not defined이다.

Arguments is not defined because while arguments is only available to us when we create a new execution

arguments는 오직 함수의 실행컨텍스트가 생성되었을 때 그 내부에서만 존재하는 객체이다.

만약 매개변수가 존재하지 않는 함수라면 arguments는 빈객체이다. {}


array-like

arguments는 또한 array-like 유사배열이기 때문에 배열메서드를 사용할 수 없다. 또한 이대로 사용할 경우 최적화에 문제가 있기 때문에

아래와 같이 사용하기를 권장한다.

  • Array.from()

그래서 Array.from(arguments) 로 타입을 배열로 바꿔서 메서드를 사용할 수 있다.

  • default parameter
function foo(...args) {
  console.log(args); // [1, 2, 3]
}

foo(1, 2, 3);

처음 예제에 적용해서 다시 확인해보자

function marry(...args) {
  console.log(args); // ["jingoo", "hani"]
  
  return `${person1} is married fo ${person2}`;
  // ReferenceError: person1 is not defined
}

console.log(marry('jingoo', 'hani'));

매개변수를 ...args 로 두면, person1과 person2가 정의되어 있지 않으므로 reference error가 뜬다.

function marry(...args) {
  return `${args[0]} is married fo ${args[1]}`;
}

console.log(marry('jingoo', 'hani'));
// jingoo is married fo hani

이때는 args가 배열로 받아올 수 있기때문에 인덱스를 적용해서 코드를 바꿔주면 에러없이 콘솔에 출력된다.

profile
매일 성장하는 프론트엔드 개발자

0개의 댓글