Section 3. context, hoisting, scope, this, IIFE

Udemy - JavaScript: The Advanced Concepts

Section 3. Foundation 2

실행 컨택스트(Execution Context)

call stack 쌓기

  1. JS엔진은 처음 실행시 전역실행컨텍스트를 생성하여 call stack에 쌓는다.
  2. 함수가 호출될때마다 새로운 실행컨텍스트를 생성하여 call stack에 쌓는다.
    (변수: {variable,arguments}, this, scope chain)
  3. 함수가 끝나면 call stack에서 해당함수의 실행컨텍스트를 제거한다

실행 컨텍스트

  1. 생성단계 - Hoisting
    1) Lexical Environment (변수 let, const - uninitialized)
    2) Variable Environment (함수선언, 변수 var - undefined)
    3) this
  2. 실행단계

Lexical Environment & Variable Environment

  • VE는 LE를 상속하기 때문에 둘다 LE라고 말할수 있다
  • hoisting, scope의 차이 때문에 구분지어 사용

Lexical Environment

  • Environment Record: 내부에서 선언된 데이터 + arguments객체
  • Outer Environment Reference (Scope Chain) : 현재 렉시컬 환경에서 찾을수 없는 변수들을 상위 렉시컬 환경을 타고올라가면서 탐색

lexical scope : 함수 선언 위치 기준
<-> Dynamic Scope : 함수 호출 위치 기준

var x = 'global';

function a() {
  console.log(x);
}

function b() {
  var x = 'b';
  a();
}

b();	// console.log(x) -> global

// a
// b       x='b'
// global  x='global'

//콜스택에 함수의 실행컨텍스트는 위와 같이 쌓였지만
//호출 위치에 상관없이, 선언위치 기준으로 scope chain에서 x를 찾음

Hoisting

변수는 선언부, 함수는 전체가 끌어올려짐 (const, let 은 좀 다름)
실제로 코드를 옮기는 것은 아니고, 시작전에 메모리에 할당하는것을 뜻함

// 변수는 첫번째 a가 선언되고, 두번째 a는 무시. 그리고 코드진행하면서 차례대로 값 대입
console.log(a)  //undefined
var a = 1;
console.log(a); //1
var a = 2;
console.log(a); //2

// 함수는 첫번째 b가 선언+할당 되고, 두번째 b가 덮어쓰기. 그리고 코드진행.
b();    //4
function b (){ console.log("3") }
b();    //4
function b() {console.log("4")}
b();    //4

var, const, let 선언 없이 그냥 쓰는경우
함수 안에서 사용해도 전역변수에 생성됨

arguments 사용 주의 : 아래와 같이 쓰는걸 권장

1) Array.from(arguments)
2) arguments.length
3) arguments[i]
4) function foo (...args) {... args를 대신사용...}

Function Scope(var) vs Block Scope(const/let)

초기 JS는 Function Scope 사용 (var)
ES6에서 Block Scope사용하도록 기능 추가 (const, let)

if (5 > 4) {
  var a = 'a';
  const b = 'b';
  let c = 'c';
}

console.log(a);	//a
console.log(b);	//error
console.log(c);	//error

for문에서의 var/let

for (var i = 0; i < 5; i++) {
  console.log(i)
}
console.log(i);	
// 0, 1, 2, 3, 4 출력
// 5 출력


for (let i = 0; i < array.length; i++) {
  console.log(i) 
}
console.log(i);	
// 0, 1, 2, 3, 4 출력
// error!

함수 용어

function india() {...}	 //함수 선언식(Declaration)
var canada = () => {...} //함수 표현식(Expression)
canada(); 		 //함수 Invocation/Call/Execution

IIFE

Immediately Invoked Function Expression
전역변수를 줄이기 위한 방법중 하나 (ES6 이후에는 모듈 사용)

(function(){})(); //함수 표현식은 바로 실행 가능
function(){](); //함수 선언식은 바로 실행 불가

 var script1 = (function(){
    function a = {
    	return 5;
    }
    return { a };
 })();
 
 script1.a(); //전역환경에 script1하나만을 더해 새로운 환경을 만들었다

이게 잘 활용된 예는 jQuery
jQuery는 수많은 기능이 있지만, window.$, window.jQuery 하위에 모두 담고있음
그래서 $('h1')처럼 $로 접근

for (var i = 0; i < 5; i++) {
  setTimeout(function(){
    console.log(i)
  }, 1000)
} 
//5,5,5,5,5

for (var i = 0; i < 5; i++) {
  (function(j){
    setTimeout(function(){
      console.log(j)
    }, 1000)
  })(i)
} 
//0,1,2,3,4

이런경우에도 유용하다

this

  • 객체, 함수를 속성으로 가진
    - x.someFunc > x가 this
    - window.aFunc > window
  • dynamic scope 사용 (헷갈릴수있음 중요!!)

Lexical Scope(기본) vs Dynamic scope(this)

var name = 'in global';

function print() {
  console.log(name);		//함수의 위치 기준 -> in global
  console.log(this.name);	//함수의 호출 기준 -> in Obj
}
var obj = { name: 'in Obj', print: print}

obj.print();

헷갈리는 this를 잘 사용하기 위한 방법

  1. 화살표 함수는 lexical scope를 사용함 (ES6부터 사용)
  2. this를 변수에 담아 사용
  3. 함수.bind(this, parameter나열 1, 2, 3...) - 함수 반환
  4. 함수명.call(this, parameter 나열 1, 2, 3,...) - 즉시실행
  5. 함수명.apply(this, [parameter 배열]) - 즉시실행
const obj = {
  name: 'Billy',
  sing: function() {
    console.log(this) 	
    
    var basic = function() { console.log(this); }
    
    var arrow = () => console.log(this);
    
    var self = this;
    var smart = function() { console.log(this); }
    
    var bindedBasic = basic.bind(this);
    
    basic(); // global (함수 선언위치가 아닌 호출위치 기준)
    arrow(); // obj (화살표 함수는 lexical scope 사용)
    smart(); //obj (변수는 lexical scope 사용)
    bindedBasic();	//obj
    basic.call(this); 	//obj	
    basic.apply(this);	//obj
  }
}

obj.sing() // obj

bind의 재밌는 사용법

function multiply(a, b) {
    return a*b;
}

var multipleByTwo = multiply.bind(this, 2);
console.log(multipleByTwo(4));

var multipleByThree = multiply.bind(this, 3);
console.log(multipleByThree(4));

apply의 재밌는 사용법

const array = [1,2,3];

function getMaxNumber(arr){
  return Math.max.apply(null, arr);  
}

getMaxNumber(array)

this의 4가지 해석방법

  1. 암시적 사용 : 호출하는 객체가 this (dynamic scope)
  2. 명시적 사용 : .bind() 로 this 지정
  3. new : 클래스의 인스턴스가 this
  4. 화살표 함수 : 선언 위치의 this (lexical scope)

'use strict'

  • 초기에 JS 가 완벽하진 않았음
  • 그래서 발생할 수 있는 실수를 막아줌
  • ES6에서는 자동으로 포함되어있음

읽기 목록

LE, VE의 공통점과 차이점까지 나누어 상세하게 설명 :
https://dkje.github.io/2020/08/30/ExecutionContext/

클로저 상황의 실행컨텍스트까지 설명 :
https://www.zerocho.com/category/JavaScript/post/5741d96d094da4986bc950a0

좋은 웹페이지 즐겨찾기