[JS] Variable, Lexical Environment, Scope Chain

colki·2021년 5월 6일
1

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

Variable Environment

keep in mind each execution context has its own variable environment.

각 실행컨텍스트는 variable environment(변수환경)을 가지고 있다.
변수 환경에는 실행컨텍스트 내부에서 선언된 변수 정보 + 외부 환경 정보가 저장된다.

즉, 실행 컨텍스트에서 변수를 선언하면, 그 실행 컨텍스트 한정 자신의 변수환경에 있는 변수에 접근해서 사용할 수 있게 된다.


function two() {
  var isValid;
  console.log(isValid); // undefined
}

function one() {
  var isValid = true; // local/variable environment
  console.log(isValid); // true

  two(); // new execution
}

var isValid = false;
one();

/* Call Stack */
// two() // isValid = undefined
// one() // isValid = true
// global // isValid = false

Lexical Environment

lexical means at compile time.

first lexical environment is the global lexical environment


자바스크립트는 코드를 실행할 때, 해당 컨텍스트에서 변수를 찾을 수 없을 경우

렉시컬환경으로 이동해서 변수를 찾는다.

Veriabla Environment(실행컨텍스트 생성 당시 snapshot)

Lexical Environment (변경사항이 실시간 반영된다)

Every execution context has a reference to its outer environment, and that outer environment is called Lexical Environment

.

어디에서 선언 했는 지에 따라 렉시컬 환경이 정해진다



Context vs Scope

Scope
Scope means what is the variable access of a function when it is invoked.

Scope has to do with the the visibility of variables

함수가 실행될 때 변수가 접근할 수 있는 유효 범위를 말한다. 또한 스코프는 visibility of variables와 관련있다.


context

  • Context tells you where we are within the object.
  • What is to the left of the dot.
  • What is the object environment that we're in right now?

컨텍스트는 현재 속해 있는 객체를 참조한다.
this가 가리키는 객체가 바로 컨텍스트이다.(. 왼쪽)

Context is related to objects. When you use the JavaScript “this” keyword, it refers to the object to which function belongs.

This, in-turn, changes the meaning of “this” inside of that function when it is executed.

그래서 call(), apply(), bind() 의 사용은 함수가 실행될 때 컨텍스트/this를 변경하는 일이다.

Reference_kevin chisholm - blog



scope = "static" scope
식별자의 유효범위.
오직 함수에 의해서만 스코프가 생성된다.

where the function is on the execution stack what matters is
where the function is written 함수가 어디에서 선언되었는 지가 스코프를 결정한다.

Scope Chain

무조건 스코프체인상에서 먼저 발견한 스코프의 식별자에만 접근 가능하다.
자신 또는 가까운 스코프에서 식별자를 찾으면 스코프체인 검색을 더이상 하지 않는다. 동일한 변수를 더 이상 찾지 않는 이 현상을 변수의 은닉화 라고 한다.

자신의 스코프에서 먼저 발견 했다면, 값을 할당한다.
또는 식별자는 있는데 값을 찾지 못할 경우에는 undefined.
자신의 스코프에 없다면 상위 스코프를 찾아간다.

🤔 스코프 曰: 내코드에 있는 변수 어디 컨텍스트 가야 찾을 수 이찌?

scope chain starts where the variable is defined and goes all the way down to the global context to see if the variable exists

자신의 실행컨텍스트의 변수환경에서 값을 찾지 못하는 경우 스코프체인을 타고 올라가 부모의 실행컨텍스트의 변수환경에서 값을 찾는다.

종점은 window객체 =글로벌 스코프에서도 못찾으면 Reference Error가 발생한다.

?그렇다면 스코프 체인은 언제 생기는 걸까 ?

attaches all these scope chains before it even runs

컴파일러가 코드를 확인하고, 코드를 실행하기 전에 스코프체인을 연결한다.
즉 실행컨텍스트끼리 연결되는 것이다.

쌓여있는 콜스택의 아래 방향으로 뒤지면서 찾는 셈이다.

그래서 어떤 데이터에 접근해야 데이터를 얻을 수 있는 지 미리 알 수 있다.

커링함수를 떠올리면 이해하기 쉽다 :)

커링currying-구현하기

💡 undefined

값이 없다 (x)
값이 아직 할당 되지 않았다, 할당 전이다(o)
체인에는 변수가 존재하나, 아직 변수에 값이 할당되지 않았다.(o)

