본문 바로가기

개념정리/기술면접대비

[기술면접 대비] 2. JS/HTML/CSS

[HTML]

1. HTML 이란?

html은 마크업 언어이다. 여는 태그, 내용, 닫는 태그를 통틀어 html 요소(element)라 이른다.

element가 가지는 속성은 attribute라 한다. attribute에는 class, id, type, disabled 등이 있다.

HTML5의 표준 html 구조는 다음과 같다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>HTML</title>
  </head>
  <body>
    <p>I am a HTML document</p>
  </body>
</html>

 

<!DOCTYPE html>은 문서의 형식을 나타낸다. 마크업의 유효성을 검증하고 브라우저에게 렌더링 모드를 알려줌. 최근에는 크게 유의미하게 사용되지는 않으며, 대신 http 헤더의 Content-Type에 문서 타입을 정의한다.

<html> 전체 페이지의 컨텐츠를 포함하는 가장 기본 요소

<head> 이용자에게 직접적으로 표시되지 않아도 검색 키워드, 홈페이지에 대한 설명, css 스타일 등의 내용을 담는다.

<meta charset=utf-8> html문서의 문자 인코딩을 지정함.

<title> 페이지 제목. 브라우저 탭에 표기됨.

<body> 페이지에 표시될 모든 컨텐츠를 담음

 

xhtml

xhtml은 Extensible html을 의미한다. 부정확한 html 규격을 보완하기 위해 엄격한 문법을 적용한 xml 마크업 언어이다. doctype을 반드시 명시해야하며 <html>,<head><title><body> 태그를 반드시 사용해야 한다. 하지만 브라우저에서는 사실상 xhtml과 html을 동일하게 해석하므로 문법상의 엄격함은 있지만 실제 실행에 차이는 없다.

 

 

17. html head 태그, data-속성

 

18. attribute와 property

프로퍼티는 해당 태그가 같는 표준 속성.

attribute는 사용자가 추가한 비표준 속성(대표적으로 datasets 등)

 


[JS]

JS란? 대표적인 특징.

프로토타입 기반 언어(생성자 함수와 프로토타입을 사용하는 js의 객체).

싱글스레드, 논블락 비동기, 이벤트 루프,

스크립트 언어.

 

JS의 Compile

JS를 인터프리터 언어라 하지만 사실상 컴파일 과정을 포함한다.

(JIT. JS엔진은 인터프리터 방식으로 코드를 실행하지만 컴파일이 필요한 순간에 엔진에 내장된 컴파일러를 실행함)

그러한 연유로 컴파일러는 코드가 실행되기 직전에 어떻게든 컴파일의 세 단계를 거친다.

1. 토크나이징Tokenizing / 렉싱Lexing

코드를 의미있는 조각으로 나눔 (렉싱은 상태유지, 토크나이징은 무상태)

2. 파싱 Parsing

나뉜 코드 조각을(토큰 배열) 프로그램의 문법 구조를 반영하는 트리 형태로 변환하여 추상구문트리(AST) 생성

3. 코드 생성 Code-Generation

AST를 엔진이 실행 가능한 기계어로 변환한다

 

파싱과 코드 생성 과정에서 코드 최적화와 함께 렉시컬 스코프가 정의된다.

var a = 2; 

따라서 위 구문을 단순히 변수 a를 위해 메모리를 할당하고, 할당된 메모리를 a라 명명한 뒤 2를 넣는다. 정도로 이해하고 넘어가면 안됨.

1. 컴파일러가 먼저 동작한다. var a 를 처리하기 위해 접근 가능한 스코프 컬렉션에 a가 있는지 살핀다.

2-1. a가 이미 있다면 컴파일러는 선언문을 무시한다.

2-2. a가 없다면 스코프 컬렉션에 새로운 변수 a를 선언할 것을 요청하는 코드(기계어)를 생성한다. 이는 스코프 매니저가 실행 시점에 변수를 생성하게 한다.

3. 컴파일러는 추후에 엔진이 a = 2 대입문을 처리할 수 있도록 코드를 생성한다.

4. 엔진이 코드를 실행한다. 접근 가능한 스코프 컬렉션에 a가 있는지 확인한다.

5. 엔진은 a를 찾을 때 까지 중첩된 스코프를 따라 올라간다. 

6. a를 찾았다면 2를 대입하고, 끝내 찾지 못 했다면 에러를 일으킨다.

 

컴파일의 오해와 진실

그렇다면 컴파일러가 렉시컬 스코프라는 것을 뚱땅뚱땅 만들어 메모리를 할당하는 것인가? 그건 아님!!!

컴파일 시점에는 실제로는 아무 일도 일어나지 않는다.

추후에 실행 시점에서 참조할 개념적 가이드를 제시하는 것 뿐! 즉 설계도면을 작성해 엔진에게 넘겨주는 것이고, 실제로 스코프와 변수에 메모리를 할당하는 등은 엔진이 코드를 실행하는 시점에서 일어난다. 모두!

 

