npm을 설치하고 parcel을 설치한다.
npm init -y
npm i -D parcel
json 파일은 아래와 같이 한다.
{
"name": "js",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "parcel ./index.html",
"build": "parcel build./index.html"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"parcel": "^2.9.3"
}
}
scripts 필드 내에 있는 dev와 build는 npm (또는 Yarn) 스크립트이다. 이 스크립트는 특정 명령어를 단순화하여 개발자가 패키지 관리 도구를 통해 쉽게 실행할 수 있도록 한다. 주어진 package.json에서는 parcel이라는 빌드 도구를 사용하고 있다.
dev
parcel ./index.html
build
parcel build ./index.html
index.html은 아래와 같이 한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script type="module" defer src="./main.js"></script>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
여기서 주요 속성들에 대한 설명이다.
defer
defer 속성은 HTML 파싱이 완료된 후에 스크립트를 실행하도록 한다. 기본적으로 스크립트는 다운로드와 실행 도중 HTML 파싱을 차단한다. defer를 사용하면 스크립트 다운로드는 병렬적으로 진행되지만, 실행은 HTML 파싱이 완료된 후에 순서대로 실행된다.
type="module"이 지정된 스크립트는 기본적으로 defer의 동작을 한다. 그러나 명시적으로 defer 속성을 추가하는 것은 좋은 습관이며, 의도를 명확하게 표현한다.
src="./main.js"
src 속성은 외부 자바스크립트 파일의 경로를 지정한다. 여기서는 현재 디렉토리에 위치한 main.js 파일을 불러오도록 지정되어 있다.
이렇게 구성된 이유는 웹 페이지의 렌더링 성능과 사용자 경험을 최적화하기 위함다. defer 속성은 브라우저가 페이지의 내용을 먼저 표시하고, 자바스크립트는 나중에 실행되게 함으로써 사용자가 페이지를 빠르게 볼 수 있게 해준다.
이렇게 구성된 이유는 웹 페이지의 렌더링 성능과 사용자 경험을 최적화하기 위함이다. defer
속성은 브라우저가 페이지의 내용을 먼저 표시하고, 자바스크립트는 나중에 실행되게 함으로써 사용자가 페이지를 빠르게 볼 수 있게 해준다.
const string1 = "Hello";
const string2 = "World";
const string3 = `Hello`;
string1, string2처럼 선언은 개인 취향이지만, string3는 다음과 같이 활용이 가능하다.
const string3 = `Hello ${string1}?!`;
// 데이터를 가져와서 출력.
// 이것 처럼 빽틱 기호를 사용하여 문자를 만드는 것을 템플릿 리터럴이라 한다.
// '' ""과 같이 작은 따옴표와 큰 따옴표를 사용하여 만드는 것은 리터럴이라 한다.
리터럴은 데이터의 표현입니다. 코드상에서 값을 직접 나타내는 표현식을 의미한다.
JavaScript에서는 여러 유형의 리터럴이 있다.
[1, 2, 3]
ES6 (ECMAScript 2015)에서 도입된 새로운 문자열 표현 방식이다. 백틱( ) 문자로 둘러싸인 문자열을 사용하여 표현된다. 템플릿 리터럴은 문자열 내에 변수나 표현식을 쉽게 포함시킬 수 있게 해주며, 멀티라인 문자열을 간단하게 만들 수 있다.
let name = "John";
console.log(`Hello, ${name}!`); // 출력: "Hello, John!"
console.log(`This is a
multi-line string!`);
템플릿 리터럴은 문자열 연결과 표현식 포함을 더욱 간편하게 만들어 준다. 전통적인 문자열 연결 방식과 비교하면, 코드의 가독성이 향상되며, 실수를 줄일 수 있다.
const number = -123.1234
const p = .14
console.log(number + undefined) // NaN
console.log(p) // 0.14
console.log(typeof (number + undefined)) // number
console.log(typeof p) // number
JavaScript에서 NaN은 "Not a Number"의 약자로, 숫자가 아닌 값을 나타낸다. NaN은 여러 가지 수학 연산의 결과로 나타날 수 있으며, 특별한 속성을 가진 부동 소수점 값다.
parseInt("Hello") // NaN
0 / 0 // NaN
Math.sqrt(-1) // NaN
NaN
값이 연살될 때NaN + 5 // NaN
NaN 처리 방법
마지막으로, NaN은 독특한 특성 때문에 코드에서 버그를 일으킬 수 있다. 따라서 코드에서 숫자 값을 처리할 때는 항상 주의 깊게 접근해야 한다.
const a = 0.1
const b = 0.2
console.log(a + b) // 0.30000000000000004가 출력된다.
이 현상은 JavaScript에서 숫자를 표현하는 방식인 부동 소수점 산술에서 일반적으로 발생하는 문제이다. JavaScript의 모든 숫자는 IEEE 754 부동 소수점 형식을 사용하여 배정밀도(double-precision)로 표현된다.
이 현상의 원인을 좀 더 구체적으로 설명하면 다음과 같다.
이진 표현: 컴퓨터는 숫자를 이진 형식(0과 1)으로 저장한다. 0.1 및 0.2와 같은 10진수는 이진 형식으로 정확하게 표현될 수 없다. 따라서 컴퓨터는 이 숫자들을 가능한 한 가장 가까운 값으로 근사화한다.
누적 오차: 위에서 언급한 이진 근사화로 인해 실제 연산 시에 오차가 누적될 수 있다. 0.1 + 0.2의 경우, 두 숫자 각각의 작은 오차가 더해져서 최종 결과에도 그 오차가 반영된다.
이 문제는 JavaScript에만 국한된 것이 아니라 많은 프로그래밍 언어와 시스템에서 발생하는 문제이다.
해결 방법은 다양한데, 일반적으로 정밀한 소수점 연산이 필요한 경우에는 다음과 같은 방법을 고려할 수 있다.
const a = 0.1
const b = 0.2
console.log((a + b).toFixed(1)) // 0.3
console.log(typeof (a + b).toFixed(1)) // string
const a = true
const b = false
let isTrue = Boolean(1); // true
let isFalse = Boolean(0); // false
이 타입은 주로 조건문, 논리 연산자, 비교 연산자의 결과로 사용되며, 코드의 조건부 실행에 주로 활용된다.
JavaScript에서는 false가 아닌 몇몇 특정 값들이 조건문에서 false처럼 동작한다. 이러한 값을 "falsy" 값이라고 한다
Boolean 값을 사용할 때 자주 사용되는 논리 연산자에는 && (AND)
, || (OR)
, 그리고 ! (NOT)
이 있다.
JavaScript에서 null은 특별한 값으로, 어떤 변수가 의도적으로 값이 없음을 나타내고 싶을 때 사용된다. 다음은 null에 대한 몇 가지 중요한 특성과 특징이다.
console.log(typeof null); // "object"
console.log(null == undefined); // true
console.log(null === undefined); // false
let data = null;
let person = {
name: 'John',
age: null // 나이 정보가 없음을 나타냄
};
요약하면, null은 JavaScript에서 의도적으로 값을 부재하게 만들고 싶을 때 사용하는 특별한 원시 값이다.
JavaScript에서 undefined는 변수가 선언되었으나 아직 값이 할당되지 않았을 때의 기본값이다. undefined에 대한 몇 가지 중요한 특성과 특징은 다음과 같다.
let myVar;
console.log(myVar); // undefined
console.log(typeof undefined); // "undefined"
function noReturnValue() {}
console.log(noReturnValue()); // undefined
let obj = {};
console.log(obj.nonexistentProperty); // undefined
console.log(undefined == null); // true
console.log(undefined === null); // false
요약하면, undefined는 값이 아직 할당되지 않은 변수나 존재하지 않는 객체의 속성, 반환 값이 없는 함수의 반환 값 등에서 발생하는 특별한 원시 값이다.
const user = {
name: 'HEROPY',
age: 85
}
console.log(user.name) // HEROPY
console.log(user.age) // 85
console.log(user.email) // undefined
JavaScript의 Array
는 순서가 있는 값들의 컬렉션이다. 배열은 객체의 일종이며, 인덱스로 값에 접근할 수 있다. 배열은 다양한 자료형의 요소를 포함할 수 있으며, 배열의 크기는 동적으로 변경될 수 있다.
[]
를 사용하는 것이다.let fruits = ['apple', 'banana', 'cherry'];
new
키워드와 함께 Array 생성자를 사용하여 배열을 만들 수도 있다. 조금 불편하므로, 배열 리터럴로 하는 것이 좋다.let fruits = new Array('apple', 'banana', 'cherry');
console.log(fruits.length); // 3
fruits.push('date'); // 'apple', 'banana', 'cherry', 'date'
fruits.pop(); // 'apple', 'banana', 'cherry'
fruits.unshift('mango'); // 'mango', 'apple', 'banana', 'cherry'
fruits.shift(); // 'apple', 'banana', 'cherry'
fruits.splice(1, 0, 'kiwi'); // 1번 인덱스에 'kiwi' 추가
let months = ["January", "February", "Monday", "Tuesday"];
let days = months.splice(2, 2, "March", "April"); // 요소 두 개를 제외하고, 다른 요소를 추가
console.log(days); // ["Monday", "Tuesday"]
console.log(months); // ["January", "February", "March", "April"]
배열은 자바스크립트에서 매우 중요한 데이터 구조로, 많은 작업과 연산을 간단하게 만들어 준다. 배열과 관련된 메서드는 자주 사용되므로, 다양한 메서드들을 익숙하게 사용하는 것이 좋다.
JavaScript에서 Object는 키와 값의 쌍으로 구성된 데이터 구조를 나타낸다. 객체는 데이터와 함께 그 데이터를 처리하는 메서드를 캡슐화하는 데 사용된다. Object는 자바스크립트에서 참조 타입이다, 즉 변수에 객체가 저장될 때 실제 객체의 데이터가 아닌 객체에 대한 참조가 저장된다.
키와 값의 쌍: 객체는 키(key)와 값(value)의 쌍으로 구성된다. 키는 문자열 또는 심볼이어야 하며, 값은 어떠한 타입이든 될 수 있다.
동적 속성: 객체의 속성은 동적으로 추가하거나 제거할 수 있다.
메서드: 객체는 함수를 값으로 포함할 수 있다. 이러한 함수를 객체의 메서드라고 한다.
const person = {
firstName: 'John',
lastName: 'Doe',
age: 30,
greet: function() {
console.log('Hello, ' + this.firstName);
}
};
const person = new Object();
person.firstName = 'John';
person.lastName = 'Doe';
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const john = new Person('John', 'Doe');
console.log(person.firstName); // John
const keyName = 'lastName';
console.log(person[keyName]); // Doe
console.log(person[lastName]); // Doe
person.greet(); // Hello, John
const userA = {
name: 'HEROPY',
age: 85
}
const userB = {
name: 'Neo',
age: 22,
parent: userA
}
const users = [userA, userB]
console.log(userB.parent.name) // HEROPY
console.log(users[0]['name']) // Heropy
모든 자바스크립트 객체는 프로토타입이라는 다른 객체에 대한 내부 링크를 갖는다. 프로토타입은 객체의 상속을 가능하게 하며, 프로토타입 체인을 통해 객체는 그것의 프로토타입에서 메서드와 속성을 상속받을 수 있다.
객체는 자바스크립트에서 중요한 데이터 구조로서, 복잡한 애플리케이션에서 데이터 모델링 및 로직 캡슐화에 매우 유용하다.
avaScript에서 Function도 객체로 취급된다. 함수는 특별한 종류의 객체이며, 실행 가능한 코드를 포함하고 있다. 함수는 일급 객체(first-class object)의 특성을 가지기 때문에 다른 값들처럼 변수에 할당하거나, 객체의 프로퍼티로 사용하거나, 배열의 요소로 저장하거나, 다른 함수의 인수로 전달하거나 리턴값으로 사용할 수 있다.
호출 가능: 함수는 ()를 사용하여 호출할 수 있다. 호출 시 함수 내부의 코드가 실행된다.
변수 환경: 함수는 자신만의 변수 환경을 갖는다. 이를 통해 함수는 클로저를 형성하며 외부 스코프의 변수에 접근할 수 있다.
매개변수: 함수는 인수를 받아들일 수 있다. 이 인수들은 함수 내부에서 사용될 수 있다.
리턴 값: 함수는 값을 반환할 수 있다. 반환하지 않으면, 기본적으로 undefined를 반환한다.
function greet(name) {
console.log('Hello, ' + name);
}
const greet = function(name) {
console.log('Hello, ' + name);
};
const greet = name => console.log('Hello, ' + name);
function hello() {
console.log('Hello!')
}
hello() // Call
위처럼 함수를 불러오는 것을 함수를 콜한다고 한다.
생성자 함수: new 키워드와 함께 호출될 수 있으며, 객체를 생성하고 초기화한다.
익명 함수: 이름이 없는 함수로, 주로 콜백 함수나 한 번만 사용될 함수로 사용된다.
즉시 실행 함수(IIFE, Immediately Invoked Function Expression): 정의와 동시에 실행되는 함수이다.
Function도 객체이기 때문에 프로토타입 속성을 갖는다. 이는 함수의 메서드 및 속성을 확장할 때 유용하다.
함수 객체는 호출(call), 바인드(bind), 적용(apply)과 같은 여러 내장 메서드를 갖는다. 이 메서드들은 함수의 컨텍스트를 조작하거나 인수를 전달하는 방식에 따라 함수를 호출할 때 유용하다.
함수는 JavaScript에서 매우 중요한 구성 요소로, 그 자체로 실행 가능한 코드 블록을 캡슐화한다. 이는 코드의 재사용성과 구조화를 촉진하며, 고차 함수와 클로저와 같은 고급 프로그래밍 패턴의 구현을 가능하게 한다.
자바스크립트에서 형 변환(Type Conversion) 또는 타입 캐스팅(Type Casting)은 한 데이터 타입을 다른 데이터 타입으로 변환하는 과정이다. 이러한 변환은 때때로 프로그래머에 의해 명시적으로 수행되기도 하며, 때로는 자바스크립트 엔진에 의해 암시적으로 수행된다.
let result = '5' + 7; // '57' (문자열로 변환)
let number = Number('5'); // 5 (숫자로 변환)
let val = String(123); // "123"
let val2 = (123).toString(); // "123"
let num = Number('123'); // 123
let num2 = +'123'; // 123
let bool = Boolean(1); // true
let bool2 = !!1; // true
'Hello' + 5 // "Hello5"
(-, *, /, % 등)
를 사용할 때이다.'5' - 3 // 2
'5' * '2' // 10
==
(동등 연산자)를 사용하여 비교할 때 암시적 변환이 발생할 수 있다. 그러나 ===
(일치 연산자)를 사용하면 형 변환이 발생하지 않는다.'5' == 5 // true
'5' === 5 // false
형 변환은 자바스크립트에서 자주 발생하는 현상이며, 특히 암시적 변환은 예기치 않은 결과를 초래할 수 있기 때문에 주의해야 한다. 프로그래밍을 할 때 항상 데이터 타입을 고려하고 필요에 따라 명시적으로 형 변환을 수행하는 것이 좋다.
자바스크립트에서는 특정 값들이 불리언(Boolean) 컨텍스트에서 평가될 때 그 값들이 true 또는 false로 간주되는 것을 의미하는 "Truthy"와 "Falsy"라는 용어를 사용한다.
Falsy 값은 불리언 컨텍스트에서 false로 평가되는 값을 의미한다. 자바스크립트에서 다음은 Falsy 값의 목록이다
Falsy 값들을 제외한 모든 값은 불리언 컨텍스트에서 true로 간주되며, 이러한 값들을 Truthy라고 합니다. 몇 가지 예는 다음과 같다.
if ([]) {
console.log('This will be printed because an empty array is truthy.');
}
이러한 Truthy와 Falsy 개념은 조건문, 삼항 연산자, 논리 연산자(&&, ||, ! 등) 등 다양한 곳에서 유용하게 사용된다.
JavaScript에서 데이터의 타입을 확인하는 방법은 주로 typeof 연산자를 사용한다. 또한, 더 복잡한 자료구조나 인스턴스의 타입을 확인하고자 할 때는 instanceof 연산자를 사용할 수 있다.
typeof는 주어진 값의 타입을 문자열로 반환한다.
console.log(typeof 123); // "number"
console.log(typeof 'hello'); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof function(){}); // "function"
console.log(typeof {}); // "object"
console.log(typeof []); // "object"
console.log(typeof null); // "object" (이것은 JavaScript의 오래된 버그로 간주된다)
instanceof 연산자는 주어진 객체가 특정 생성자의 인스턴스인지 확인하는데 사용된다.
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log('hello' instanceof String); // false (문자열 원시값은 String 객체의 인스턴스가 아니다)
console.log(new String('hello') instanceof String); // true
typeof나 instanceof로 충분한 정보를 얻지 못할 때, Object.prototype.toString.call() 메서드를 사용하여 정확한 타입을 얻을 수 있다.
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
이 방법은 null, undefined, array 등의 정확한 타입을 판별하는 데 유용하다.
각 방법은 특정 상황에 따라 적합하므로 필요에 따라 적절한 방법을 선택하여 사용하면 된다.
객체가 생성될 때, 그 객체의 원형(프로토타입)에는 constructor 프로퍼티가 있어 해당 객체의 생성자 함수를 가리킨다. 기본 내장 객체들, 예를 들어 Array, Object, String, Number 등도 이 규칙을 따른다.
const arr = [];
console.log(arr.constructor === Array); // true
const obj = {};
console.log(obj.constructor === Object); // true
const str = "hello";
console.log(str.constructor === String); // true
const num = 123;
console.log(num.constructor === Number); // true
그러나 constructor를 사용하여 타입을 검사하는 것은 몇 가지 주의점이 있다.
따라서, 대부분의 경우 typeof
연산자나 instanceof
연산자, 그리고 Array.isArray()
와 같은 몇몇 내장 메서드들이 타입 검사에 더 일반적으로 사용된다.