모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다. 이를 스코프(Scope, 유효범위)라 한다. 즉, 스코프는 식별자가 유효한 범위를 말한다.
스코프(유효 범위)를 통해 식별자인 변수 이름의 충돌을 방지하여 같은 이름의 변수를 사용할 수 있도록 한다. 스코프 내에서 식별자는 유일한 것으로 중복되어선 안된다. 따라서 만약 한 스코프 내에 ‘variable’라는 이름의 식별자가 있다면, 이 스코프 내에서는 한 번만 사용할 수 있다. 그러나 다른 스코프에서 동일한 이름의 ‘variable’라는 식별자를 사용하는 것은 가능하다.
스코프가 왜 중요한가?
네임스페이스 충돌을 방지할 수 있다.
변수가 임의 수정되는 현상을 방지할 수 있다.
스코프의 종류
변수는 자신이 선언된 위치(전역 or 지역)에 의해 자신이 유효한 범위인 스코프가 결정됨.
전역(global): 코드의 가장 바깥 영역, 전역 스코프, 전역 변수
전역 변수는 어디에서나 참조할 수 있다.
함수나 블록 안에 들어 있지 않은 모든 변수를 의미한다.
지역(local): 함수 몸체 내부, 지역 스코프, 지역 변수
자신이 선언된 지역과 하위 지역(중첩 함수)에서만 참조할 수 있다.
스코프 체인
스코프는 함수의 중첩에 의해 계층적 구조를 갖고, 모든 지역의 최상위 스코프는 전역 스코프이다. 이처럼 스코프가 계층적으로 연결된 것을 스코프 체인이라 한다. 스코프 체인은 실행 컨텍스트의 렉시컬 환경(코드가 어기서 실행되었고, 주변에 어떤 코드가 있는 지에 관한 context)을 단방향으로 연결한 것이다.
외부 함수: 중첩 함수를 포함하는 함수
중첩 함수: 함수 몸체 내부에 정의한 함수
자바스크립트 엔진은 스코프 체인을 따라 변수를 참조하는 코드의 스코프에서 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다. 절대 하위 스코프로 내려가면서 식별자를 검색하는 일은 없다. 이는 상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조할 수 있지만 하위 스코프에서 유효한 변수를 상위 스코프에서 참조할 수 없다는 것을 의미한다.
함수 레벨 스코프
함수 레벨 스코프: 코드 블록이 아닌 함수에 의해서 생성된 코드 블록을 지역 스코프로 인정한다.
블록 레벨 스코프: 함수 몸체 만이 아니라 모든 코드 블록(if, for, while, try/catch 등)이 지역 스코프를 만든다. e.g. java, c 등
단, var키워드는 함수 내부가 아닌 곳에서 선언하면 코드 블록 내에서 선언되었다 하더라도 전역 변수라서 중복선언을 허용하는데, 함수의 코드 블록만이 지역 스코프로 인정된다.
렉시컬 스코프
동적 스코프: 함수 정의 시점에는 함수가 어디서 호출될 지 알 수 없다. 따라서 함수가 호출되는 시점에 동적으로 상위 스코프를 결정하는 것을 말한다.
렉시컬 스코프 (정적 스코프): 함수 정의가 평가되는 시점에 상위 스코프가 정적으로 결정, 자바스크립트는 함수를 어디서 정의했는지에 따라 상위 스코프를 결정하므로 렉시컬 스코프를 따른다.
스코프
조건
설명
동적 스코프
함수를 호출한 곳
함수 정의 시점에 함수가 호출될 곳이 어딘지 알 수 없음. 함수가 호출되는 시점에 동적으로 상위 스코프 결정
input과 output이 이루어지는 일련의 과정을 문(statement)들로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것. 한 가지 일을 수행하는 코드가 블럭으로 묶여 있는 것.
1 2 3 4 5 6 7
function 함수명(매개변수) { return 반환값; } //함수를 정의하는 코드블록, 함수 정의로 함수생성
함수명(인수); //함수호출, 인수를 매개변수를 통해 함수에게 전달 //코드블록의 문을 실행하여 반환값 반환
함수명은 생략 가능(익명함수) 이름이 있다면 기명 함수
매개변수(parameter): 함수의 정의에서 전달받은 인수를 함수 내부로 전달하기 위해 사용하는 변수. 함수 몸체 내부에서만 참조 가능, 외부에서는 참조할 수 없다.
인수(argument): 함수가 호출될 때 함수로 값을 전달해주는 값. 인수를 매개변수를 통해 함수에게 전달. 값으로 평가될 수 있는 표현식이어야 한다.
인수 -> 인수 전달 -> 매개변수 -> return 반환값 ->함수 외부로 반환 매개변수와 인수의 개수가 일치하는지 여부는 체크하지 않는다. 매개변수의 이상적인 개수는 0개이며, 최대 3개 이상을 넘기지 않는 것을 권장한다. 만약 그 이상이 필요하다면 매개변수 선언 후 객체를 인수로 전달받는 것이 좋다. 매개 변수가 많아지면 함수 호출 시 전달해야 할 인수의 순서를 고려한다.
함수의 사용 이유
코드의 재사용: 동일한 작업을 반복 수행할 때 코드를 여러번 작성하는 대신 호출하여 재사용
코드의 중복 억제: 중복된 코드를 여러 번 작성하면 수정도 여러 번 해야함.
코드의 가독성 향상: 함수 이름(식별자)을 통해 함수의 역할 파악
함수 리터럴
함수 리터럴로 함수를 생성한다. 함수의 이름을 생략할 수 있다.
함수 == 객체 함수 != 일반 객체
함수 - 호출가능, 함수 객체 고유의 프로퍼티 가짐. 일반 객체 - 호출불가
1 2 3 4 5
//add라는 이름의 변수를 만들고 함수를 담는다.
var add = function (a, b) { return a + b; };
함수 리터럴의 4가지 부분
function이라는 예약어 (예시의 function)
function의 이름 (예시는 익명 함수)
기명함수: 함수 이름이 있음
익명 함수: 함수 이름이 없음.
function의 매개변수 (예시의 a,b)
매개변수의 이름은 함수의 변수로 정의된다. 그러나 다른 변수와는 달리 undefined로 초기화되는 대신 함수가 호출될 때의 인수로 초기화된다.
중괄호({})로 감싸진 문(statement)
함수의 몸체
함수가 호출될 때 실행된다.
함수 정의
함수를 호출하기 이전에 인수를 전달받을 매개변수와 실행할 문들, 그리고 반환할 값을 지정하는 것
함수 선언문
함수 리터럴과 형태는 같지만 그와 달리 함수 선언문에서는 함수 이름을 생략할 수 없다. 함수 선언문은 또한 표현식이 아닌 ‘문’이다.(변수에 할당할 수 없다.) 따라서 함수 선언문을 실행하면 undefined가 출력된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
functioncoffee(temp, menu) { return temp + menu; } //함수명(예시에서 coffee) //함수명을 생략하면 오류가 발생한다.
//다음과 같이 함수를 선언할 수도 있다. functioncoffee(temp, menu) { console.log(temp + " " + menu); } coffee("iced", "americano"); //iced americano
함수 선언문 vs 함수리터럴 vs 함수 표현식
1 2 3
functionadd(x, y) { return x + y; }
함수 리터럴: 식별자는 옵션이므로 없어도 된다. 함수 리터럴은 함수 그 자체가 아니라 함수의 값이다. 따라서 함수는 함수 리터럴을 참조한다고 할 수 있다. 단, 자바스크립트 엔진은 상황에 따라 동일한 함수 리터럴에 대해 함수 선언문(표현식이 아닌 문) 또는 함수 리터럴 표현식(표현식인 문)으로 해석하기도 한다.
함수 선언문: 표현식이 아닌 문이다. 단, 자바스크립트 엔진은 함수 선언문을 표현식으로 변환하여 함수 객체를 생성한다고 할 수 있다.
함수 표현식: 표현식인 문이다.
함수 표현식
함수 리터럴로 생성한 함수 객체를 변수에 할당하는 것.
함수는 일급객체이므로 함수를 값처럼 자유롭게 사용할 수 있다. 함수는 객체 타입의 값이기 때문에 변수에 할당할 수 있고, 프로퍼티 값이 될 수 있고, 배열의 요소가 될 수도 있다.
함수 생성 시점과 함수 호이스팅
함수 선언문: 함수를 정의함. 함수 선언문 이전에 호출 및 참조 가능. 런타임 이전에 먼저 실행됨.
1 2 3 4 5
console.log(add(2, 5)); //7
functionadd(x, y) { return x + y; }
함수 정의 -> 함수 객체 생성 -> 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 생성된 함수 객체를 할당 -> 런타임 -> 런타임 때 평가되어 함수 리터럴이 함수 객체가 됨.
함수 호이스팅: 위와 같이 함수 선언문이 선두로 끌어 올려진 것처럼 동작하는 것
변수 호이스팅 vs 함수 호이스팅
공통점: 런타임 이전에 먼저 실행되어 식별자를 생성한다.
변수: undefined로 초기화됨.
함수 선언문: 함수 객체로 초기화됨.
함수 표현식: 변수 할당문의 값이 함수 리터럴인 문. 변수 선언문과 변수 할당문의 축약 표현과 동일하게 동작.
1 2 3 4 5
console.log(sub(2, 5)); // TypeError: sub is not a function
var sub = function (x, y) { return x - y; };
함수 표현식의 함수 리터럴은 할당문이 실행되는 시점(런타임)에 평가되어 함수 객체가 됨. 이 경우 함수 호이스팅이 아니라 변수 호이스팅이 발생.
함수 선언문은 함수 호출 전에 반드시 함수를 선언해야한다는 규칙을 무시하고, 선언문 이전에도 참조 및 호출이 가능하다. 따라서 가능한 함수 표현식을 쓰는 것이 권장된다.
function 생성자 함수
객체를 생성하는 방법에는 factory함수와 생성자(constructor)함수가 있다.
factory 함수: 객체를 반환하는 클래스나 생성자가 아닌 함수. 자바스크립트의 함수는 new 키워드가 없어도 객체를 반환다.
생성자 함수: 객체를 생성하는 함수 step1. 생성자 함수를 작성하여 개체를 정의한다. (파스칼 케이스를 사용한다.) step2. new 키워드를 사용하여 개체의 인스턴스를 만든다.
//함수의 인자로 전달받은 값을 개체의 속성에 할당하기 위해 `this`를 사용한다. //this는 코드블록이 실행될 때 빈 객체를 참조한다.
1 2 3 4 5
mycar = new Car("Eagle", "Talon TSi", 1993); //new는 빈 객체를 만든다. //위에 정의된 함수에서 this는 new가 생성한 빈 객체를 가리킬 수 있게 한다. //this를 통해 new가 생성한 빈 객체에 접근한다. //new가 새로 생성한 객체에 함수의 값을 반환한다.
화살표 함수
ES6에서 새롭게 도입된 화살표 함수(Arrow function)는 function 키워드 대신 화살표(=>, Fat arrow)를 사용하여 보다 간략한 방법으로 함수를 선언할 수 있다. 화살표 함수는 항상 익명 함수로 정의한다.
1 2 3
const add = (x, y) => x + y;
console.log(add(2, 5)); // 7
반환문
함수의 실행을 중단하고 함수 몸체를 빠져나간다.
반환문 이후에 다른 문이 존재하면 그 문은 무시된다.
반환문을 생략하면 undefined가 반환된다.
1 2 3 4 5
functionsum(x, y) { return x + y; //반환문 console.log(sum(2, 3)); //실행되지 않는다. } console.log(sum(2, 3)); // 5
다양한 함수의 형태
즉시실행함수
함수 정의와 동시에 즉시 호출된다. 단 한번만 호출되며 다시 호출할 수 없다. 보통 익명함수를 사용함.
1 2 3 4 5 6
// 익명 즉시 실행 함수 (function () { var a = 3; var b = 5; return a * b; })();
재귀 함수
함수가 자기 자신을 호출하는, 재귀 호출을 수행하는 함수를 말한다. 재귀 함수는 자신을 무한 재귀 호출하므로 반드시 탈출 조건을 만들어야한다. 그렇지 않으면 에러가 발생한다. 대부분 재귀 함수는 for문이나 while문으로 구현할 수 있다. 하지만 재귀함수를 쓰면 반복문 없이도 구현이 가능하다.
값을 생성하는 가장 간단한 방법 사람이 이해할 수 있는 문자로 값을 생성 런타임 때 값을 평가하여 생성함
표현식인 문과 표현식이 아닌 문
값: 표현식이 평가되어 생성된 결과 표현식: 값으로 평가될 수 있는 문 문: 컴퓨터에 내리는 명령. 선언문, 할당문, 조건 문 등 표현식인 문: 변수에 할당할 수 있는 문으로 값을 생성한다. 표현식이 아닌 문: 변수에 할당할 수 없다.
1 2 3 4 5 6 7 8 9 10
//example var a; ->undefined //변수 선언문은 표현식이 아닌 문이기 때문에 //평가할 수 없어서 완료된 평가값이 나올 자리에 //평가값 대신 undefined가 출력된다. //undefined는 평가된 값이 아닌 완료값이다. a = 1; ->2 //변수 할당문은 표현식인 문이기 때문에 평가된 값 2가 출력된다.
const value1 = "5"; const value2 = 9; let sum = value1 + value2;
console.log(sum);
숫자 리터럴 9는 문자열로 변환되고, value1과 value2의 값이 연결되어 문자열 59가 출력된다. 자바스크립트는 문자열과 숫자가 있을 때 문자열을 이용하기 때문이다. 따라서 만약 숫자 5 + 9를 더한 14의 결과를 출력하고 싶다면 number() 메소드를 통해서 명시적인 타입 변환을 해주어야 한다.
1
sum = Number(value1) + value2;
명시적 타입 변환(Explicit coercion) 또는 타입 캐스팅(Type casting) 개발자가 의도적으로 값의 타입을 변환하는 것
암묵적 타입 변환(Implicit coercion) 또는 타입 강제 변환(Type coercion) 개발자의 의도와 상관없이 자바스크립트 엔진에 의해 암묵적으로 타입이 자동 변환되는 것.
이러한 명시적 또는 암묵적 타입 변환이 기존 원시값을 직접 변경하는 것은 아니다. 원시값은 변경불가이기 때문이다. 타입 변환이란 기존 원시값을 사용하여 다른 타입의 새로운 원시값을 생성하는 것이다.
1 2 3
// 원시값 1이 '1'로 직접 변경되는 것이 아니다. // 1을 사용해 타입이 다른 '1'을 새롭게 생성하여 '1' + ''을 평가한다. 1 + ""; // '1'
암묵적 타입 변환
표현식을 평가할 때 코드 문맥에 부합하지 않더라도 자바스크립트는 암묵적 타입 변환을 통해 표현식을 평가하고, 문자열, 숫자, 불리언과 같은 원시 타입 중 하나로 타입을 자동 변환한다.
1 2 3
1 + "2"; //"12" 문자열 타입으로 변환 1 - "1"; //0 숫자열 타입으로 변환 //불리언 타입으로 변환
명시적 타입 변환
개발자의 의도에 의해 명시적으로 타입을 변환한다.
문자열 타입으로 변환
String 생성자 함수를 new 연산자 없이 호출하는 방법 ex) String(1); -> “1”
Object.prototype.toString 메소드를 사용하는 방법 ex) (1).toString(); -> “1”
문자열 연결 연산자를 이용하는 방법 1 + ‘’; -> “1”
숫자 타입으로 변환
number 생성자 함수를 new 연산자 없이 호출하는 방법 ex) Number(‘0’) -> 0
parseInt, parseFloat 함수를 사용하는 방법(문자열만 숫자 타입으로 변환 가능) ex) parseInt(‘0’) -> 0
단항 산술 연산자를 이용하는 방법 ex) +’0’; -> 0
산술 연산자를 이용하는 방법 ex) ‘0’ * 1 -> 0
빈 문자열(‘’), 빈 배열([]), null, false는 0으로 변환된다.
true는 1로 변환된다. ex) true * 1; -> 1
객체와 빈 배열이 아닌 배열, undefined는 변환되지 않으므로 null이 된다.
불리언 타입으로 변환: 조건식의 평가 결과를 불리언 타입으로 암묵적 타입 변환.
Boolean 생성자 함수를 new 연산자 없이 호출하는 방법 ex)Boolean(‘x’) -> true
! 부정 논리 연산자를 두번 사용하는 방법 ex) !!’x’ -> true
falsy값: false, undefined, null, 0, -0, NaN, 빈 문자열
truthy값: false 값을 제외한 모든 값
단축 평가
논리합(||) 연산자와 논리곱(&&) 연산자 표현식의 평가 결과는 불리언 값이 아닐 수도 있다. 논리합(||), 논리곱(&&) 연산자 표현식은 언제나 2개의 피연산자 중 어느 한쪽으로 평가된다. 표현식을 평가하는 도중 결과가 확정되면 나머지 평가 과정을 중단하는 것이다. 이러한 성질을 이용하여 논리곱(&&)과 논리합(||)연산자는 if문을 대체할 수 있기도 하다.
'Cat' && 'Dog' // 'Dog' 논리곱(&&)연산자는 두 개의 피연산자 모두 true일 때 true를 반환한다. 첫 번째 피연산자 Cat을 평가했을 때 true로 평가되지만 두번째 피연산자까지 평가해보아야 완전한 평가가 이루어진다. 따라서 두번째 피연산자인 Dog까지 평가한 후 Dog를 반환한다.
'Cat' || 'Dog' // 'Cat' 논리합(||) 연산자는 두 개의 피연산자 중 하나만 true여도 true를 반환하기 때문에 첫 번째 피연산자 Cat이 true이므로 두번째 피연산자 Dog를 평가하지 않고 바로 Cat을 반환한다.
객체는 다양한 타입의 값(원시 값 또는 다른 객체)들을 하나의 단위로 구성한 복합적인 자료 구조이다. 0개 이상인 속성명(property)과 객체의 관련된 값이 중괄호({})로 묶여진다. 즉 키(key)과 값(value)으로 구성된 프로퍼티(Property)들의 집합이라고 할 수 있다.
1 2 3 4 5 6 7 8 9 10
키워드 식별자 = { key(속성) : value(값) }
var counter = { number : 0, //프로퍼티 increase: function() { this.num++; //메소드 } }
자바스크립트에 사용할 수 있는 모든 값이 프로퍼티 값이 될 수 있고, 함수도 사용할 수 있다. 이 경우 일반 함수와 구분하기 위해 메소드(Method)라 부른다.
프로퍼티와 메소드의 역할
프로퍼티: key와 value를 통해 객체의 상태를 나타낸다.
메소드: 프로퍼티를 참조하고 조작할 수 있는 동작을 말한다.
객체 리터럴에 의한 객체 생성
클래스 기반 객체지향 언어: 클래스 사전정의, 필요한 시점에 new연산자와 생성자를 호출하여 인스턴스를 생성하여 객체 생성 (C++, java 등) 프로토타입 기반 객체지향 언어: 자바스크립트. 객체 리터럴, Object 생성자 함수, 생성자 함수, Object.create 메소드, 클래스 (ES6) 등의 방법으로 객체 생성
객체 리터럴 객체를 생성하는 표기법이며, 변수에 할당이 이루어지는 시점에 자바스크립트 엔진은 객체 리터럴을 해석하여 객체를 생성한다. var empty = {};와 같이 프로퍼티를 정의하지 않으면 빈 객체가 생성된다. 서버에게 주소를 넣어달라고 요청하는 것처럼 연속된 구조체나 연관된 데이터를 일정한 방법으로 변환하고자 할 때 많이 쓰인다.
객체 리터럴의 중괄호({})는 코드블록이 아니다. 코드블록에는 중괄호 뒤이 세미콜론이 붙지 않지만, 객체 리터럴은 값으로 평가되는 표현식이므로 중괄호 뒤에 세미콜론을 붙인다.
프로퍼티
프로퍼티 키 : 빈 문자열을 포함하는 모든 문자열 또는 symbol 값
프로퍼티 값 : 자바스크립트에서 사용할 수 있는 모든 값
프로퍼티 키는 프로퍼티 값에 접근할 수 있는 이름이며, 식별자 역할을 하며 식별자 네이밍 규칙을 준수하는 것이 권장된다.
프로퍼티 접근
마침표(.)를 사용하는 마침표 표기법(Dot notation) 또는 대괄호([…])를 사용하는 대괄호 표기법(Bracket notation)을 사용한다. 대괄호 표기법을 사용하는 경우, 대괄호 내부에 지정하는 프로퍼티 키는 반드시 따옴표로 감싼 문자열이어야 한다. 자바스크립트에서 사용 가능한 유효한 이름이 아닐 때도 반드시 대괄호 표기법을 사용하며, 대괄호 내의 프로퍼티 키는 따옴표로 감싼 문자열이어야 한다.
1 2 3 4 5 6 7 8 9 10
var person = { name: "Lee", };
// 마침표 표기법에 의한 프로퍼티 접근 // 객체이름.접근하고자 하는 항목 console.log(person.name); // Lee
// 대괄호 표기법에 의한 프로퍼티 접근 console.log(person["name"]); // Lee
존재하지 않는 프로퍼티에 접근: undefined를 반환. referenceError가 발생하지 않는다.
프로퍼티 값 갱신: 이미 존재하는 프로퍼티에 값을 할당한다. 나중에 선언한 프로퍼티가 먼저 선언한 프로퍼티를 덮어쓴다.
프로퍼티 동적 생성: 존재하지 않는 프로퍼티에 값을 할당한다.
프로퍼티 삭제: delete 연산자를 사용한다. 이때 delete 연산자의 피연산자는 프로퍼티 값에 접근할 수 있는 표현식이어야 한다. 만약 존재하지 않는 프로퍼티를 삭제하면 아무런 에러없이 무시된다.
프로퍼티 키 생략: ES6에서 프로퍼티 값으로 변수를 사용할 때 변수 이름과 프로퍼티 키가 동일한 이름이면 프로퍼티 키를 생략할 수 있다.
식(표현식, expression)이 평가(evaluate)되어 생성된 결과 var result = 10 + 20;에서 10과 20이라는 숫자는 런타임(할당 이전)에 평가되어 값을 생성하고, 런타임 때 10 + 20을 평가하여 30이라는 값을 result에 할당한다. 즉, 변수 result가 기억하는 메모리 공간의 값은 10 + 20이 아닌 값 30이다.
리터럴(literal)
사람이 이해할 수 있는 문자 또는 약속된 기호를 사용하여 값을 생성하는 표기 방식
자바스크립트 엔진은 코드가 실행되는 시점(런타임, runtime)에 리터럴을 평가하여 값을 생성한다. 즉, 리터럴은 값을 생성하기 위해 미리 약속한 표기법(notation)이라고 할 수 있다.
10, 20은 사람들 사이에서 숫자리는 기호로 약속되어 있는데, 이를 숫자 리터럴이라고 한다.
숫자 리터럴 10과 20을 코드 내에 var result = 10 + 20;이라고 작성하면 자바스크립트는 이를 평가하여 런타임 이전에 숫자 값 10과 20을 생성한다.
런타임 때 코드가 실행되는데 이때 리터럴은 평가되어 값을 생성한다. 즉 10 + 20의 값을 평가한 결과 30을 result라는 변수의 값이 생성된다.
표현식 (expression)
값으로 평가(evaluation)될 수 있는 문(statement)이다. 즉, 표현식이 평가되면 값을 생성하거나 값을 참조한다. 따라서 값으로 평가되는 문은 모두 표현식이다. 이때 표현식과 표현식이 평가된 값은 동등한 관계, 즉 동치(equivalent)이다.
변수에 값을 할당 x = 7은 x에 7을 할당시키기 위해 연산자(=)를 사용한 것으로, 7로 계산된다.
단순히 값을 가짐. 3 + 4는 위 예시처럼 변수에 7을 할당하는 것이 아니라 그 자체로 3과 4를 더한 값을 갖는다.
표현범주: 산수(산술 연산자), 문자열, 논리(참, 거짓), 일차식, 좌변식
문(statement)
프로그램을 구성하는 기본 단위이자 최소 실행 단위. 문의 집합으로 이루어진 것이 프로그램이며 문을 작성하고 순서에 맞게 나열하는 것이 프로그래밍이다. 문은 여러 토큰들로 구성된다. 토큰(token)이란 문법적인 의미를 가지며, 문법적으로 더이상 나눌 수 없는 코드의 기본 요소를 의미한다. 선언문, 할당문, 조건문, 반복문 등이 있다.
문은 반복문이나 if문처럼 action을 수행하게 하기 때문에 명령문이라고도 한다. 자바스크립트 엔진이 문이 있기를 기대하는 곳에 표현식을 쓸 수 있는데, 이를 표현식인 문이라고 한다. 그러나 그 반대는 불가하다. 즉 표현식이 있기를 기대하는 곳에 문을 쓸 수는 없다. 예를들어 if문은 함수의 인수가 될 수 없다.
세미콜론과 세미콜론 자동 삽입 기능
세미콜론( ; )은 문의 종료를 나타내며, 문은 반드시 세미콜론으로 끝나야 한다. 단, 0개 이상의 문을 중괄호로 묶은 코드 블록 { … } 뒤에는 세미콜론을 붙이지 않는다. 예를 들어 if 문, for 문, 함수 등의 코드 블록 뒤에는 세미콜론을 붙이지 않는다. 이들 코드 블록은 언제나 문의 종료를 의미하는 자체 종결성(self closing)을 갖기 때문이다.
표현식과 문
표현식: become a value. 값이 생성되거나 문의 일부가 될 수 있다. 문: perform action. 값이 생성되지 않고, 실행문이나 코드블록의 action을 컨트롤할 뿐이다.
const x = 5;는 숫자 리터럴 5를 해석하여 값 5를 생성하는 것이다. const y = getAnswer();는 어떤 값을 반환하도록 호출하는 것이므로 역시 표현식이다.
표현식인 문과 표현식이 아닌 문
표현식인 문과 표현식이 아닌 문을 구별하는 방법은 변수에 할당하는 것이다. 변수에 할당이 가능하면 표현식인 문, 그렇지 않으면 표현식이 아닌 문이다.
문: 실행되거나 특정한 action을 수행할 수 있는 코드의 조각을 말한다. 표현식: 평가되어 값을 생성할 수 있는 코드의 조각을 말한다. 표현식인 문: 값으로 평가될 수 있는 문. ex.변수 할당문 표현식이 아닌 문: 값으로 평가될 수 없는 문 ex.변수 선언문
1 2 3 4 5
var x; //변수 선언문 //undefined
var x = 10; //변수 할당문 //10
구글 개발자 도구에서 위의 var x;와 같이 변수 선언문을 실행하면 undefined가 반환된다. 변수 선언문은 표현식이 아닌 문이므로 값을 반환해야할 자리에 평가된 값이 아닌 undefined가 출력되는 것이고, 이를 완료값이라고 한다. 이는 자바스크립트 엔진이 암묵적으로 할당해주는 것이며, 변수에 담을 수 없고, 참조할 수 없는 값이다. 반면 변수에 할당을 하면 할당된 값이 출력되므로 할당문은 표현식인 문이라고 할 수 있다.
2개의 피연산자를 산술 연산하여 숫자 타입의 값을 만든다. 피연산자의 값을 변경하는 부수 효과는 없다. 언제나 새로운 값을 만들 뿐이다.
operand1 operator operand2 ex) 3 + 4
단항 산술 연산자 (Unary operators)
1개의 피연산자를 산술 연산하여 숫자 타입의 값을 만든다. 주로 반복문에서 많이 쓰인다.
operator operand or operand operator ex) a++ or ++a
주의: 이항 산술 연산자와 달리 증가/감소(++/–) 연산자는 피연산자의 값을 변경하는 부수 효과가 있다.
증가/감소(++/--) 연산자는 위치에 따른 의미 .전위 증가/감소 연산자: 피연산자의 앞에 위치하여 먼저 피연산자의 값을 증가/감소시킨 후, 다른 연산을 수행한다. .증가/감소(++/—) 연산자: 피연산자의 뒤에 위치하여 먼저 다른 연산을 수행한 후, 피연산자의 값을 증가/감소시킨다.
1 2 3 4 5 6 7 8 9 10 11 12 13
var num1 = 4; ++num1; // -> 5; num1; // -> 5;
/* ++num1는 num1의 값을 먼저 증가시킨 후 할당한다. (선증가 후할당) */
var num2 = 4; num2++; // -> 4; num2; //-> 5;
/* num2++은 num2의 값을 먼저 할당한 후 증가시킨다. (선할당 후증가) */
문자열 연결 연산자 (String operators)
+연산자는 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 동작한다. 참고로 javascript에서 true는 1, false는 0과 같다.
1 2 3 4 5 6 7 8 9 10 11
console.log("my " + "string"); // console logs the string "my string".
암묵적 타입 변환(Implicit coercion) 또는 타입 강제 변환(Type coercion) 개발자의 의도와는 상관없이 자바스크립트 엔진에 의해 암묵적으로 타입이 자동 변환되기도 한다. 위 예제에서 1 + true를 연산하면 자바스크립트 엔진은 암묵적으로 불리언 타입의 값인 true를 숫자 타입인 1로 타입을 강제 변환한 후 연산을 수행한다.
할당 연산자 (Assignment Operator)
우항에 있는 피연산자의 평가 결과를 좌항에 있는 변수에 할당한다.
좌항의 피연산자는 우항의 피연산자의 값에 기초한다.
x = y는 y의 값을 x에 할당하겠다는 뜻이다.
할당 연산자는 좌항의 변수에 값을 할당하므로 변수의 값이 변하는 부수 효과가 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13
= : x = 5 = x = 5 += : x += 5 = x = x + 5 -= : x -= 5 = x = x - 5 *= : x *= 5 = x = x * 5 /= : x /= 5 = x = x / 5 %= : x %= 5 = x = x % 5
//우항의 값을 좌항의 변수값에 할당한 후, 새로운 변수 값을 반환한다.
var x = 3; //x는 3이라는 값을 담고 있다. var y = 4; //y는 4라는 값을 담고 있다.
x *= y; //x는 12라는 값을 담고 있다.
비교 연산자 (Comparison operators)
두 값(좌항과 우항)의 피연산자를 비교한 다음 그 결과로 불리언 값을 반환한다. 비교 연산자는 if 문이나 for 문과 같은 제어문의 조건식에서 주로 사용한다.
동등/일치 비교 연산자
좌항과 우항의 피연산자가 같은 값을 갖는지 비교하여 불리언 값을 반환한다. 동등 비교 연산자는 느슨한 비교를 하지만 일치 비교 연산자는 엄격한 비교를 한다.
== 동등 비교: x == y x와 y의 값이 같음
=== 일치 비교: x === y x와 y의 값과 타입이 같음
!= 부동등 비교: x != y //x와 y의 값이 다름
!== 불일치 비교: x !== y //x와 y의 값과 타입이 다름
동등 비교(==) 연산자는 좌항과 우항의 피연산자를 비교할 때 먼저 암묵적 타입 변환을 통해 타입을 일치시킨 후, 같은 값인지 비교한다. 타입은 다르지만 암묵적 타입 변환을 통해 타입을 일치시키면 동등하다. 5 == ‘5’; // -> true
다음을 주의하여야 한다.
NaN === NaN; : false;
0 === -0; : true;
0 == -0 : true;
대소 관계 비교 연산자
피연산자의 크기를 비교하여 불리언 값을 반환한다.
>: x > y : x가 y보다 크다
<: x < y : x가 y보다 작다
>=: x >= y : x가 y보다 같거나 크다
<=: x <= y : x가 y보다 같거나 크다
삼항 조건 연산자 (ternary operator)
조건식 ? 조건식이 true일때 반환할 값 : 조건식이 false일때 반환할 값
조건식의 평가 결과에 따라 반환할 값을 결정한다. 조건식이 참이면 콜론(:) 앞의 두번째 피연산자가 평가되어 반환되고, 거짓이면 콜론(:) 뒤의 세번째 피연산자가 평가되어 반환된다.
1 2 3 4 5 6
var x = 2;
// 2 % 2는 0이고 0은 false로 암묵적 타입 변환된다. var result = x % 2 ? "홀수" : "짝수";
console.log(result); // 짝수
논리 연산자 (Logical Operator)
우항과 좌항의 피연산자(부정 논리 연산자의 경우, 우항의 피연산자)를 논리 연산한다.
1 2 3
|| 논리합(OR) ✕ && 논리곱(AND) ✕ ! 부정(NOT) ✕
논리 부정(!) 연산자는 언제나 불리언 값을 반환한다. 단, 피연산자가 반드시 불리언 값일 필요는 없다. 논리합(||) 또는 논리곱(&&) 연산자 표현식의 평가 결과는 불리언 값이 아닐 수도 있다. 논리합(||) 또는 논리곱(&&) 연산자 표현식은 언제나 2개의 피연산자 중 어느 한쪽으로 평가된다.
쉼표 연산자
왼쪽 피연산자부터 차례대로 피연산자를 평가하고 마지막 피연산자의 평가가 끝나면 마지막 피연산자의 평가 결과를 반환한다.
1 2 3
var x, y, z;
(x = 1), (y = 2), (z = 3); // 3
그룹 연산자
연산자 우선순위(Operator precedence)와 관련있다. 일부 연산자는 합계 (프로그래밍에서 표현식이라고 함)의 결과를 계산할 때 다른 연산자보다 먼저 적용된다. JavaScript의 연산자 우선 순위는 수학 시간에 배운 것처럼 곱하기와 나누기는 항상 먼저 수행 한 다음 더하기와 빼기 (합은 항상 왼쪽에서 오른쪽으로 평가됨) 따라서 먼저 연산하고 싶은 부분을 그룹 연산자()로 묶어서 우선 순위를 조절할 수 있다.
1 2 3 4
10 * 2 + 3; // -> 23
// 그룹 연산자를 사용하여 우선 순위 조절 10 * (2 + 3); // -> 50
typeof 연산자
피연산자의 데이터 타입을 문자열로 반환한다. typeof 연산자는 7가지 문자열 “string”, “number”, “boolean”, “undefined”, “symbol”, “object”, “function” 중 하나를 반환한다. “null”을 반환하는 경우는 없으며 함수의 경우 “function”을 반환한다.
typeof 연산자로 null 값을 연산해 보면 “null”이 아닌 “object”를 반환하는데 이는 자바스크립트의 첫 번째 버전의 버그이다. 따라서 null 타입을 확인할 때는 typeof 연산자 보다는 연산자(===)을 사용하는 것이 좋다. 또한 선언하지 않은 식별자를 typeof 연산자로 연산해 보면 ReferenceError가 발생하지 않고 “undefined”를 반환한다.
지수 연산자
ES7에서 새롭게 도입된 지수 연산자는 좌항의 피연산자를 밑으로, 우항의 피연산자를 지수로 거듭 제곱하여 숫자 타입의 값을 반환한다. 이항 연산자 중 우선순위가 가장 높다. 참고로 지수 연산자 이전에는 Math.pow(2,2)와 같이 사용하였다.
좌항의 피연산자가 null 또는 undefined인 경우 undefined를 반환, 그렇지 않으면 우항의 프로퍼티 참조를 이어간다. 논리 연산자 &&와 같은 기능이다.
&&를 사용할 때: 중첩된 구조의 객체 obj에서 하위 중첩된 하위 속성을 찾을 때 다음과 같이 사용한다. obj.first는 태스트 없이 obj.first.second에 직접 접근할 때 생기는 에러를 방지하기 위해서 값에 접근하기 전에 null 또는 undefined가 아니라는 점을 검증하는 것이다.
let nestedProp = obj.first && obj.first.second;
옵셔널 체이닝을 사용: &&를 사용할 때 처럼 명시적인 테스트가 필요없다. ?.의 옵셔널 체이닝을 사용함으로써 obj.first.second에 접근하기 전에 obj.first가 null 또는 undefined가 아니라는 것을 암묵적으로 확인한다.
let nestedProp = obj.first?.second;
obj.first가 null or undefined라면 undefined가 반환된다. 그렇지 않으면 우항의 프로퍼티 참조를 이어간다.
// Written as of ES2019 functiondoSomething(onContent, onError) { try { // ... do something with the data } catch (err) { if (onError) { // Testing if onError really exists onError(err.message); } } }
1 2 3 4 5 6 7 8
// Using optional chaining with function calls functiondoSomething(onContent, onError) { try { // ... do something with the data } catch (err) { onError?.(err.message); // no exception if onError is undefined } }
typeof null은 ‘null’을 반환하지 않는다. null의 타입은 원시타입인데, object를 반환한다. 이는 첫 번째 자바스크립트 버전의 고칠 수 없는 버그이다. 만약 고치게 될 경우 기존 코드에 영향을 미칠 수 있기 때문이다.
숫자 타입
자바스크립트의 숫자는 실수 타입 하나만 존재한다. (C, Java와 같은 언어는 정수와 실수 등으로 숫자를 구분함.) 정수나 음의 정수 등도 모두 실수로 처리한다.
정수, 실수, 2진수, 8진수, 16진수 리터럴은 모두 메모리에 배정밀도 64비트 부동소수점 형식의 2진수로 저장
자바스크립트는 2진수, 8진수, 16진수를 표현하기 위한 데이터 타입을 제공하지 않기 때문에 이들 값을 참조하면 모두 10진수로 해석된다.
1 2 3 4 5 6 7 8 9 10
var binary = 0b01000001; // 2진수 var octal = 0o101; // 8진수 var hex = 0x41; // 16진수
// 표기법만 다를 뿐 모두 같은 값이다. console.log(binary); // 65 console.log(octal); // 65 console.log(hex); // 65 console.log(binary === octal); // true console.log(octal === hex); // true
javascript는 모든 수를 실수로 처리하기 때문에 정수와 정수를 나눴을 때 실수가 나올 수도 있다. (ex. console.log(3/2) //1.5)
다음과 같은 3가지 특별한 값을 표현할 수도 있다. NaN의 경우 반드시 대소문자를 구별해야 한다.
Infinity : 양의 무한대
Infinity : 음의 무한대
NaN : 산술 연산 불가(not-a-number)
문자열 타입
텍스트 데이터를 나타내는 데 사용하며, 0개 이상의 16bit 유니코드 문자(UTF-16) 들의 집합이다. 작은 따옴표(‘’), 큰 따옴표(“”) 또는 백틱(``)으로 텍스트를 감싸서 사용한다.
‘abc’, ‘’, “”, `a` 모두 문자열이다.
만약 문자열을 따옴표(‘’)로 감싸지 않는다면?
1 2 3 4 5
var string = "hello"; typeof string; // "string"
var str = hello; //ReferenceError: hello is not defined
위와 같이 문자열을 따옴표(‘’)로 감싸지 않으면 자바스크립트 엔진은 문자열을 키워드나 식별자와 같은 토큰으로 인식하므로 에러가 발생한다. 문자열을 따옴표로 감싸는 이유는 키워드, 식별자 같은 토큰과 구분하기 위해서이기 때문이다. 또한 스페이스와 같은 공백 문자도 따옴표 없이는 사용할 수 없다.
템플럿 리터럴
ES6부터 도입되었으며, 작은 따옴표(‘’) 또는 큰 따옴표(“”) 같은 일반적인 따옴표 대신 백틱(backtick) (``)을 사용한다.
멀티라인 문자열
일반문자열에서는 개행이 허용되지 않으므로 백슬래시(\)로 시작하는 이스케이프 시퀀스를 사용해야 하지만, 백틱(``)을 사용하면 개행하지 않아도 되고, 공백도 있는 그대로 적용된다.
1 2 3 4 5 6 7 8 9 10 11
//일반문자열은 이스케이프 시퀀스를 사용하여 줄바꿈한다. (줄바꿈 /n) var template = '<ul>\n\t<li><a href="#">Home</a></li>\n</ul>';
console.log(template);
//템플릿 문자열은 이스케이프 시퀀스가 없어도 줄바꿈 가능 (백틱사용) var template = `<ul> <li><a href="#">Home</a></li> </ul>`;
console.log(template);
표현식 삽입
${ }으로 표현식을 감싸서 사용한다. 이때 표현식의 평가 결과가 문자열이 아니더라도 문자열로 강제 타입 변환되어 삽입된다. 단, 템플릿 리터럴을 일반 문자열에서 삽입하면 문자열로 취급된다.
1 2 3 4 5 6 7 8 9
//일반 문자열은 '+'연산자로 문자열을 연결한다. var first = "hello"; var second = "world"; console.log(first + " " + second); //hello world
//템플릿 리터럴 var first = "hello"; var second = "world"; console.log(`${first}${second}`); //hello world
불리언 타입
논리적 참, 거짓을 나타내는 true와 false를 말한다. 제어문에서 주로 사용한다.
undefined 타입
개발자가 의도적으로 할당하기 위한 값이 아니라 자바스크립트 엔진이 변수를 초기화할 때 사용하는 값. 변수 선언이 되면 자바스크립트 엔진은 undefined를 암묵적으로 할당한다. 변수 참조 시 undefined가 반환된다면 변수 선언 후 아직 할당이 이루어지지 않았음을 알 수 있다.
null 타입
변수에 값이 없다는 것을 의도적으로 명시(의도적 부재 Intentional absence)하고 싶다면 undefined가 아니라 null을 할당한다. null을 할당하는 것은 변수가 이전에 참조하던 값을 더이상 참조하지 않겠다는 의미이며, 가비지 컬렉팅이 이루어질 것이다.
symbole 타입
ES6에서 새롭게 추가된 7번째 타입으로 변경 불가능한 원시 타입이며, 다른 값과 중복되지 않는다. 심볼은 주로 이름의 충돌 위험이 없는 객체의 유일한 프로퍼티 키(property key)를 만들기 위해 사용한다. 함수를 호출해 생성하지만 참조할 수는 없다.
1 2 3 4 5 6 7 8 9 10
// 심볼 값 생성 var key = Symbol("key"); console.log(typeof key); // symbol
// 객체 생성 var obj = {};
// 심볼 key를 이름의 충돌 위험이 없는 유일한 프로퍼티 키로 사용한다. obj[key] = "value"; console.log(obj[key]); // value
데이터 타입의 필요성
데이터 타입에 의한 메모리 공간의 확보와 참조 자바스크립트 엔진은 데이터 타입, 즉 값의 종류에 따라 정해진 크기의 메모리 공간을 확보한다. 즉, 변수에 할당되는 값의 데이터 타입에 따라 확보해야 할 메모리 공간의 크기가 결정된다.
데이터 타입에 의한 값의 해석
값을 저장할 때 확보해야 하는 메모리 공간의 크기를 결정하기 위해
값을 참조할 때 한번에 읽어 들여야 할 메모리 공간의 크기를 결정하기 위해
메모리에서 읽어 들인 2진수를 어떻게 해석할 지를 결정하기 위해
정적 타입(static/strong type)
C나 Java와 같은 정적 타입(Static/Strong type) 언어는 변수를 선언할 때 변수에 할당할 수 있는 값의 종류, 즉 데이터 타입을 사전에 선언해야 한다. 이를 명시적 타입 선언(explicit type declaration)이라 한다.
동적 타입(Dynamic/Weak type)
자바스크립트는 값을 할당하는 시점에 변수의 타입이 동적으로 결정되고 변수의 타입을 언제든지 자유롭게 변경할 수 있다. 다시 말해 자바스크립트 변수는 선언이 아닌 할당에 의해 타입이 결정된다. 그리고 재할당에 의해 변수의 타입은 언제든지 동적으로 변할 수 있다.
if (age > 19) { canDrinkAlcohol = "OK. You can buy alcohol."; } else { canDrinkAlcohol = "No. You can't buy alcohol."; } console.log(canDrinkAlcohol);
if문을 삼항조건연산자로 써보기
1 2 3 4 5 6
var age = 19; var canDrinkAlcohol = "";
canDrinkAlcohol = age > 19 ? "OK. You can buy alcohol." : "No. You can't buy alcohol."; console.log(canDrinkAlcohol);
신장을 출력하는 if문
1 2 3 4 5 6 7 8 9 10
var height = 169; var result = "";
var result = height > 169 ? "wow, you are taller than me" : height < 169 ? "You are little bit shorter than me." : "You and me are the same height."; console.log(result);
if문을 삼항조건연산자로 써보기
1 2 3 4 5 6 7 8 9 10
var height = 169; var result = "";
if (height > 169) { console.log("wow, you are taller than me"); } elseif (height < 169) { console.log("You are little bit shorter than me."); } else { console.log("You and me are the same height."); }