참고 (You Don't Know JS 2판)

It's important to note that compilation doesn't actually do anything in terms of reserving memory for scopes and variables. None of the program has been executed yet.
Instead, compilation creates a map of all the lexical scopes that lays out what the program will need while it execute

 

 

 

실행 컨텍스트 Execution Context

 

JS의 실행 가능한 코드가 실행되는 환경

ECMAScript 문서는 다음과 같이 정의한다 

An execution context is a specification device(mechanism) that is used to track the runtime evaluation of code by an ECMAScript implementation. 

출처 : https://tc39.es/ecma262/#sec-execution-contexts

 

JS 코드 실행을 위한 순수한 메커니즘적 개념이므로 실제적으로 JS 코드로 접근, 제어 가능한 대상은 아니다. 

주요 항목으로 코드 실행 상태를 기록하는 Code evaluation state와 각종 정보를 담은 데이터 구조(Environment Record)를 가진다. (추가로 실행 주체를 판단하는 Script/Module/Function 등의 정보도 담고있다)

 

각각의 실행 컨텍스트(이하 EC라 칭함)는 콜스택에 차례로 쌓이며 실행이 끝나면 소멸된다.

가장 최상단에 위치한 EC가 곧 현재 실행중인 EC(running Execution Context)가 된다.

 

실행 컨텍스트의 Environment Record

출처 : https://tc39.es/ecma262/#table-additional-state-components-for-ecmascript-code-execution-contexts

* JS코어를 공부하면 가장 많이 언급되는 개념 중 하나가 바로 렉시컬 환경 또는 렉시컬 스코프이다. ECMAScript 2015 (ECMA-262 6th Edition) 까지의 문서에는 Lexical Environment 에 대한 내용을 따로 다루었으나 현재 버전에서는 순환참조적이었던 기존의 Lexical Env에 대한 설명이 Env Record의 내용에 통합/포함 되었다. 따라서 렉시컬 환경에 대한 논의도 Env Record의 하위 범주로써 설명한다.

 

1. Variable Environment : 전역변수, 매개변수, 지역변수, 선언문으로 작성된 함수(표현식 함수는 제외) 등.

조금 더 자세히 들어가자면, 전역 컨텍스트의 변수객체는 최상위에 위치하며 모든 전역 변수, 전역 함수를 포함하는 전역 객체(Global Object)를 참조한다.

함수 컨텍스트는 매개변수의 정보를 담은 arguments 객체와 지역 변수, 내부 함수를 포함하는 활성객체(activation object)를 참조한다. 

2. Lexical Environment : 해당 컨텍스트가 참조 가능변수, 함수 등의 정보를 리스트 형태로 가르키는 구조.

해당 컨텍스트의 활성 객체부터 시작해 중첩된 함수들의 활성 객체를 거쳐 최종적으로 전역 객체까지 상위 컨텍스트의 스코프 레퍼런스를 차례로 저장하고 있다.

 

Environment Record 계층구조

출처 : https://tc39.es/ecma262/#sec-environment-records

Env Record에 대한 세부 내용을 살펴보면 크게 선언적 환경, 객체 환경, 전역 환경 세 항목이 주를 이룬다.

함수 또는 모듈은 선언적 Env Record로 분류되며 그 중에서 모듈환경의 상위 환경은(OuterEnv) 전역환경(global Env)를 가르킨다. 

 

[확실하지 않은 레퍼런스]

실행 가능한 코드란 전역으로 생성되는 코드, 함수 내에 존재하는 코드로 나뉜다. (eval 함수로 실행되는 코드는 생략...)

따라서 실행 컨텍스트도 크게 전역으로 생성되는 global EC 함수당 할당되는 function EC가 구분된다. 

 

JS 엔진은 함수 실행 중에 변수를 만나면 우선 현재 scope내에서(활성객체) 변수를 찾아보다가, 탐색에 실패할 경우 스코프 체인을 타고 올라가며 부모 함수와 전역 객체의 스코프를 탐색한다.

* 스코프 체인은 변수에 대한 메커니즘 이라면 객체의 프로퍼티에 대한 메커니즘은 프로토타입 체인이다.

3. this 전역 컨텍스트의 경우 this에 window 객체를 바인딩한다. 함수 컨텍스트는 함수의 호출 패턴에 따라 결정된다.

 

실행컨텍스트의 흐름 https://poiemaweb.com/js-execution-context

1. creation 단계:  global의 경우 전역 객체를 생성(Window)하고, function의 경우는 arguments 객체를 생성한다. 

this는 전역 객체를 가르키는데 함수 컨텍스트는 런타임에 함수 호출 패턴에 의해 this에 할당되는 값이 결정된다,

메모리 공간을 준비하며 함수 선언문은 메모리를 할당하고, 변수는 호이스팅이 일어난다.

*스코프는 creation 단계에서 현재 실행중인 컨텍스트의 스코프를 가진다는 점을 주의!

2. execution 단계 : 코드를 순차적으로 실행하며 변수에 실제 값을 할당한다.

 

1. foo()의 실행 컨텍스트가 생성되는 시점의 컨텍스트는 bar() 이다.

따라서 foo() > bar() > global의 스코프 체이닝을 가진다

var value = "value1";

function bar(){
    function foo(){
	    console.log(value);
    }
    var value = "value2";
    console.log(value); // value2
    foo(); 
}

bar();

 

1. foo()의 실행 컨텍스트가 생성되는 시점의 컨텍스트는 글로벌 컨텍스트 이다.

따라서 foo()의 실행은 bar() 내에서 일어나도 foo() >global의 스코프 체이닝을 가진다

var value = "value1";

function foo(){
	console.log(value);
}
function bar(){
    var value = "value2";
    console.log(value); // value2
    foo(); // value1
}

bar();

 

 

스코핑과 즉시실행 함수

1. 스코프는 현재 실행중인 컨텍스트의 유효 범위를 가르킨다.

2. 스코프는 실행이 아닌 선언문의 위치에 따라 결정된다.

 

실제 함수의 실행은 컴파일 과정이 끝난 뒤, 즉 이미 렉시컬 스코프가 생성된 이후에 일어난다.

따라서 함수는 언제 실행이 되었는지가 아닌, 어디서 실행되었는지에 따라 스코프가 지어짐!

스코프 변경을 런타임에 하는 것 (eval 등)은 결국 렉싱과정의 최적화를 무용하게 하므로 지양할 것.

이를 염두에 두고 애초에 코드를 작성할 때 처음부터 함수의 선언 위치를 신중하게 결정하자.

 

기존의 JS는 블록 단위가 아닌 함수 단위의 느슨한 스코핑을 가진다. var로 선언된 변수는 기본적으로 전역변수로 선언되며, 중괄호 블록 안에서 선언해도 블록 범위를 벗어난다.

무분별한 전역변수 등록을 방지하고, 외부에서 내부 변수로의 접근을 방지하기 위해서는 강제로 함수 단위의 스코핑을 지어 줄 필요가 있었는데 이에 등장한 기법이 바로 즉시실행함수(IIFE,Immediately Invoked Function Expression)로 감싸는 것이다.

스코핑을 위해 존재하기 때문에 함수를 정의와 동시에 즉시 실행한다. 표기법은

(function{ 
	//expression
})(); 

메모리를 점유하지 않고 즉시 해제하는데 실제로 해제되는 시점은 js의 가비지 컬렉션이 동작해야 함. 

이 때 주의점! 

사실 전역 공간을 침범하지 않아 눈에 보이지 않다 뿐이지, JS는 할당 없이 정의부만 있는 것들을 실행 컨텍스트의 temp 영역에 등록한다. 즉 메모리를 점유하기는 함! 그러므로 이것도 무분별하게 남발하면 안 됨.

 

함수지향 언어 JS 관련 이슈

JS의 함수는 일급 객체(first-class object) 이다. 또한 Object의 하위 타입으로 호출 가능한 객체로 취급한다.

함수를 일반적인 객체처럼 변수에 대입하거나 함수에 인자로 넘길 수 있고, 함수를 동적으로 생성 가능하다. 

 

함수는 변수를 가질 수 있고, 접근할 수 있다.

그렇다면 함수가가 생성된 이후에 함수가 참조하는 상위 스코프의 변수가 변경되면 함수는 어떤 시점의 값을 가져올 것인가?  함수를 매개변수로 넘기고 실행했을 때, 해당 함수의의 스코프 체인은 어떻게 정의되는가?

 

>> 함수는 어디에서 어떻게 실행되었는지와 무관하게 함수가 선언된 위치에 따라 정의되는 고유한 스코프를 가진다. 이를 렉시컬 스코프라 함!

 

 

렉시컬 환경 

JS에서 실행중인 함수, 코드블럭, 스크립트 전체가 가지는 이론상의 환경객체(실제로 존재x)

지역변수와 this를 가지는 환경레코드, 외부 환경에 대한 참조(전역 렉시컬 환경은 외부 참조를 가지지 않으므로 외부참조 값이 null이다)

변수는 기본적으로 환경레코드의 프로퍼티이다. 호이스팅이 일어나지만 실제로 값이 할당되기 전에는 undefined로 초기화 되거나 TDZ가 생성되어 접근을 제한함.

함수는 선언과 동시에 바로 초기화됨.(함수 선언문 한정. 표현식은 제외) 

함수를 호출하면 호출중인 함수를 위한 새로운 내부 렉시컬 환경이 만들어지며(매개변수 및 지역변수를 가짐), 내부 렉시컬 환경이 참조하는 외부 렉시컬 환경을 함께 가지게 된다.

모든 함수는 함수가 생성된 시점의 렉시컬 환경을 기억한다. 이를 통해 외부 렉시컬 환경의 변수에 접근한다.

함수가 실행 될 때 변수값 참조는 내외부 렉시컬 환경에서 최신값을 참고한다.

 

클로져 closure

- 함수와 함수가 선언된 렉시컬 환경의 조합.

- 함수가 자신이 선언된 시점의 렉시컬 스코프를 기억하여 스코프 밖에서 실행되어도 기존의 스코프에 접근 가능한 특성.

- 중첩된 함수는 내부 함수가 외부 범위에서 선언된 변수에도 접근이 가능한 JS의 렉시컬 스코프 특성을 이용해 이미 생명주기가 끝난 외부함수의 변수를 참조하는 내부함수.

 

키포인트 두가지 ( 참조 : You Don't Know JS 2판

1. 클로저는 함수에서 사용된다. (일반 Object는 클로저를 갖지 않는다.)

2. 클로저를 관찰하기 위해서는 함수를 원래 정의된 곳과 다른 스코프에서 실행해야 한다.

 

클로저 함수 A와 외부함수 B에 대해 B의 실행 컨텍스트가 사라져도 A가 여전히 B의 변수 객체를 스코프 체인으로 가지고 있기 때문에 참조 및 변경이 가능하다. (해당 외부함수의 환경객체를 참조하는 내부함수가 하나라도 존재하는 한 가비지 컬렉션이 메모리에서 삭제하지 않음)

 

클로저 어디에 쓰는가?

 

1. 콜백함수

함수를 인자로 넘길 수 있는 JS의 특성상 다양한 곳에서 콜백함수가 사용됨. 이 때 콜백함수가 자신이 선언된 곳의 외부환경 변수를 참조하는 모든 경우가 곧 클로저가 사용된 예라 할 수 있다.

ex) setTimeout 등의 타이머, 이벤트 핸들러, AJAX 요청 콜백, 웹 워커 등

 

더보기
// 1, 하나의 콜백함수
function makeSizer(size) {
  return function() { //클로저 
    document.body.style.fontSize = size + 'px';
  };
}

// 2. 다양한 size값을 파라미터로 전달해 각각 클로저 함수를 리턴받아 변수에 저장
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

3. DOM 이벤트에 바인딩
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

 

2. 함수를 이용한 모듈패턴 생성

모듈? 내장 프라이빗 데이터, 메서드는 은닉화, 캡슐화 & 퍼블릭 메서드(들)을 공개 API로 제공   

모듈 함수 내에서 프라이빗 변수와 함수는 숨기고, 퍼블릭한 클로저를 리턴해 사용하게 함.

1) 최외곽 함수를 최소 한 번 호출해 모듈 인스턴스 생성.

