<script>
태그를 이용하여 자바스크립트 코드를 HTML 문서에 삽입할 수 있다.
<!DOCTYPE HTML>
<html>
<body>
<p>스크립트 전</p>
<script>
alert( 'Hello, world!' );
</script>
<p>스크립트 후</p>
</body>
</html>
<script>
태그에 src
속성을 사용하여 외부에서 작성한 자바스크립트 코드를 HTML에 삽입할 수 있다.
// 상대 경로 방식 예시
<script src="/path/to/script.js"></script>
// 절대 경로 방식 예시
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
ES5 이후 기존 기능 중 일부가 변경되어, 하위 호환성에 문제가 생겼다.
기본 모드에선 변경사항의 대부분이 활성화되지 않지만, 특별 지시자를 사용해 strict mode를 활성화하면 변경사항을 활성화할 수 있다.
스크립트 최상단에 use strict 지시자가 오면, 스크립트 전체가 "모던"하게 동작한다.
"use strict";
...
함수 본문 맨앞에 use strict 지시자가 오면, 해당 함수만 strict mode로 실행된다.
function func() {
"use strict";
...
}
참고로, 클래스와 모듈을 사용한 모던 자바스크립트는 use strict가 자동으로 적용되므로, 스크립트에 붙일 필요가 없다.
값을 저장할 때 쓰이는, 쉽게 말해 이름이 붙은 저장소이다.
변수는 메모리 영역에 연결되고, 값이 해당 메모리 영역에 저장된다. 그 후 변수 이름을 통해 값에 접근할 수 있다.
let
키워드를 사용해 변수를 생성한다.
// 변수 선언(생성)
let message;
// 변수 안에 데이터 할당(저장)
message = "hello";
// 변수명을 이용해 저장된 데이터에 접근
alert(message);
// 변수 선언과 할당을 한 번에 하기
let message = "hello";
변수는 한 번만 선언해야 한다. 같은 이름의 변수를 여러 번 선언하면 에러가 발생한다.
참고로, var
키워드는 let
과 거의 동일하게 동작한다. 하지만 호이스팅 문제 때문에 모던 자바스크립트에는 쓰지 않는 것을 권장한다.
자바스크립트와 함수형 언어 내용 추가 예정
변수명을 지을 때는 두 가지 제약 사항이 있다.
- 변수명에는
문자
와숫자
, 기호$
와_
만 들어 갈 수 있다.- 숫자는 변수명의 첫 글자에 올 수 없다.
그리고 제약 사항은 아니지만 개발자들 사이의 암묵적인 변수명 규칙들이 있다.
- 변수가 담고있는 것이 무엇인지 잘 설명해주는 이름이어야 한다.
ex)userName
- 여러 단어를 조합한 변수명은
camelCase
혹은snake_case
를 사용해 명명한다.let
,class
등 예약어는 변수명으로 사용할 수 없다.
저장한 데이터를 변경하지 않는 변수를 선언할 때에는, const
키워드를 사용한다. 상수를 변경하려고 하면 에러가 발생한다.
const myBirthDay = "1996.06.26";
기억하기 힘든 값을 대문자 상수로 선언하는 것은 널리 사용되는 관습이다. 이런 상수는 대개 대문자와 밑줄로 명명한다.
const COLOR_RED = "#F00";
자바스크립트는 동적 타입 언어로, 변수에 저장되는 값의 타입은 언제든 바꿀 수 있다.
숫자형은 정수 및 부동소수점 숫자를 나타낸다.
let n = 123;
1, -1, 0.1
뿐만 아니라 Infinity, -Infinity, NaN
같은 특수 숫자 값도 포함된다.
높은 정밀도로 작업을 해야 할 때 다루는 숫자형과 비슷한 타입이다.
정수 리터럴 끝에 n을 붙여서 만들 수 있다.
const bigInt = 1234567890123456789012345678901234567890n;
""
, ''
, ``
세 가지 따옴표로 문자열을 만들 수 있다.
백틱으로 감싼 문자열안에는 ${}
안에 변수나 표현식을 넣을 수 있다.
let str = "hello";
let str2 = 'Single quotes are ok too';
let phrase = `can embed another ${str}`;
true
나 false
값을 나타내는 자료형이다.
let isGreater = 4 > 1;
alert( isGreater ); // true
null
값만을 가지는 자료형이다.
존재하지 않는 값, 비어 있는 값, 알 수 없는 값 등을 나타내는 데 사용한다.
let age = null;
undefined
값만을 가지는 자료형이다.
값이 할당되지 않은 상태를 나타낸다. 변수는 선언했지만 값을 할당하지 않았을 때, 해당 변수에 undefined
가 자동으로 할당된다.
let age;
alert(age); // undefined
문자열이나 숫자, null 등 한 가지만 표현할 수 있는 원시(primitive) 자료형과 달리, 데이터 컬렉션이나 복잡한 개체를 표현할 수 있는 자료형이다.
객체의 고유한 식별자(unique identifier)를 만들 때 사용된다.
해당 변수의 자료형을 알고 싶을 때 사용하는 연산자이다.
typeof ...
혹은 typeof(...)
두 가지 방식으로 사용할 수 있다.
typeof undefined // undefined
typeof 0 // number
typeof("foo") // string
typeof null // object => null 타입은 객체형과 별도의 자료형이지만 하위 호환성을 위해 object 타입을 나타낸다.
typeof alert // function => 함수형으로 표현되지만 함수형은 따로 없고, 함수는 객체형에 속한다. 이 또한 하위 호환성 유지를 위해 남겨진 표현이다.
함수와 연산자에 전달되는 값이 적절한 자료형으로 자동 변환되는 일이나, 전달받은 값을 의도적으로 원하는 타입으로 변환해 주는 경우를 말한다.
// 문자형으로 변환
let value = true; // true
value = String(value) // "true"
// 숫자형으로 변환
let num = "123"; // "123"
num = Number(num) // 123
// 불린형으로 변환
// 0, 빈 문자열, null, undefined, NaN은 false.
// 그 외의 값은 true로 변환된다.
Boolea(1) // true
Boolean(0) // false
Boolean("hi") // true
Boolean("") // false
단항 연산자(unary operator)
: 피연산자를 하나만 받는 연산자
// 단항 연산자(unary operator)
let x = 1;
x = -x; // -1
// 단항 연산자 +는 피연산자가 숫자형이 아닌 경우 숫자형으로 변환한다.
+true // 1
+"" // 0
이항 연산자(binary operator)
: 두 개의 피연산자를 받는 연산자
// 숫자 덧셈
let x = 1, y = 3;
y - x // 2
// 문자열 붙이기
let s = "my" + "string"; // "mystring"
// 피연산자가 하나라도 문자열이면 결과는 문자열을 반환
2 + "1" // "21"
하나의 표현식에 둘 이상의 연산자가 있는 경우, 실행 순서는 연산자의 우선순위에 의해 결정된다.
우선순위가 같으면 왼쪽에서 오른쪽으로 연산이 수행된다.
괄호안의 연산은 모든 우선순위를 무시하고 가장 먼저 수행된다.
- 덧셈 연산자
+
- 뺄셈 연산자
-
- 곱셈 연산자
*
- 나눗셈 연산자
/
- 나머지 연산자
%
- 거듭제곱 연산자
**
무언가를 할당할 때 쓰이는 연산자이다.
// 값 할당
let x = 2 * 2 + 1; // 5
// 값을 반환
let a = 1;
let b = 2;
let c = 3 - (a = b + 1);
a // 3
c // 0
// 체이닝 => 할당 연산자는 우측부터 평가가 진행된다.
let a, b, c;
a = b = c = 2 + 2;
a // 4
b // 4
c // 4
변수에 연산자를 적용하고, 그 결과를 같은 변수에 저장할 때 쓰이는 연산자이다.
let n = 2;
n += 5; // 7
n *= 2; // 14
n -= 8; // 6
n /= 3; // 2
let counter = 2;
// 증가 연산자 : 변수를 1 증가시킨다.
counter++; // 3
// 감소 연산자 : 변수를 1 감소시킨다.
counter--; // 2
증감 연산자는 피연산자의 뒤에 올 때를 후위형(postfix)
, 피연산자 앞에 올 때는 전위형(prefix)
이라 부른다.
전위형은 증감 후의 새로운 값을 반환한다.
후위형은 증감 전의 기존 값을 반환하고, 증감한다.
피연산자를 32비트 정수로 변환하여 이진 연산을 수행한다.
- AND
&
- OR
|
- XOR
^
- NOT
~
- 왼쪽 시프트
<<
- 오른쪽 시프트
>>
- 부호 없는 오른쪽 시프트
>>>
여러 표현식을 코드 한 줄에서 평가할 수 있게 한다.
표현식 각각이 모두 평가되지만, 마지막 표현식의 평가 결과만 반환된다.
let a = (1 + 2, 3 + 4);
a; // 7 (3 + 4의 결과만 할당됨)
// 한 줄에서 세 개의 연산이 수행됨
for (a = 1, b = 3, c = a * b; a < 10; a++) {
...
}
- 부등호 연산자
>
,<
,>=
,<=
- 동등 연산자
==
- 부등 연산자
!=
- 일치 연산자
===
- 불일치 연산자
!==
비교 연산자는 기본적으로 불린형을 반환한다.
2 > 1 // true
2 == 1 // false
2 != 1 // true
비교 연산자를 사용하여 유니코드순으로 문자열을 비교할 수 있다.
"Z" > "A" // true
"A" > "a" // false
"Bee" < "Be" // false
일치와 불일치 연산자는 형 변환 없이 피연산자의 자료형까지 비교하여 검사하기 때문에 더 엄격하다.
0 == false // true
0 === false // false
- OR
||
- AND
&&
- NOT
!
OR 연산자
는 피연산자가 모두 false인 경우를 제외하고 연산 결과는 항상 true이다.
true || true; // true
false || true; // true
true || false; // true
false || false; // false
또, 첫 번째 truthy
를 찾는데에도 쓰인다.
result = value1 || value2 || value3;
이러한 기능은 다양한 용도로 활용할 수 있다.
let firstName = "";
let lastName = "";
let nickName = "바이올렛";
alert( firstName || lastName || nickName || "익명"); // 바이올렛
||
단락 평가는 연산자 왼쪽 조건이 falsy일 때만 명령어를 실행하고자 할 때 자주 쓰인다.true || alert("not printed");
false || alert("printed");
AND 연산자
는 두 피연산자가 모두 참일 때만 true를 반환한다. 또, 첫 번째 falsy를 찾는데에도 쓰인다.
result = value1 && value2 && value3;
1 && 0; // 0
1 && 5; // 5
null && 5; // null
0 && "아무거나 와도 상관없습니다."; // 0
NOT 연산자
는 피연산자를 불린형으로 변환하고, 변환된 불린값의 역을 반환한다.
result = !value;
!true; // false
!0; // true
NOT연산자가 피연산자를 불린형으로 변환하고 평가하는 특징을 활용해 , 두 개 연달아 사용하면 역의 역연산을 수행하므로 피연산자를 불린형으로 변환할 수 있다.
!!"non-empty string"; // true
!!null; // false
여러 피연산자 중 값이 확정되어있는 변수를 찾을 수 있다.
a ?? b // a가 null 혹은 undefined이면 b, 아니면 a 이다.
괄호 안에 들어가는 조건을 평가하고, 그 결과를 불린값으로 변환하여 true이면 코드 블록이 실행된다.
let year = prompt('ECMAScript-2015 명세는 몇 년도에 출판되었을까요?', '');
if (year == 2015) {
alert( '정답입니다!' )
};
0
, ""
, null
, undefined
, NaN
은 불린형으로 변환 시 모두 false가 된다. 이 들을 falsy 값이라 부른다.if문
에 else문
을 불일 수 있다.
else
뒤에 이어지는 코드 블록은 조건이 거짓일 때 실행된다.
let year = prompt('ECMAScript-2015 명세는 몇 년도에 출판되었을까요?', '');
if (year == 2015) {
alert( '정답입니다!' );
} else {
alert( '오답입니다!' ); // 2015 이외의 값을 입력한 경우
}
else문은 필수가 아닌 선택 사항이다.
여러 개의 조건을 처리할 때 쓰이는 구문이다.
let year = prompt('ECMAScript-2015 명세는 몇 년도에 출판되었을까요?', '');
if (year < 2015) {
alert( '숫자를 좀 더 올려보세요.' );
} else if (year > 2015) {
alert( '숫자를 좀 더 내려보세요.' );
} else {
alert( '정답입니다!' );
}
물음표 연산자 또는 삼항 연산자라고도 불리며, 조건문을 좀 더 간결하게 변형할 수 있다.
연산자 앞의 조건이 truthy이면 value1이, falsy라면 value2 가 반환된다.
let result = condition ? value1 : value2;
추가적으로 물음표 연산자는 조건에 따라 반환 값을 달리하려는 목적으로 만들어졌다. 조건을 여러 분기로 만들어 처리할 때는 if를 사용하는 것이 좋다.
복수의 if
조건문을 switch
조건문으로 바꿀 수 있다.
switch(x) {
case 'value1': // if (x === 'value1')
...
[break]
case 'value2':
...
[break]
default: // 옵션임
...
[break]
}
switch와 case 문의 조건에는 변수 뿐만아니라 어떤 표현식이든 올 수 있다.
condition이 truthy이면 body가 실행된다.
while (condition) {
// ...
}
본문이 먼저 한 번 실행되고, 조건이 truthy이면 본문이 계속 실행된다.
do {
// body
} while (condition);
- 가장 먼저 begin이 실행된다.
- condition이 truthy이면 body가 실행된다.
- 그 후 step을 실행한다.
- 2~3을 반복한다.
for (begin; condition; step) {
// body
}
begin과 condition, step은 생략될 수 있다.
let i = 0; // i를 선언하고 값도 할당하였습니다.
// begin 생략
for (; i < 3; i++) { // 'begin'이 필요하지 않기 때문에 생략하였습니다.
alert( i ); // 0, 1, 2
}
// begin과 step 생략
let i = 0;
for (; i < 3;) {
alert( i++ );
}
// 모두 생략 => 무한 반복문
for (;;) {
// 끊임 없이 본문이 실행됩니다.
}
대개는 반복문의 조건이 falsy가 되면 반복문이 종료된다.
이외에도 break
지시자를 사용해 원하는 때에 반복문을 빠져나올 수 있다.
let sum = 0;
while (true) {
let value = +prompt("숫자를 입력하세요.", '');
if (!value) break; // (*)
sum += value;
}
alert( '합계: ' + sum );
continue
지시자를 사용해 현재 실행 중인 반복을 멈추고, 조건이 truthy일 때 다음 반복을 강제로 실행시킬 수 있다.
for (let i = 0; i < 10; i++) {
// 조건이 참이라면 남아있는 본문은 실행되지 않습니다.
if (i % 2 == 0) continue;
alert(i); // 1, 3, 5, 7, 9가 차례대로 출력됨
}
중첩된 반복문에서 한 번에 빠져나오고 싶을 때가 있다.
이때는 반복문 앞에 라벨을 사용한다.
labelName: for (...) {
...
}
// 예시
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`(${i},${j})의 값`, '');
// 사용자가 아무것도 입력하지 않거나 Cancel 버튼을 누르면 두 반복문 모두를 빠져나옵니다.
if (!input) break outer; // (*)
// 입력받은 값을 가지고 무언가를 함
}
}
alert('완료!');
마찬가지로 continue 지시자에 사용하면 해당 레이블이 지정된 반복문의 다음 반복을 실행한다.
함수를 이용해 중복 작성 없이 유사한 동작을 하는 코드를 여러 번 호출할 수 있다.
함수는 function
키워드, 함수 이름, 매개변수들(생략가능), 함수 본문으로 구성된다.
정의된 함수 이름 옆에 괄호를 붙여 호출한다.
//함수 선언
function funcName(param1, param2, ... paramN) {
// body ...
}
// 함수 호출
funcName();
함수 내에서 선언한 변수로, 함수 안에서만 접근할 수 있다.
function showMessage() {
let message = "안녕하세요!"; // 지역 변수
alert( message );
}
showMessage(); // 안녕하세요!
alert( message ); // ReferenceError: message is not defined (message는 함수 내 지역 변수이기 때문에 에러가 발생합니다.)
함수 외부에서 선언된 변수로, 함수 내부에서 외부 변수에 접근하거나 수정할 수 있다.
let userName = 'John';
function showMessage() {
userName = "Bob"; // (1) 외부 변수를 수정함
let message = 'Hello, ' + userName;
alert(message);
}
alert( userName ); // 함수 호출 전이므로 John 이 출력됨
showMessage();
alert( userName ); // 함수에 의해 Bob 으로 값이 바뀜
다만 함수 내부에 외부 변수와 동일한 이름을 가진 지역변수가 선언되었다면, 해당 지역변수는 외부 변수를 가린다.
let userName = 'John';
function showMessage() {
let userName = "Bob"; // 같은 이름을 가진 지역 변수를 선언합니다.
let message = 'Hello, ' + userName; // Bob
alert(message);
}
// 함수는 내부 변수인 userName만 사용합니다,
showMessage();
alert( userName ); // 함수는 외부 변수에 접근하지 않습니다. 따라서 값이 변경되지 않고, John이 출력됩니다.
매개변수를 이용해 임의의 데이터를 함수 안에 전달할 수 있다.
function showMessage(from, text) { // 인자: from, text
alert(from + ': ' + text);
}
showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
showMessage('Ann', "What's up?"); // Ann: What's up? (**)
함수 호출 시에 각각의 매개변수 위치에 맞춰 인수(argument)를 전달하면 된다.
전달된 값은 매개변수에 복사되고, 매개변수는 지역변수처럼 사용된다.
매개변수가 있는 함수 호출시에 인수를 전달하지 않으면, 해당 매개변수는 undefined가 된다.
이 때, 매개변수에는 default 값을 설정해줄 수 있다.
function showMessage(from, text = "no text given") {
alert( from + ": " + text );
}
showMessage("Ann"); // Ann: no text given
함수 선언시에 return
지시자를 사용해 함수를 호출했을 때, 호출한 곳에 특정 값을 반환하게 할 수 있다.
function sum(a, b) {
return a + b;
}
let result = sum(1, 2);
alert( result ); // 3
return문은 함수 내 어디서든 사용할 수 있다.
return을 만나게 되면 함수 실행은 즉시 중단되고, 함수를 호출한 곳에 값을 반환한다.
return 문이 없거나 return만 있는 함수는 undefined를 반환한다.
- 함수는 동작 하나만 담당해야 한다.
- 이름만 보고도 어떤 동작을 하는지 알 수 있는 코드를 자기 설명적(self-describing) 코드라하고, 함수는 자기 설명적 코드여야한다. 즉, 함수 이름은 함수가 어떤 동작을 하는지 표현해야한다.
자바스크립트는 함수를 특별한 종류의 값으로 취급한다.
이러한 특성에 의해, 함수 선언문 이외에도 함수 표현식을 사용해 함수를 만들 수 있다.
변수를 선언하고 할당하는 것처럼, 함수를 생성하고 변수에 할당할 수 있다.
// 함수 선언문
function sayHi() {
alert( "Hello" );
}
// 함수 표현식
let sayHi = function() {
alert( "Hello" );
};
위의 어떤 방식으로 함수를 생성했든, 함수는 값으로 취급되고 변수에 할당할 수 있다. 따라서, 변수를 다른 변수에 할당하는 것처럼 함수도 다른 변수에 할당할 수 있다.
function sayHi() { // (1) 함수 생성
alert( "Hello" );
}
let func = sayHi; // (2) 함수 복사
func(); // Hello // (3) 복사한 함수를 실행(정상적으로 실행됩니다)!
sayHi(); // Hello // 본래 함수도 정상적으로 실행됩니다.
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
function showOk() {
alert( "동의하셨습니다." );
}
function showCancel() {
alert( "취소 버튼을 누르셨습니다." );
}
// 사용법: 함수 showOk와 showCancel가 ask 함수의 인수로 전달됨
ask("동의하십니까?", showOk, showCancel);
위 예시에서 함수 ask의 인수, showOk와 showCacncel을 콜백함수
또는 콜백
이라 한다.
쉽게 말해 어떤 함수를 함수의 인수로 전달하고, 인수로 전달한 그 함수를 나중에 호출(called back)하는 것이 콜백 함수의 개념이다.
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
ask(
"동의하십니까?",
function() { alert("동의하셨습니다."); },
function() { alert("취소 버튼을 누르셨습니다."); }
);
위의 예시에서 함수 ask의 인수에 이름이 없이 선언된 함수를 익명함수라 한다.
익명 함수는 변수에 할당된 것이 아니므로, 함수 ask 내부에서만 접근할 수 있다.
문법적 차이
자바스크립트 엔진이 언제 함수를 생성하는지 차이
// 함수 표현식
sayHi("John"); // error!
let sayHi = function(name) { // (*) 마술은 일어나지 않습니다.
alert( `Hello, ${name}` );
};
// 함수 선언문
sayHi("John"); // Hello, John
function sayHi(name) {
alert( `Hello, ${name}` );
}
let age = prompt("나이를 알려주세요.", 18);
// 조건에 따라 함수를 선언함
if (age < 18) {
function welcome() {
alert("안녕!");
}
} else {
function welcome() {
alert("안녕하세요!");
}
}
// 함수를 나중에 호출합니다.
welcome(); // Error: welcome is not defined
이를 해결하기 위해서는 함수 표현식을 쓰면 된다.
let age = prompt("나이를 알려주세요.", 18);
let welcome;
if (age < 18) {
welcome = function() {
alert("안녕!");
};
} else {
welcome = function() {
alert("안녕하세요!");
};
}
welcome(); // 제대로 동작합니다.
함수 표현식보다 단순하고 간결한 문법으로 함수를 만들 수 있는 방법이다.
let func = (arg1, arg2, ...argN) => expression
인수가 없다면 괄호를 비워 놓으면 된다.
let func = () => expression
함수 본문이 여러 줄이라면 중괄호 안에 함수 본문을 넣고, return 지시자를 사용해 명시적으로 결과값을 반환해주어야 한다.
let sum = (a, b) => { // 중괄호는 본문 여러 줄로 구성되어 있음을 알려줍니다.
let result = a + b;
return result; // 중괄호를 사용했다면, return 지시자로 결괏값을 반환해주어야 합니다.
};
alert( sum(1, 2) ); // 3