Front-End 면접 질문 대비 Part1 (hoisting, closure, this)

이희제·2021년 1월 1일
106

직무 면접 대비

목록 보기
1/6

이번에 면접 대비를 위해 프론트 엔드 분야에서 알아야 될 내용들을 정리하고자 합니다.


Hoisting

➡️ 함수 안에 있는 선언들을 모두 끌어올려서 해당 함수 유효 범위의 최상단에 선언하는 것. 끌어올린다고 생각하면 됩니다.

여기서 유효 범위는 함수 블록 {} 안을 뜻합니다.

호이스팅의 대상

✅ 자바스크립트에서는 ES6에서 도입된 let, const를 포함한 모든 선언을 호이스팅합니다.

변수는 3가지 단계를 거쳐 생성 됩니다.

  • 선언 단계(Declaration Phase): 변수를 변수 객체(Variable Object)에 등록합니다. 이 변수 객체는 스코프가 참조하는 대상이 됩니다.
  • 초기화 단계(Initialization Phase): 변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보합니다. 이 단계에서 변수는 undefined로 초기화됩니다.

  • 할당 단계(Assignment Phase): undefined로 초기화된 변수에 실제 값을 할당합니다.

💡 var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어집니다.

➡️ 초기화 단계에서 undefined로 초기화되기 때문에 변수 선언문 이전에 변수에 접근해도 에러가 발생하지 않고 undefined를 반환하는 것입니다.

💡 반면 let, const 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 이루어집니다.

➡️ 따라서, 스코프에 변수를 등록(선언단계)하지만 초기화 단계는 변수 선언문에 도달했을 때 이루어지므로 초기화 이전에 변수에 접근하면 참조 에러(ReferenceError)가 발생하는 것입니다.

스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 일시적 사각지대(Temporal Dead Zone, TDZ)라고 합니다.

Example)

calculateAge(1999);   // 함수 선언 전에 함수를 호출 -> hoisting 이 되어 잘 작동한다. 

function calculateAge(year){  // 함수 선언. 
    console.log(2020 - year);
}

//retirement(1990);   // 작동하지 않는다

var retirement = function(year) {     // 함수의 선언 방식이 아니기 떄문에 hoisting 되지 않는다. (only work on function declaration)
    console.log(65- (2020- year));
}

//variables (변수)

console.log(age); // 작동은 되지만 undefined로 뜬다. (hoisting) -> js 가 변수가 선언되었는지는 알고 있는 것이다. 

// 변수의 선언만 호이스팅.

var age = 50;  //global 

console.log(age);

function foo(){
    var age = 65; //local
    console.log(age);   //local print
}

foo();  
console.log(age);  // global print

호이스팅 우선순위

➡️ 변수 선언이 함수 선언보다 높은 우선순위를 가집니다.

var myName = "hi";

  function myName() {
      console.log("yuddomack");
  }
  function yourName() {
      console.log("everyone");
  }

  var yourName = "bye";

  console.log(typeof myName);  // string
  console.log(typeof yourName); // string


Closure (클로져) ✨

✅ 두 개의 함수로 만들어진 환경으로 특별한 객체의 한 종류

-> 외부 함수 호출이 종료되더라도 외부 함수의 지역 변수 및 변수 스코프 객체의 체인 관계를 유지할 수 있는 구조를 클로저라고 합니다.

++ 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수

var color = 'red';
function foo() {
    var color = 'blue'; // 2
    function bar() {
        console.log(color); // 1
    }
    return bar;
}
var baz = foo(); // 3
baz(); // 4

-> 여기서 baz가 클로져입니다.

  • outer 함수의 context에 속해있는 변수를 참조합니다.
  • outer 함수의 지역변수로 존재하는 color를 자유변수라고 합니다.

클로져의 활용

  1. 상태 유지
  1. 전역 변수의 사용 억제 => 자신이 생성되었을 때의 lexical 환경을 기억하기 때문이다.

  2. 정보의 은닉

참고 : https://poiemaweb.com/js-closure



this

✅ 자바스크립트에서 모든 함수는 실행될 때마다 함수 내부에 this라는 객체가 추가됩니다.

✅ this는 함수가 호출되는 상황에 따라서 달라집니다.

1. 객체의 메서드를 호출할 때

객체의 프로퍼티가 함수일 경우에는 메서드라고 합니다.

var myObject = {
  name: "foo",
  sayName: function() {
    console.log(this);  // 여기서 this는 myObject를 참조 
  }
};
myObject.sayName();
// console> Object {name: "foo", sayName: sayName()}

➡️ 메서드에서 this 는 메서드를 포함하고 있는 객체를 참조합니다. (해당 메서드를 호출한 객체가 바인딩 되는 것)

2. 함수를 호출할 때

✅ 객체의 메서드가 아니라 함수를 호출하면 this 는 전역 객체에 바인딩됩니다.

var value = 100;
var myObj = {
  value: 1,
  func1: function() {
    console.log(`func1's this.value: ${this.value}`);

    var func2 = function() {
      console.log(`func2's this.value ${this.value}`);
    };
    func2();
  }
};

myObj.func1();
// console> func1's this.value: 1
// console> func2's this.value: 100
  • func1 은 myObj 객체의 메서드이기 때문에 this에는 myObj가 바인딩 된다.

  • func2는 메서드가 아닌 함수이기 때문에 this에는 전역 객체가 바인딩된다.


3. 생성자 함수를 통해 객체를 생성할 때

✅ new 키워드를 통해서 생성자 함수를 호출할 때는 this는 객체 자신이 됩니다.

✅ new를 사용하면 빈 객체가 생성되는 것입니다.

let Person = function (name, year, job) {
    this.name = name;
    this.year = year;
    this.job = job;
    console.log(this);
};

var john = new Person("hi", 1996, "hoho"); // new 를 통해서 새로운 빈 object 가 생성된 것이다. 그리고john 에 할당해주는 것이다. 

var Person = function(name) {
  console.log(this);
  this.name = name;
};

var foo = new Person("foo"); // Person
console.log(foo.name); // foo

4. apply, call, bind를 통한 호출

➡️ bind 는 함수가 가리키는 this 만 바꾸고 호출하지 않음. (this 를 고정시킨다)

➡️ call 은 this를 바인딩하고 함수를 호출하고 실행시킨다. (this 를 설정해줄 수 있다)

➡️ apply 는 call과 거의 똑같지만 인자 전달을 배열로 해준다.

var john = {
    name: "heeje",
    age: 25,
    job: "student",
    presentation: function (style, day) {
        if (style === "formal") {
            console.log(
                "good moring" + day + "i'm " + this.name + this.job + this.age
            );
        } else if (style === "friendly") {
            console.log("hey what's up?" + this.name + this.job + this.age);
        }
    },
};

john.presentation("formal", "morning");

var jane = {
    name: "jane",
    age: 25,
    job: "pro",
};

john.presentation.call(jane, "friendly", "afternoon"); //  call method (jane 을 전달. this를 바인딩 )

// => john.presentation.apply(jane, ["friendly", "afternoon"]);

let johnFriendy = john.presentation.bind(jane); //bind method -> this를 jane 으로 변경 -> 함수를 실행하지 않는다.
johnFriendy("friendly", "moring");

마무리

앞으로의 면접 준비를 위해서 글을 정리해보았습니다.

profile
그냥 하자

2개의 댓글

comment-user-thumbnail
2021년 1월 31일

let/const도 호이스팅이 이루어집니다. 다만 선언과 초기화가 나누어져있어서 TDZ으로 인해 에러가 발생하는거죵
https://wonism.github.io/is-let-hoisted/

1개의 답글