2) 최외곽 함수는 비공개 스코프에 대한 클로저를 가진 내부 함수를 하나 이상 반환(여러 개일 경우 객체형으로 반환)

* 단 하나의 인스턴스만 생성하는 싱글톤 패턴을 적용하려면 최외곽 함수를 IIFE로 감싸서 선언과 동시에 단 한번만 실행하여 인스턴스를 생성하게 할 수 있다.

더보기
// makeCounter 라는 함수 생성
var makeCounter = function() {
  // private하게 내부에서만 관리되는 변수와 함수
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  
  //외부에서 사용 가능한 퍼블릭 함수를 클로저로 구현해 리턴한다.
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

// makeCounter()를 이용해 독립된 coutner1,2 객체를 생성
var counter1 = makeCounter();
var counter2 = makeCounter();

// 외부에서는 퍼블릭 함수만을 사용함
alert(counter1.value()); /* 0 */
counter1.increment();
counter1.increment();
alert(counter1.value()); /* 2 */
counter1.decrement();
alert(counter1.value()); /* 1 */

// counter1과 counter2는 독립적으로 존재하며 다른 클로저의 값에 영향을 주지 않는다.
alert(counter2.value()); /* 0 */

 

3. 상태관리에 사용되는 전역변수를 대체

function을 통해 특정 상태를 변경하려 할 때 기본적으로 변경된 상태를 기억하는 전역변수를 사용해야 함.

그러나 전역변수는 데이터의 무결성을 보장하기 어렵다. 즉 의도치 않은 side effect로 값이 오염될 가능성이 있음.

이 때, 이벤트 function 내부에 지역변수와 closure함수를 사용한다면 전역변수를 두지 않고 상태를 유지할 수 있다.

더보기
<!DOCTYPE html>
<html>
  <body>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    var increase = (function () {
      // 카운트 상태를 유지하기 위한 익명함수 내의 지역변수
      var counter = 0;
      // 클로저를 반환
      return function () {
        return ++counter;
      };
    }());

    incleaseBtn.onclick = function () {
    // count 상태를 기억하기 위한 전역변수를 따로 사용할 필요가 없다
    // increase()의 리턴으로 받은 클로저 함수가 내부적으로 기억하는 지역변수가 사용됨
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

 

클로저 유의사항

클로저의 기능이 필요하지 않은 경우는 함수 내에서 함수를 불필요하게 생성하지 말자. 

메모리에 매번 새로운 메서드가 올라가기 때문!

따라서 보통 객체들이 범용적으로 사용하는 메서드는 프로토타입에 연결해서 싱글톤 패턴을 유지해야 한다.

 

JS의 객체지향

객체지향에서 클래스는 복사를 의미한다. 클래스를 이용한 객체 생성(인스턴스화)과 상속 모두 사본을 만드는 것.

하지만 JS는 사본을 생성하는 클래스라는 개념 자체가 없다. 다만 객체가 있을 뿐.

JS의 객체는 클래스를 통해 인스턴스화 되지 않는다. 그냥 바로 자신의 프로퍼티와 메서드를 정의해 객체를 생성함.

따라서 기본적으로 JS의 객체는 싱글톤이다. 

JS는 클래스적인 내용을 객체와 함수의 특성을 이용해 흉내내지만 근본적인 차이가 있다. 바로 복사가 아닌 위임에 기반해 동작한다는 것.

이를 가능케 하는 JS의 핵심 기능이 프로토타입.

 

프로토타입이란?

JS의 모든 객체 하위 타입은 [[prototype]]이라는 내부 프로퍼티를 가진다. 

프로토타입다른 객체를 참조하는 레퍼런스로 사용되며 linked list형태로 최상위 프로토타입인 Object.prototype 까지 상위 프로토타입에 연쇄적으로 연결되어있다.

 

함수와 생성자 호출로 만들어진 객체는 이러한 프로토타입을 이용해 메서드를 위임하는 방식으로 객체를 만든다.

 

생성자 함수와 new

JS에서 재사용 가능한 유사 객체를 만들기 위해 사용.

사실 생성자 함수 라는 특별한 함수는 존재하지 않는다. 어떤 함수든 new 키워드를 앞에 붙여 호출 할 수 있다.

new 키워드를 곧 함수를 생성하는 생성자 호출 이라 부르는 게 더 적합. 

new 붙여 생성자 호출할 함수명의 첫글자는 대문자로 적는게 관습적이다.

new로 함수를 호출하면 일어나는 일

1) 새 객체가 생성된다

2) 생성된 객체의 [[Prototype]]이 연결된다.