또는 할당 전에 확인하는 경우에도 나타날 수 있다.


[[scope]] property

Lexical Environment === [[scop]]


콘솔창에서 foo함수를 만들고 window를 뒤지면 foo를 찾을 수 있다.
foo는 [[scope]]라는 속성을 가지고 있는데, 이 속성으로 렉시컬스코프가 window임을 알 수 있다.

no keyword variable

  • leakage of global the tables
  • implicit global
  • 암묵적 전역

colki 앞에 var const let 이 없이 선언했음에도 불구하고 foo는 'hany'를 리턴한다.

function foo() {
  colki = 'hany';
  return colki;
}

foo(); 
console.log(foo()); // 'hany'

컴파일러: colki 얘 아는 애야?

😑 foo曰: colki isn't in my nothing's been declared.
I haven't seen a var or a concert or a left keyword.
키워드 없는 얘는 처음보는데? 선언한적 없음. 딴데가서 알아봐

컴파일러: ............?

foo 스코프의 변수환경에서는 colki를 찾을 수 없다.

즉, keyword가 붙지 않는 변수는 변수환경에 포함되지 않는다.

그렇다면 변수인지 아닌지 컴파일러는 알 도리가 없다. 개답답....!

그래서 컴파일러는 colki의 정체를 알아내기 위해 스코프체인을 뒤지다가, 결국 widow까지 찾아가서 물어본다.

🌎 window曰: 휴 쟤가 변수는 아닌데.. 비스무리하니까 ..그냥 내 프로퍼티에 넣어줄게. 그럼 생긴건 전역변수니까 알아서 해석하렴.

window는 억울하지만 컴파일러 눈치가 보이니 자기 패밀리로 인정해준다.

결국 이 변수아닌 변수는 window의 변수환경에 담겨 window의 프로퍼티가 되어 마치 전역변수인 양 행동한다.

하지만 키워드가 없으므로 진짜 변수는 아니다. 변수인 척 하는 window의 프로퍼티이다. 따라서 호이스팅이 발생하지 않는다.

그리고 프로퍼티이기 때문에 delete 연산자로 삭제할 수 있다.

var x = 10; // 전역 변수

function foo () {
  y = 20; // 선언하지 않은 변수
  console.log(x + y);
}

foo(); // 30

console.log(window.x); // 10
console.log(window.y); // 20

delete x; // 전역 변수는 삭제되지 않는다.
delete y; // y는 전역변수가 아니라 전역프로퍼티이니 삭제 가능!

console.log(window.x); // 10
console.log(window.y); // undefined

💡 prevent no keyword variable problem

  • var, let, const 키워드 붙여주면 된다.
  • 파일 맨 앞에 'use strict' 를 써주면, 해당 코드에서 Reference Error : 000 is not defined 가 뜨기 때문에 코드를 수정할 수 있다.

enclosed

anonymous function it's going to have a name.

It's not really doing anything is it.

왼쪽이 변수요, 오른쪽이 함수인 함수표현식에는
(익명) 함수표현식과 또 기명 함수표현식이 있다.

// 우리가 보통 말하는 (익명) 함수표현식
var a = function () {};

// "기명 함수표현식"
// 과거에는 함수 이름이 있어서 디버깅에 유리했으나 이제는 익명함수에도
// 변수의 이름이 부여되기 때문에 딱히 무쓸모..
var b = function bbb() {};

기명함수표현식으로 작성한 경우 외부에서는 기명함수명으로 함수를 호출할 수 없다.


var heyhey = function doodle() {
  console.log(heyhey); // f doodle(){}
  return 'heyhey';
}

heyhey(); / 'heyhey'
doodle();
// ReferenceError: doodle is not defined
// 스코프 체인에서 못 찾겠음!

This is because the doodle function is actually enclosed in its own scope

doodle함수 자체가 값이 되어 heyhey변수에 담겨있는 모습이다.

자신의 변수환경에 doodle이 아닌 heyhey에 봉인된채로 담겨 있기 때문에 외부에서는 doodle에 접근할 수 없다.

반면에 heyhey 는 글로벌에 있기 때문에 외부에서 heyhey에 접근해서 리턴값을 확인할 수도 있고,

또한 doodle내에서도 heyhey를 콘솔에 찍으면 heyhey를 확인할 수 있다.

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

0개의 댓글