드디어 자바스크립트 파트! 앞부분은 웬만하면 알테니(아마도..?) 두 개씩 읽어도 좋을 듯. 웬만한건 굳이 정리하지말고 넘어가자.. 내 개인 공부용이니까
이전에는 자바스크립트가 표준화되지 못해서 각 브라우저에서만 동작 하는 기능들이 있어 크로스 브라우징 이슈가 심각했다.
이후 ECMA 인터내셔널에 표준화 요청, ECMAScript로 명명됨.
ES6(ECMAScript 2015)가 핵심
Ajax가 등장하면서 서버로부터 필요한 데이터만 비동기적으로 받아 렌더링할 수 있게 되어 구글 매보가 같은 어플리케이션 같은 웹이 등장
제이쿼리가 등장하며 DOM 컨트롤이 쉬워지고, 크로스 브라우징 이슈가 다소 해결 됨
구글 맵을 통해 가능성이 보이며 자바스크립트로 웹 어플리케이션을 구축하려는 시도가 늘어남. 이에 따라 빠른 자바스크립트 엔진이 필요했고, 구글이 v8 엔진을 선보임
Nodejs의 등장으로 브라우저가 아닌 환경에서도 자바스크립트를 사용할 수 있게 됨.
자바스크립트는 프로그래밍 언어로서 뼈대를 이루는 ECMAScript와 브라우저가 지원하는 각종 API(클라이언트 사이드 API 등)를 아우르는 개념.
웹 브라우저에서 동작하는 유일한 언어. (아마 작성 당시에는 그럴지 몰라도 요즘은 아닐걸? 이름은 까먹었는데 다른 시도가 등장하고 있다. 김태곤 THE RED에서 들었음)
자바스크립트는 개발자가 별도로 컴파일하지 않는 인터프리터 언어이다.
자바스크립트는 멀티 패러다임 언어다
자바스크립트는 프로토타입 기반의 객체지향 언어이다.
브라우저와 Nodejs에서 사용되는 자바스크립트는 목적도 다르고, 사용할 수 있는 기능도 다르다. (DOM 선택이나, require 등..)
크롬, 개발자 도구를 적극 활용하자
콘솔보다는 디버깅을 이용할 것.
브라우저의 핵심 기능은 서버에 request(요청)하고 서버의 response(응답)을 받아 브라우저에 표시하는 것이다.
자바스크립트는 렌더링 엔진이 아닌 자바스크립트 엔진이 처리한다.
HTML 파서는 script 태그를 만나면 파싱(DOM 생성 프로세스)를 중지하고 자바스크립트 엔진으로 제어 권한을 넘긴다.
JS 엔진은 JS 코드, 파일을 로드, 파싱, 실행하고 HTML 파서에 다시 제어 권한을 넘기고 중지됐던 위치부터 다시 HTML 파싱을 진행한다.
이처럼 브라우저는 동기적으로 HTML, CSS JavaScript를 처리한다. 즉 script 태그의 위치에 따라 블로킹이 발생, DOM의 생성을 지연시킬 수 있어서 위치가 중요하다.
body의 가장 하단에 script를 두는 것이 일반적이다.
HTML이 모두 처리된 후에 script를 처리하기 때문에 로딩 시간이 단축되며, 자바스크립트가 DOM을 조작할 때 해당 DOM이 없는 에러가 발생하지 않는다.
음.. 너무 옛날 글인가? 변수 선언에 var만 나와있다.. 적당히 읽기만 해보자
원시타입 ( number, string, boolean, null, undefined, symbol(es6에 추가됨)
객체 타입 ( object )
변수는 값의 위치(메모리 상의 주소)를 기억하는 저장소이다.
메모리에 값을 저장하기 위해서는 먼저 메모리 공간을 확보해야하며, 이는 값의 종류(타입)에 따라 크기가 결정된다.
데이터 타입은 확보해야할 메모리 공간, 할당할 수 있는 유효한 값에 대한 정보, 메모리에 저장된 2진수를 어떻게 해석할 것인가에 대한 정보를 컴퓨터, 개발자에게 제공한다.
자바스크립트의 숫자형은 number하나 뿐으로, 모든 수를 실수로 처리한다.
정수로 표시한다해도 사실은 실수라서, 정수끼리 나누어도 실수가 나올 수 있다. (주의)
한 번 생성된 문자열은 read only로서 변경할 수 없다. ex) str[0] = 's'로 변경 x
물론 재할당은 가능하지만, replace등으로 변경하는 것은 '새로운 문자열을 반환'하는 것이라 다르다.
typeof null을 하면 null이 아닌 object가 나오는데, 이는 자바스크립트 설계상 오류이다. 따라서 null임을 확인하려면 === 연산자를 사용해 === null을 해야한다.
var는 함수 레벨 스코프, let, const는 블록 레벨 스코프를 가진다. var는 쓰지 말자
switch case문에서 각 case에 break 문을 사용하지 않아서 switch문이 끝날 때 까지 모든 case, default문을 실행하는 것을 폴 스루(fall through)라고 한다.
여러 case를 묶어서 하나의 동일한 결과를 낼 수 있다
break문은 레이블 문, 반복문, switch 문의 코드 블록을 탈출한다
명시적 타입 변환, 타입 캐스팅 : 개발자에 의한 타입 변경
암묵적 타입 변환, 타입 강제 변환 : 자바스크립트 엔진에 의한 자동 변경변경
자바스크립트 엔진은 표현식을 평가할 때 문맥, 즉 컨텍스트(Context)에 고려하여 암묵적 타입 변환을 실행한다. (문자열+숫자는 문자열로 변환한다던가)
falsy
단축 평가 : 논리 평가를 결정한 피연산자의 평가 결과를 그대로 반환
aa && bb 일 경우, aa가 true이고, bb가 true이면 평가되므로 평가를 결정한 요소는 bb이다.
aa || bb 의 경우 aa가 true면 평가되므로 평가를 결정한 요소는 aa이다.
객체 타입은 pass-by-reference이고 동적으로 변화할 수 있기에 어느 정도 메모리 공간을 확보해야하는지 예측할 수 없다. 그래서 런타임에 메모리 공간을 확보하고, 메모리의 힙 영역(heap)에 저장된다.
원시 값은 pass-by-value이고 한 번 정해지면 변경할 수 없다.(immutable) 런타임(변수 할당 시점)에 메모리의 스택영역에 고정된 메모리 영역을 점유하고 저장된다.
함수 선언문 : function 키워드를 사용하는 방식
함수 표현식 : function(){} (함수명 생략), 변수에 할당한다.
함수 선언문으로 정의된 함수는 선언, 초기화, 할당이 한번에 이루어진다. 자바스크립트 엔진이 스크립트가 로딩되는 시점에서 바로 초기화하고, VO(variable object)에 저장하기 때문이다.
반면 함수 표현식으로 정의된 함수는 함수 호이스팅이 아니라, 변수 호이스팅이 발생한다
가능한 함수표현식으로 함수를 정의하는걸 권한다. 첫 번째 이유는 함수 호출 전 함수를 선언해야한다는 규칙을 무시해서 코드의 구조가 엉성해질 수 있다. 두 번째 이유는 대규모 어플리케이션의 경우 함수 선언문은 인터프리터가 너무 많은 코드를 VO에 저장하므로, 응답속도가 떨어질 수도 있다
함수도 객체이지만, 다른 객체와의 차이점은 호출할 수 있다는 것이다
매개변수(parameter, 인자)는 함수내에서 변수와 동일하게 메모리 공간을 확보하며, 함수에 전달된 argument(인수)가 해당 파라미터에 할당되는 것이다.
따라서 참조객체를 인수로 전달하면 파라미터에 해당 인수를 할당하는 것이고, 참조가 이어지는 것이다!! (Call-by-reference)
함수만의 속성
arguments, 전달 받은 인수가 유사배열 형태로 담겨있다. 함수 내부에서만 사용 가능(rest parameter가 있어서 안 쓸듯..?)
caller, 자신을 호출한 함수.
length, 함수 정의 시 작성된 매개변수의 개수
name, 해당 함수 명
function getType(target) {
return Object.prototype.toString.call(target).slice(8, -1);
}
function isString(target) {
return getType(target) === 'String';
}
스코프는 참조 대상 식별자를 찾아내기 위한 규칙이다.
대부분 C-family language는 블록 레벨 스코프를 따르지만, 자바스크립트는 함수 레벨 스코프를 따른다
단 ECMAScript 6에서 도입된 let, const 키워드를 통해 블록 레벨 스코프를 사용할 수 있다.
var로 변수를 선언하면 전역객체 window의 속성이 된다.
함수의 호출 위치에 따라 상위 스코프를 결정 하는 것을 동적 스코프라하며, 반대로 함수의 선언 위치에 따라 상위 스코프를 결정하는 것을 렉시컬 스코프, 정적 스코프라 한다. JS는 후자이다.
자바스크립트 문법을 보다 엄격히 적용. ESLint를 더 추천함.
전역에 strict mode를 선언하지 마라. (파일 전역 말고, 프로젝트 전체 전역)
일반 함수로서 호출하면 this를 undefined로 바인딩한다. 생성자 함수가 아닌 일반 함수 내부에서 this를 사용할 필요가 없기에. 에러는 발생하지 않음
JS 함수는 호출될 때 암묵적으로 arguments 객체와 this를 암묵적으로 전달 받는다.
함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.
함수를 호출하는 방식은 다양하다
this는 기본적으로 전역객체(window, global)에 바인딩 된다.
내부 함수는 일반 함수, 메소드, 콜백함수 어디에서 선언되었든 관계없이 this는 전역객체를 바인딩한다.
메소드 내부의 this는 해당 메소드를 소유한 객체에 바인딩 된다.
생성자 함수의 경우 new를 통해 호출되는 함수로써, 빈 객체가 생성되고, 이 객체가 this로 바인딩 된다.. new로 생성자 함수를 호출하면 암묵적으로 this를 반환하며, 암묵적으로 다른 return을 하지 않아야 한다.
객체 리터럴과 생성자 함수의 차이. [[prototype]]이 Object.prototype인가, 생성함수.prototype인가.
생성자 함수를 new 없이 호출하면 생성자 함수의 this는 window가 되기 때문에 this.~가 전역변수가 된다.
apply, call, bind
전역코드 진입, 전역 객체 생성, 전역 실행 컨텍스트 생성
스코프 체인 생성/초기화, Variable Instantiation(변수 객체화) 실행, this value 결정
Variable Instantiation은 VO에 속성 : 값을 넣는 과정을 말하며, 반드시 이 순서대로 진행된다
2번의 생성된 함수는 [[Scopes]] 프로퍼티를 가지며, 함수 객체가 실행되는 환경을 가르킨다. 따라서 현재 실행 컨텍스트의 스코프 체인이 참조하고 있는 객체를 값으로 한다.
[[Scopes]]는 자신의 실행 환경(LE) 와 자신을 포함하는 외부 함수의 실행 환경(LE), 전역 객체 를 가르킨다.
이때, 외부 함수의 실행 컨텍스트가 소멸한다해도 [[Scopes]] 프로퍼티가 가르키는 외부 함수의 실행 환경(AO)는 소멸하지 않고 참조할 수 있다. 이것이 클로저 이다.
브라우저 환경에서 제공하는 window, XmlHttpRequest, HTMLElement 등의 DOM 노드 객체와 같이 호스트 환경에 정의된 객체
브라우저와 Nodejs의 다른 호스트 객체는 다를 수 있다.
BOM. 브라우저 탭, 브라우저 창의 모델 생성. 최상위 객체 window와 하위 document, history, location, navigator, screen 등이 있다.
DOM. 현재 웹페이지의 모델을 생성. 최상위 객체 document. 이 객체의 자식 객체들은 문서의 다른 요소들을 표현
브라우저의 window, Node.js의 global을 가르킴.
isFinite() : 정상적인 유한수인지 검사. ('10', null도 true)
parseFloat() : 전달된 문자열을 부동소수점 숫자로 변환한다.
parseInt() : 전달된 문자열을 정수형 숫자로 해석하여 반환한다. 두 번째 인자로 첫 번째 인자의 진법 기수를 넣을 수 있다(기본 10진수). 반환값은 언제나 10진수이다
encodeURI() : 전달된 URI를 인코딩(이스케이프 처리)한다. decodeURI()는 반대로 디코딩.
encodeURIComponent() : 전달된 URI component를 인코딩. decodeURIComponent()는 반대로 디코딩.
isSafeInteger() : 인수가 안전한 정수인지 판별. int 범위를 초과하는지.
toString(n) : n진법으로 변환
toFixed(n) : 매개변수로 지정된 소숫점자리를 반올림해서 문자열로 반환
toPrecision(n) : 매개변수로 지정된 전체 자릿수까지 유효하도록 반올림해서 문자열로 반환. 불가능하면 지수표기법으로
new Date() : 인수를 전달하지 않으면 현재 날짜, 시간을 가지는 인스턴스 반환
new Date(milliseconds) : 1970년 1월 1일 00:00(UTC)을 기점으로 인수로 전달된 밀리초만큼 경과한 날짜와 시간을 가지는 인스턴스를 반환
new Date(dateString) : dateString이 해석 가능한 날짜, 시간이면 그 시간을 갖는 인스턴스 반환
new Date(year, month[, day, hour, minute, second, millisecond]) : 입력한 날짜 시간으을 갖은 인스턴스 반환. 년 월은 필수
new 없이 그냥 Date()하면 인스턴스가 아닌 결과값을 문자열로 반환
.now : 기점에서 현재 시간까지 경과한 밀리초 반환
.parse : 기점에서 인수까지의 시간 밀리초 반환
.UTC : 문자열 인수를 받아서 위와 동이랗게 반환
/패턴/플래그
exec, test, match, replace, search, split 등의 메소드를 가짐
. : 임의의 문자 한 개를 의미한다
| : or
[] : []내의 문자는 or로 동작한다
[-] : 범위 지정
\d : 숫자를 의미, \D는 숫자가 아닌 것을 의미
\w : 알파벳과 숫자를 의미, \W는 알파벳과 숫자가 아닌 것을 의미
\s : 여러가지 공백 문자를 의미. [\t\r\n\v\f]
^ : 특정 단어로 시작하는지
$ : 특정 단어로 끝나는지
/^\d+$/ : 숫자인지
/^[\s]+/ : 하나 이상의 공백으로 시작하는지
/^[A-Za-z0-9]{4,10}$/ : 4~10자리의 숫자, 영어로 된 문자열인지
/^\d{3}-\d{3,4}-\d{4}$/ : 핸드폰 번호 형식에 맞는지
.exec : 문자열을 검색하여 매칭 결과 배열 or null로 반환. g 플래그 써도 첫 번째 매칭 결과만 반환
.test : 문자열을 검색하여 매칭 결과를 true, false로 반환
.lastIndexOf(str, fromIndex) : 인수로 전달한 문자, 문자열을 대상 문자열에서 검색, 마지막으로 발견된 곳의 index 반환. fromIndex를 넣으면 해당 인덱스에서 역방향으로 검색 시작 (이 경우에는 처음 발견되는, 즉 index가 높은 쪽의 요소를 바로 반환)
.trim() : 양쪽 끝에 있는 공백 문자를 제거한 문자열 반환. trimStart, trimEnd도 있음
.of(n) : 전달된 인수를 요소로 갖는 배열을 생성한다. Array(n)과 달리 갯수가 아닌 인수 그 자체
.push(): 원본 배열 맨 마지막에 요소를 추가하는데, array[n] = el;로 직접 넣는게 더 빠르다.
일반적으로 배열이라는 자료구조는 동일한 크기의 메모리 공간이, 빈틈없이 연속적으로 나열된 구조이다. 밀집배열이라 부른다
자바스크립트늰 희소배열로, 메모리 공간이 동일하지도 않고 연속적으로 이어져 있지도 않다. 자바스크립트 배열은 사실 인덱스를 프로퍼티 키로 갖는 특수한 객체다. (해시 테이블)
따라서 인덱스로 배열에 접근하면 일반 배열보다 느리지만, 특정 요소를 탐색하거나 요소를 삽입, 삭제 하는 경우에는 더 빠를 수 있다
함수를 인자로 받거나 결과로 반환하는 함수
forEach
some : 배열 내 일부 요소가 콜백 함수의 테스트를 통과하는지 확인, 그 결과를 boolean으로 반환 (하나라도 통과하면 ok)
every : 배열 내 모든 요소가 콜백 함수의 테스트를 통과하는지
DOM은 두 가지 기능을 담당한다
HTML 문서에 대한 모델 구성 : 브라우저는 HTML 문서를 로드한 후 해당 문서에 대한 모델을 메모리에 생성한다. 이때 모델은 객체의 트리로 구성되는데 이것을 DOM tree라 한다.
HTML 문서 내의 각 요소에 접근 / 수정 : DOM은 모델 내의 각 객체에 접근하고 수정할 수 있는 프로퍼티와 메소드를 제공한다. DOM이 수정되면 브라우저를 통해 사용자가 보게 될 내용 또한 변경된다.
문서 노드, 요소 노드, 어트리뷰트 노드, 텍스트 노드로 구성된다.
document.getElementsByClassName(class) 같은 다중 DOM 선택 API를 사용하면 HTMLCollection으로 반환되는데, 유사 배열이다. 또한 실시간으로 Node의 상태 변경을 반영하기에 해당 반환값을 변경시키면 반환값에서 빠져나갈 수도 있다 (조건이 달라져서)
HTMLCollection를 스프레드 연산자로 배열화 해서 사용하는걸 권장함
querySelectorAll 는 NodeList를 반환하는데, 이는 실시간으로 반영되지 않는다.
브라우저는 싱글쓰레드에서 이벤트 드리븐 방식으로 동작한다.
Heap : 동적으로 생성된 객체 인스턴스가 할당되는 영역
자바스크립트 엔진은 단순히 call stack에 쌓인 요청을 순차적으로 실행할 뿐이다. 동시성을 지원하기 위한 비동기 요청, 이벤트는 자바스크립트 엔진을 구동하는 환경, 즉 브라우저(또는 Nods.js)가 담당한다
인라인 이벤트 핸들러 방식은 사용 금지. html과 js는 관심사가 다르므로 분리한다.
이벤트 캡쳐링부터 시작해서 버블링으로 종료된다. 다만 addEventLister의 3번 째 매개변수를 true로하면 캡쳐링을 캐치하고, false(default)는 버블링을 캐치한다.
즉 '이벤트 전파는 캡처링- 버블링'이 다 일어나지만 그것을 '캐치'하는 방식을 선언해두는 것이다.
target(이벤트를 발생시킨 객체)과 currentTarget(핸들러가 등록된 객체)을 구분하자.
stopPropagation() : 이벤트 처리 후 현 지점에서 버블링을 중단시키는 메소드
Request/Response는 '파일 단위'로 실행된다. 즉 HTML, CSS, JS파일이 각각 요청, 응답된다.
Ajax ( Asynchronous JavaScript and XML) : 자바스크립트를 이용한 비동기적 서버-브라우저 데이터 교환 통식 방식
JSON (JavaScript Object Notation) : 클라이언트와 서버 간 데이터 교환을 위한 규칙, "데이터 포맷". **JS의 객체 리터럴과 유사하지만, 순수 텍스트로 구성된 규칙있는 데이터이다"
키는 반드시 큰 따옴표로 둘러싸야한다. (작은 따옴표 사용 불가)
XMLHttpRequest : 브라우저가 이 "객체"를 사용해서 Ajax 요청을 생성하고, 전송한다. 서버에서 응답을 반환하면 같은 XMLHttpRequest객체가 그 결과를 처리한다.
XMLHttpRequest.open(method, url,[, async]) : 서버로 요청을 준비.
XMLHttpRequest.open(args) : 준비된 요청을 서버에 전달. request body에 담아 전송할 인수를 전달할 수 있다. GET 요청이면 인수는 무시되어 null로 설정된다.
XMLHttpRequest.setRequestHeader : 반드시 XMLHttpRequest.open 이후에 호출된다. 이름 그대로 헤더의 값을 설정함.
Content-type : request body에 담아 전송할 데이터의 "MIME-type"의 정보 표현
Accept : HTTP 클라이언트가 서버에 요청할 때 서버가 센드백 할 데이터의 "MIME-type"을 지정할 수 있음.
보안 상의 이유로 다른 도메인(http와 https, 포트가 다른 도메인)으로의 요청(크로스 도메인 요청은 제한하는 것.
동일 출처 원칙을 우회하는 방법은 3가지, 웹서버의 프록시 파일 / JSONP / Cross-Origin Resource Sharing이 있다.
프록시 : 서버에 원격 서버로부터 데이터를 수집하는 별도의 기능
JSONP : script 태그의 원본 주소에 대한 제약이 존재하지 않음을 이용해 다른 도메인 서버에서 데이터를 수집하는 방법.
CORS : HTTP 헤더에 정보를 추가해서 브라우저와 서버가 서로 통신해야함을 알게 하는 방법.
REST에서 가장 중요한 두 가지 규칙.
HTTP Method
GET : 모든/특정 리소스 조회
POST : 리소스 생성
PUT : 리소스의 전체를 교체
PATCH : 리소스의 일부를 수정
DELETE : 모든/특정 리소스를 삭제
REST API의 구성
link tag를 사용하는 전통적인 웹 방식은 '새로고침'이 되기에 사용성이 좋지 않음
변경이 필요없는 부분을 포함해 전체 페이지를 갱신하기에 비효율적
SPA는 웹에 필요한 모든 정적 리소스를 최초에 한 번 다운 받고, 이후에는 갱신에 필요한 데이터만 받아서 페이지를 갱신함
트래픽 감소, 새로고침 발생x
초기 구동 속도가 느리다.
SEO(검색엔진 최적화) 문제
출발지에서 목적지까지의 경로를 결정하는 기술
어플리케이션에서 라우팅은 사용자가 태스크를 수행하기 위해 어떤 화면에서, 다른 화면으로 전환하는 네이게이션을 관리하니 위한 기능이다.
브라우저가 화면을 전환하는 경우
브라우저의 주소창에 URL 입력하여 해당 페이지로 이동
웹페이지의 링크 클릭하여 해당 페이지로 이동
브라우저의 뒤로가기 / 앞으로가기 버튼 클릭하여 history의 뒤 / 앞으로 이동
SPA의 경우 "데이터를 받아 화면을 리렌더링 할 뿐"이기에 URL이 변경되지 않고, history를 관리할 수 없음을 의미한다. (SEO 이슈 발생 원인)
history를 관리하기 위해서는 각 페이지는 브라우저의 주소창에서 구별할 수 있는 유일한 URL을 소유해야한다.
전통적 링크 방식
Ajax 방식
Hash 방식
Pjax 방식
각기 장단점이 있다. 본 페이지에서 표로 확인할 것