3) 생성된 객체가 해당 함수의 this에 바인딩된다. (이 또한 결국 함수의 호출 시점에 this가 바인딩 된 것)

4) 함수 내용이 실행되며 this에(새로 생성된 그 객체) 프로퍼티를 추가한다.

5) 명시적으로 return문을 사용해 다른 객체를 반환하지 않는 한, 자동으로 this(생성된 객체)를 반환한다.

 

이러한 이유로 생성자 호출당한 함수 내의 this는 글로벌 객체가 아닌, 해당 함수의 스코프를 가르킨다. 

익명 생성자 함수를 이용해 단 한번만 호출하는 함수로 객체를 생성하면 코드 캡슐화 가능

* new.target 생성자 함수가 new를 통해 호출되었는지 확인 가능. 이를 응용해 new 사용을 강제할 수 있다

더보기

 

function User(name) {
  if (!new.target) { // new 없이 호출했다면
    return new User(name); // new를 붙여줍니다.
  }

  this.name = name;
}

let john = User("John"); // new를 사용하지 않고 호출했지만 내부적으로 new를 붙여주게 됨
alert(john.name); // John

 

출처 : https://ko.javascript.info/constructor-new#ref-965

 

 

This란 대체 무엇

this는 런타임(실제 함수 호출 시점)에 바인딩되며, 함수의 호출 방식에 따라 정해진다.

this는 렉시컬 스코프와 무관하다!!!! 혼동하지 말자. (arrow func제외)

this는 함수가 선언된 위치와 전혀 상관없으며 언제 어떻게 호출이 되었는지가 중요하다

 

함수 호출 방식을 크게 네 종류로 나누면

1) 일반적인 함수 실행

글로벌 컨텍스트나 일반적인 방식으로 실행된 함수 내에서 this는 전역객체

* 예외! strict 모드에서는 undefined(전역 객체를 기본 바인딩 대상에서 제외함)

 

2) 객체의 메서드 프로퍼티로 실행

암시적 바인딩

객체의 메서드 프로퍼티로 실행된 함수는 어디서 선언되었는지와 상관 없이 호출 시점에 그 객체의 레퍼런스를 참조함. 

따라서 해당 객체가 함수의 this에 바인딩 됨!! 

// 전역으로 선언되었어도
function foo(){
    console.log(this.a)
}

let obj = {
    a : 2,
    foo : foo
}

// 호출되는 시점에서 obj가 foo 함수의 레퍼런스 컨텍스트가 됨 
obj.foo() // 2 

 

주의할 사항

더보기

객체의 메서드 프로퍼티도 해당 객체의 컨텍스트를 레퍼런스로 가지지 않고 실행된다면 암시적 바인딩이 소멸될 수 있다

 

function foo(){
    console.log(this.a)
}

let obj = {
    a : 2,
    foo : foo //객체의 메서드 프로퍼티
}

// 전역 참조를 보여주기 위해 불가피하게 var 키워드로 변수 선언
var a = "전역 a입니당" 

// 1. 
let bar = obj.foo; // bar라는 전역 컨텍스트가 foo의 새로운 레퍼런스가 됨
//실제 foo는 여기서 호출되어 this가 전역으로 바인딩 되었다.
bar(); // 전역 a입니당 

// 2. 
function doFoo(fn){
    //실제 foo는 여기서 호출된다. 일반 함수 내에서 this는 전역에 바인딩됨을 기억하자.
    fn(); 
}

doFoo(obj.foo); // 전역 a입니당

 

3) 간접 실행 (call, apply, bind)

첫번째 파라미터로 넘긴 객체를 명시적으로 바인딩 함.

- foo.call(obj, p1, p2, p3, ... )

 나머지 인자 하나하나 넘겨줌. 함수를 간접 실행함

- foo.apply(obj, [p1, p2, p3, ...] )

 나머지 인자 배열로 넘겨줌. 함수를 간접 실행함

- let newFoo = foo.bind(obj, p1, p2, p3, ... )

 나머지 인자 하나하나 넘겨줌. this 바인딩만 하고 함수를 실행하지 않음. 대신 새로운 함수를 반환함. 

 

4) 생성자(new)를 통한 실행

new 키워드를 사용해 호출된 생성자 함수는 해당 객체가 this에 바인딩되어 반환된다.

* 하지만 생성자 함수 내부의 다른 일반 함수의 this는 전역에 바인딩된다

 

바인딩 우선순위 4(new) > 3(명시적) > 2(암시적) > 1(기본값)

1. let bar = new foo(); // this는 foo
2. let bar = foo.bind(obj2) // this는 obj2
3. let bar = obj1.foo() // this는 obj1
3. let bar = foo() // this는 전역 (strict에서는 undefined)

 

 

! ES6 ! arrow 함수의 this

화살표 함수는 표준 앞서 열거한 표준 바인딩 규칙을 모두 무시하고 렉시컬 스코프로 this를 바인딩한다.

즉, 함수의 호출 방식에 따라 this의 스코프가 바뀌지 않고 상위 환경의 this를 그대로 사용하며 다른 방식으로 오버라이드 할 수 없다.

보통 이벤트 리스너나 타이머의 콜백으로 사용해 상위 컨텍스트의 this를 상속하게 한다. 

function foo(){
    // 상위 컨텍스트인 foo()의 this를 그대로 상속한다.
    // 즉 해당 화살표 함수가 아닌, foo()가 어떻게 호출되었는지가 중요함
    setTimeout(()=>{ 
        console.log(this.a);
    },10)
}

let obj = {a:"obj의 a"};
foo.call(obj) // "obj의 a" 

* 객체의 메서드 프로퍼티를 화살표 함수로 작성하게 되면 해당 객체 대신 전역을 가르킨다.

 

 

나머지 매개변수

js의 함수는 정의와 상관없이 함수에 넘겨주는 인수의 개수에 제약이 없었다.

예전에는 function 내부에서 arguments라는 유사 배열 객체를 이용해 함수의 매개변수를 다양하게 할당했다.

최신 문법에서는 함수를 정의할 때 마지막 매개변수spread연산자를 이용해 여분의 매개변수를 모두 배열로 담을 수 있다. 

function sum(...values){
	// 넘겨받은 모든 매개변수가 values 배열에 담긴다
	return values.reduce((acc,cur)=>{
    	acc+=cur;
        return acc;
    })
}

sum(2,1,3); // 6
sum(3,6,7,8,3,2,1); // 30


function sum2(first, second, ...values){
	let answer = first+second;
	// 첫번째와 두번째 매개변수를 제외한 나머지 매개변수가 values 배열에 담긴다
	let left = values.reduce((acc,cur)=>{
    	acc+=cur;
        return acc;
    })
	console.log(`sum of first and second arguments : ${answer}`);
	console.log(`sum of left arguments : ${left}`);
}

sum2(2,1,3); // 3, 3
sum2(3,6,7,8,3,2,1); // 9, 21

 

 

동기/비동기

요청과 처리가 동시에 일어나는 동기(Sync), 동시에 일어나지 않는 비동기(Asyc).

동기 통신은 요청에 대한 응답을 받을 때까지 대기한다. 비동기는 그 동안 다른 작업을 수행할 수 있다.

이 때, 블락과 동기는 동일한 개념이 아니다. 

sync + blocking 상태 : 처리 다 될 때까지 붙잡혀서 딴 생각도 못 하고 기다림.

sync + non-blocking 상태 : 붙잡히지는 않았는데 자꾸 신경쓰여서 다른 일을 못 함.

async + blocking 상태 : 붙잡혔는데 딴생각 하면서 기다림.

async + non-blocking 상태: 붙잡히지도 않아서 신경도 안 쓰고 다른 일 하러 감.

 

JS 싱글스레드와 동시성모델 이벤트 루프

developer-mac.tistory.com/43

자바스크립트는 싱글 스레드이다. 한 번에 하나의 코드만 실행 할 수 있기 때문에 병렬성을 지원하지 않지만, (브라우저 또는 node.js 등의 js가 실행되는 환경은) 이벤트 루프에 기반한 논블락 비동기 동시성 모델을 지원한다. 실제로 한 번에 여러 처리가 가능한 건 아니지만 그렇게 보이도록 동작하는 것.

자바스크립트 런타임 모델은 크게 하나의 콜스택, 힙, 큐 영역을 가진다. 콜스택에 처리할 함수가 순차적으로 쌓이고(호출 시 push, 리턴 시 pop), 은 동적으로 할당된 전역 객체와 같은 비구조화된 데이터를 저장하며 는 runtime 환경에서 처리해야 할 메시지를 기록한다.

기본적으로 콜스택에 쌓인 함수를 순차적으로 처리하는 동안 js는 다른 동작을 할 수 없다. 그러나 AJAX 통신, SetTimeout 등과 같은 WepAPI 를 사용하는 경우 콜스택에 쌓이지 않고 브라우저 WepAPI에서 처리된다. 처리가 완료되면 다시 JS의 큐에 들어와 이벤트 루프가 자신을 콜스택에 넣어 줄 때 까지 대기한다. 이벤트 루프는 콜스택과 큐를 주시하다가 큐에 태스크가 있으면 콜 스택이 빌 때마다 밀어넣는다. 이러한 원리로 싱글스레드 환경에서도 비동기 동시 처리가 가능한 것이다. 

 

2. var, let, const

변수 선언 키워드인데 스코프에 차이가 있음.

var는 함수레벨 스콮, let, const는 블럭 레벨 스콮

var는 중복 선언 가능, let,const는 불가

var,let은 값 재할당 가능. const는 불가.

 

3. arrow function과 일반 함수

한 줄 표현은 블락 및 리턴 생략 가능. (이 때 객체 반환은 괄호 안에서)

화살표 함수는 this가 없다. 화살표 함수 내에서의 this는 외부 렉시컬 환경의 this를 그대로 사용한다.

화살표 함수는 생성자 함수로 사용 할 수 없다.(new 호출 불가) prototype 프로퍼티this가 아예 없기 때문.

 

 

5. 호이스팅과 TDZ

어디서 변수를 선언해도 선언문이 모두 실행 컨텍스트 최상위로 끌어올려지는 것.

var, let, const 로 선언된 모든 변수에 호이스팅이 일어난다.

var는 undefined로 인식되지만 let, const는 TDZ, Temporal Dead Zone이 형성되어 cannot access before initialized 에러를 발생시킨다는 차이가 있음

kline1103.tistory.com/17

 

6. 정규식

정규 표현식은 문자열에 나타는 특정 문자 조합과 대응시키기 위해 사용되는 패턴.

자바스크립트에서 정규 표현식 또한 객체임

 

7. shallow copy

중첩된 객체에 대해 최상위 레벨 프로퍼티만 참조 없이 복사되는 얕은 복사(shallow copy).

 

8. JS의 객체지향과 함수형 프로그래밍

자바스크립트는 기본적으로 class를 지원하지 않는 프로토타입 기반 언어이다. 

함수 자체가 생성자 역할을 한다.

 

9. 프로토타입, 상속

js 객체의 메서드 프로퍼티를 그냥 등록하게 되면 새로운 객체를 생성 할 때 마다 동일한 메서드가 메모리에 미친듯이 올라간다. 이를 방지하기 위해 사용하는 게 프로토타입. 프로토타입에 체이닝을 걸어 객체의 인스턴스는 독립적으로 관리해도 메서드의 원형은 추가적인 생성 없이 사용 가능하게 한다.

js의 모든 객체는 프로토타입이라는 은닉 속성을 가진다. 객체의 프로토타입 또한 프로토타입을 가지고 이러한 체이닝은 모든 객체들의 최상위 객체인 Object에서 끝난다. Object의 프로토타입은 null이다. 

어떤 객체 objA에 대해 고전적인 방식으로는 objA.__proto__ 로 접근 가능했으나 현재는 Object.getPrototypeOf(objA) 을 이용 가능하다.

객체 별 프로토타입 체인을 쫓아가보자

1. 객체
var o = {a: 1};
// o ---> Object.prototype ---> null

2. 배열
var a = ["yo", "whadup", "?"];
// a ---> Array.prototype ---> Object.prototype ---> null

3. 함수
function f(){
  return 2;
}
// f ---> Function.prototype ---> Object.prototype ---> null

4. Object.create() 특정 객체를 프로토타입으로 새로운 객체를 생성
var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

5. null은 Object를 상속받지 않는다! 따라서 프로토타입 체이닝도 없음
//이 때, undefined는 Object.create의 인자로 들어가지 못 함 (null이나 Object만 가능)
var d = Object.create(null);
// d ---> null

 

10. JS의 타입

 - number Type 은 int double float등 다양하지만 js는 오직 하나만. 모든 수를 실수로 처리 (정수만을 위한 타입x)

 - 원시 타입 : boolean, string, number, undefined, null, symbol

 - 객체 타입 : Object

 

빈 객체를 생성하는 방법

1. obj = {}

2. obj = Object.create(null)

2번 방식 Object.create(null)은 객체의 기본 프로토타입을 위임하지 않으므로 보다 더 텅 빈 객체를 생성할 수 있음.

 

11. undefined와 null 그리고 undeclared

undefined는 접근 가능한 스코프 내에 변수가 선언은 되었으나 할당되지 않은 상태

undeclared는 접근 가능한 스코프 내에 변수가 선언조차 되지 않음. ReferenceError:  is not defined 에러를 뱉는다 

(여기서 is not defined는 undefined와 다르다. 헷갈려서 짜증나지만 그러하다)

*개빡치는 헷갈 포인트 하나 더 : 선언되지 않은 변수와 (var로)선언만 된 변수 모두 typeof를 찍으면 undefined다.

*심지어 let, const로 선언된 변수는 typeof를 찍으면 ReferenceError 를 일으킨다. 아예 선언조차 안되면 undefined인데!

더보기
아주 개같애

  

null은 명시적으로 값이 비어있음을 나타내고자 의도적으로 빈 값을 할당한 상태. 또한 null은 객체 변수를 초기화 할 때 사용한다.

 

typeof(undefined)는 undefined, typeof(null)은 object이다. 왤까? js 초기 버전의 타입태그로 인한 오류. null과 Object가 동일한 0의 타입태그를 가져버려서. 수정 요청을 거절되었음. 너무 많은 사이트를 다 파괴할 것이라...

 

12. JS의 배열과 실제 자료구조 배열의 차이

js의 배열은 hash map으로 구현된 가짜 배열이다.

 

13. iterator

double 

 

13. 순환참조

double 

 

 

14. garbage collector

double 

 

15. 이벤트 버블링, 이벤트 캡쳐링, 이벤트 위임 Delegation

이벤트 버블링이란 트리 구조를 이루는 dom의 특성. 하위 요소에서 발생한 이벤트가 상위 요소까지 전파되는 것.

이벤트 캡쳐링은 반대로 상위 요소로부터 이벤트가 발생한 하위 요소를 찾아가는 것.

이벤트 전파를 방지하려면 event 객체의 stopPropagation() 메서드를 호출하면 됨.

이벤트 발생시 흐름은 ( 캡쳐링 > 타깃 확보 > 버블링 ) 순으로 이루어진다

이벤트 위임이란, 하위 모든 요소들에 이벤트를 매핑하지 않고 상위 요소에서 하위 요소에 이벤트를 위임해 제어할 수 있게 하는 것.

 

16. 함수 선언식과 표현식

함수 선언식은 일반적인 함수 선언형식. 표현식은 유연한 js의 특징을 활용한 선언 방식이다. 

선언식 : function funcA (){ }

표현식 : var funcA = function(){} 

선언식 함수는 호이스팅이 일어나지만 표현식은 그렇지 않음.

 

14. Callback, Promise, async/await

비동기 통신을 위한 핵심 기능들. 자바스크립트는 싱글스레드이다. 따라서 내부적으로 병렬 처리가 필요한 작업들을 위해 내부적으로 비동기 논블락 모델을 차용했는데 이 때 사용되는 것. 

callback 함수의 인자로 함수를 받아 특정 시점에 전달받은 함수를 다시 호출하는 것을 의미한다.

Promise는 callback과 하는 일은 같지만 인자로 함수를 전달하지 않고, Promise 객체를 반환하여 .then()을 호출하여 사용한다. 예외처리를 위한 .catch() 메소드가 존재한다.

async/await는 ES7에서 추가된 문법으로 직관적이고 편한 코드를 작성할 수 있다. 비동기 처리가 필요한 함수 앞에 async라는 키워드를 명시한 뒤 내부에서 비동기로 처리할 메서드 앞에 await 를 사용하여 비동기 처리를 진행한다. async가 표기된 함수는 promise 객체를 리턴하기 때문에 promise와 동일한 예외처리가 가능하다.

+내가 사용한 경험?

 


[CSS]

21. CSS와 SVG

 

22. 화면에 보이지 않게 하는 방법들

프로퍼티

 - hidden : 화면x, 빈공간 x, 렌더링 x

 - aira-hidden : 화면 o, 스크린리더 x, 렌더링 ?  

 

css 스타일

 - display : none; 화면 x,  빈공간 x, 렌더링 x

 - visibility : hidden; 화면 x, 빈공간 o, 렌더링 ?

 - opacity : 0; 화면x, 빈공간 o, 렌더링 ?

 - width:0; height:0; ; 화면x, 빈공간 x, 렌더링 ?

 

9. SASS, SCSS

css 확장판 스크립팅 언어

SCSS가 더 많이 쓰임.