Developer's Development

3.4.3 [화면 구현] JavaScript: Core 본문

AI 활용 애플리케이션 개발/화면 구현

3.4.3 [화면 구현] JavaScript: Core

mylee 2025. 10. 3. 21:47
JavaScript 개요

 

JavaScript는 웹 페이지의 보조적인 기능을 수행하기 위해 브라우저에서 동작하는 경량 프로그래밍 언어로서 1996년 넷스케이프 커뮤니케이션즈에서 처음 개발되었다.

ECMAScript 5(ES5)는 HTML5와 함께 출현한 표준 사양이며,

2015년에 공개된 ECMAScirpt 6(ES6)는 let/const 키워드, 화살표 함수, 클래스 등과 같이 범용 프로그램이 언어로서 갖춰야 할 기능들을 대거 도입하는 큰 변화가 있었다.

 

  • JavaScript의 성장

Ajax (1999년)

jQuery (2006년)

V8 자바스크립트 엔진 (2008년)

Node.js (2009년)

SPA 프레임워크

 

  • JavaScript의 특징

웹 브라우저에서 동작하는 유일한 프로그래밍 언어이다.

개발자가 별도의 컴파일 작업을 수행하지 않는 인터프리터 언어이다.

❗인터프리터 언어
= 코드가 실행되는 단계인 런타임에 문 단위로 한 줄씩 중간 코드인 바이트 코드로 변환한 후 실행하는 언어

 

클래스 기반 객체지향 언어보다 효율적이면서 강력한 프로토타입 기반의 객체지향 언어이다.

 

 

개발 환경 구축

 

  • Node.js 다운로드

https://nodejs.org/ko/

 

Node.js — Run JavaScript Everywhere

Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.

nodejs.org

 

  • cmd

node -v, npm -v로 버전을 출력하여 정상적으로 설치되었는지 확인

 

  • vscode

test.js 파일 생성

console.log('hello javascript')

 

실행시킬 땐 Ctrl + ` (terminal)에서 node test.js로 실행

Extension > 'Code Runner' install

→ Ctrl + Alt + N으로 파일 실행 가능

 

 

variable (변수)

 

  • data type (데이터 타입)

number (숫자 타입), string (문자열 타입 '', "", ``), boolean (논리 타입), undefined & null, symbol & object

 

  • dynamically typed language (동적 타입 언어)

1. 정적 타입(static/strong type) 언어

2. 동적 타입(dynamic/weak type) 언어

 

  • implicit-coercion (암묵적 타입 변환)

convert to string (문자열 타입으로 변환)

convert to number (숫자 타입으로 변환)

convert to boolean (논리 타입으로 변환)

 

  • explicit-coercion (명시적 타입 변환)

convert to string (문자열 타입으로 변환): toString()

convert to number (숫자 타입으로 변환): parseInt(), parseFloat()

convert to boolean (논리 타입으로 변환): !!

// JS 한 줄 주석

/* 
    JS 여러줄 주석
*/


/* 문자열 타입 */
var string;
string = 'JavaScript "큰 따옴표" 자체로 출력';
string = "JavaScript '작은 따옴표' 자체로 출력";
// string = `JavaScript`;

console.log(string);

// var str = '안녕하세요.
// 맹구입니다.';
var str = '안녕하세요. \n맹구입니다.';
console.log(str);

/* 템플릿 리터럴 */
/* 
    ES6부터 도입된 벡틱(``)을 사용한 표현식으로
    멀티라인 문자열, 표현식 삽입 등
    편리한 문자열 처리 기능을 제공하는 문자열 표기법
*/

var multiline = `안녕하세요.
진짜 맹구입니다.`;
console.log(multiline);

var teacher = '맹구';
var student = 17;

// 맹구 선생님과 함께 하는 17기의 SKN 일대기!
console.log(teacher + " 선생님과 함께 하는 " + student + "기의 SKN 일대기!");
console.log(`${teacher} 선생님과 함께 하는 ${student}기의 SKN 일대기!`);

/* undefined */
var item;
console.log(item);  // undefined

/* null */
var hello = 'hello';
hello = null;
console.log(hello); // null

 

 

operator (연산자)

 

  • comparison operator (비교 연산자)
/* 비교 연산자 */

/* 
    - 동등 비교(==, !=): 값이 같은지 비교 (암묵적 형변환 비교)
    - 일치 비교(===, !==): 타입과 값이 모두 같은지 비교
*/

console.log("===== 숫자 1과 문자 '1', true 비교 =====");
console.log(`1 == '1' : ${1 == '1'}`);     // T
console.log(`1 == true : ${1 == true}`);   // T
console.log(`1 === '1' : ${1 === '1'}`);   // F
console.log(`1 === true : ${1 === true}`); // F

console.log("===== 숫자 0과 빈 문자열 '', 문자 '0', false 비교 =====");
console.log(`0 == '0' : ${0 == '0'}`);       // T
console.log(`0 == '' : ${0 == ''}`);         // T
console.log(`0 == false : ${0 == false}`);   // T
console.log(`0 === '0' : ${0 === '0'}`);     // F
console.log(`0 === '' : ${0 === ''}`);       // F
console.log(`0 === false : ${0 === false}`); // F

console.log("===== NaN과 NaN 비교 =====")
// NaN은 자신과 일치하지 않는 유일한 값
console.log(`NaN == NaN : ${NaN == NaN}`);               // F
console.log(`NaN === NaN : ${NaN === NaN}`);             // F
console.log(`Number.isNaN(NaN) : ${Number.isNaN(NaN)}`); // T

 

  • logical operator (논리 연산자)

👉🏻 short circuit evaluation (단축 평가)

표현식을 평가하는 도중 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 말한다.

/* 
    논리 연산자
    - AND : &&
    - OR  : ||
    - NOT : !
*/

// OR, AND 연산자 표현식의 결과는 boolean이 아닐 수도 있음

// 'apple'이 이미 Truthy한 값이므로 true로 평가되고,
// 논리 연산의 결과를 결정한 피연산자 'apple'을 그대로 반환
console.log(false || 'apple');  // apple
console.log('apple' || false);  // apple
console.log('apple' || 'banana');  // apple

console.log(false && 'apple');      // false
console.log('apple' && false);      // false
console.log('apple' && 'banana');   // banana

// 단축 평가를 사용해 if문 대체 가능
var num = 1;
if (num % 2 == 0) {
    console.log('짝수');
} else {
    console.log('홀수');
}

num % 2 == 0 && console.log('짝수');
num % 2 == 0 || console.log('홀수');

// 객체가 가리키기를 기대하는 변수가 null 또는 undefined가 아닌지 확인하고
// 프로퍼티를 참조할 때 단축 평가를 유용하게 활용 가능
var obj = null;
// console.log(obj.value);
obj && console.log(obj.value);

 

  • ES11 operator

👉🏻 optional chaining operator (옵셔널 체이닝 연산자)

/* 
    ?.
    - ES11(EcmaScript2020)에서 도입된 연산자
    - 좌항의 피연산자가 null 또는 undefined인 경우 undefined 반환
    - 그렇지 않으면 우항의 프로퍼티 참조 이어감
*/

var obj = null;

// var val = obj.value;            // TypeError
// var val = obj && obj.value;     // 단축 평가로 해결
var val = obj?.value;

console.log(val);   // undefined


// 논리연산자 &&를 이용한 단축 평가에서는 
// 빈 문자열 같은 Falsy한 값을 false 취급해서 문제 발생 => 옵셔널 체이닝이 해결
var str = '';

var len = str.length;   // 단축 평가 이용: len == ''
len = str?.length;      // 옵셔널 체이닝 이용: len == 0

console.log(len);   // 0

 

👉🏻 nullish coalescing operator (null 병합 연산자)

/* 
    ??
    - ES11(ECMAScript2020)에서 도입된 연산자
    - 좌항의 피연산자가 null 또는 undefined인 경우 우항의 피연산자 변환
    - 그렇지 않으면 좌항의 피연산자 반환
    - 변수 기본값 설정 시 유용
*/

var test = null ?? 'default';
console.log(test);      // default


// 단축 평가 ||를 사용해 기본값을 설정하는 방식은
// 아래와 같은 빈 문자열을 false로 취급해 기본 값이 할당되는 문제 발생
// => nullish coalescing 방식이 해결
var str = null;
var value = str || 'default';
value = str ?? 'default';
console.log(value);     // default

 

 

object literal (리터럴 객체)

 

  • object (객체)
// 객체 리터럴을 통한 객체 생성 (중괄호 내 0개 이상의 프로퍼티 정의)

var teacher = {
    // 프로퍼티: 객체의 상태를 나타내는 값 (data)
    name: "맹구",
    age: 900,

    // 메서드: 프로퍼티(상태 데이터)를 참조하고 조작할 수 있는 동작
    getInfo: function() {
        return `${this.name}은(는) ${this.age}세의 선생님입니다.`
    }
}

console.log(typeof teacher);    // object
console.log(teacher);           // { name: '맹구', age: 900, getInfo: [Function: getInfo] }

var student = {};
console.log(typeof student);    // object
console.log(student);           // {}

 

  • property (속성)
// 객체 == 프로퍼티의 집합
// 프로퍼티 == 키(key): 값(value)

var idol = {
    group: 'BTS',
    '@ s o n g @': 'Fire',   // 특수문자도 key값으로 사용 가능 (단, ''로 감싸야 함)
    '': '',                  // 빈 문자열도 key값으로 사용 가능
    0: 1,                    // 숫자 key는 내부적으로 문자열로 변환됨
    var: 'var',              // 예약어도 key로 사용 가능
    group: '방탄소년단'        // 이미 존재하는 key를 중복 선언하면 나중에 선언한 프로퍼티로 덮어씀
}

var key = 'count';
idol[key] = 7;

console.log(typeof idol);   // object
console.log(idol);

// 마침표 표기법
console.log(idol.group);

// 대괄호 표기법 (프로퍼티 키는 반드시 따옴표로 감싼 문자열 사용)
console.log(idol['0']);     // 1
console.log(idol['group']); // '방탄소년단'


/* 프로퍼티 값 단축 구문 (ES6)*/
var productName = 'iPhone17';
var price = 1000000;

var product = {
    productName: productName,
    price: price
};

console.log(product);

// 프로퍼티 값으로 변수를 사용하는 경우
// 변수 이름과 프로퍼티 key가 동일한 이름일 때 프로퍼티 key 생략 가능
var new_product = {
    productName, price
};

console.log(new_product);   // { productName: 'iPhone17', price: 1000000 }


/* 계산된 프로퍼티 key (ES5) */
// 계산된 프로퍼티 key로 프로퍼티를 동적 생성 가능 (대괄호 표기법 사용)
var prefix = 'B';
var index = 1;

var boardObj = {};

boardObj[prefix + '-' + index++] = '게시글 1';
boardObj[prefix + '-' + index++] = '게시글 2';
boardObj[prefix + '-' + index++] = '게시글 3';

console.log(boardObj);  // { 'B-1': '게시글 1', 'B-2': '게시글 2', 'B-3': '게시글 3' }

// ES6
// 객체 리터럴 내부에서도 계산된 프로퍼티 key로 key 동적 생성 가능
var new_boardObj = {
    [`${prefix}-${index++}`] : '게시글 4',
    [`${prefix}-${index++}`] : '게시글 5',
    [`${prefix}-${index++}`] : '게시글 6',
}

console.log(new_boardObj);

 

  • method
// 메소드를 정의하려면 프로퍼티 값으로 함수를 할당 (ES5)
var puppy = {
    name: '뽀삐',
    eat: function(food) {
        console.log(`${this.name}은(는) ${food}를 맛있게 먹어요~!`);
    }
};

puppy.eat('소고기');    // 뽀삐은(는) 소고기를 맛있게 먹어요~!


/* 프로퍼티 값 단축 구문 (ES6)*/
var squirrel = {
    name: '람쥐썬더',
    action(skill) {
        console.log(`${this.name}이(가) ${skill}을 보여줍니다!`);
    }
};

squirrel.action('현란한 강의'); // 람쥐썬더이(가) 현란한 강의을 보여줍니다!

 

  • property change, add, remove (프로퍼티 값 변경, 추가, 삭제)
/* 
    - 존재하지 않는 프로퍼티에 접근해서 값을 대입 > 추가
    - 존재하는 프로퍼티에 접근해서 값을 대입 > 수정
    - delete 키워드를 이용해 프로퍼티에 접근 > 삭제
      (이때 존재하지 않는 프로퍼티를 넣어도 에러 없이 무시됨)
*/ 


var kitty = {
    name: '나비'
};

// 추가
kitty['zipsa'] = '맹구';
kitty.like = '츄르';
console.log(kitty);     // { name: '나비', zipsa: '맹구', like: '츄르' }

// 수정
kitty.name = '보리';
kitty['like'] = '맹구';
console.log(kitty);     // { name: '보리', zipsa: '맹구', like: '맹구' }

// 삭제
delete kitty.like;
delete kitty['zipsa'];
delete kitty.age;
console.log(kitty);     // { name: '보리' }

 

  • 연산자 (in)
var actor = {
    name: '차은우',
    age: 30,
    gender: 'M',
    company: undefined
}

console.log(actor.name === undefined);      // false
console.log(actor.drama === undefined);     // true
console.log(actor.company === undefined);   // true

// 프로퍼티 존재 여부 확인 가능
console.log('name' in actor);       // true
console.log('drama' in actor);      // false
console.log('company' in actor);    // true


// for-in 반복문
// 객체의 key를 순회하며 받음
for (var key in actor) {
    console.log(key);
    console.log(actor[key]);
}

 

 

function (함수)

 

  • 함수 선언 (function declaration)
function hello(name) {
    return `${name}님 안녕하세요 :)`;
}

// JS엔진은 생성된 함수를 호출하기 위해
// 함수 이름과 동일한 식별자를 암묵적으로 생성하고 거기에 함수 객체를 할당함
/* 
    var hello = function hello(name) {
        return `${name}님 안녕하세요 :)`;
    }; 
*/

/* 함수 표현식 (function expression) */
/* 
    - var 변수 = 함수 (단, 함수명 생략 가능)
    - 변수명에 해당하는 식별자로 함수 호출해야 함
    - 함수명을 생략하지 않아도 문제는 없지만 함수 호출은 식별자(변수)로만 가능함
*/
var hi = function new_hello(name) {
    return `${name}야 안녕!!!`;
};
hi = function new_hello(name) {
    return `${name}야 안녕!!!`;
};

console.log(hello('맹구'));             // 맹구님 안녕하세요 :)
console.log(hi('맹구'));                // 맹구야 안녕!!!
// console.log(new_hello('맹구'));      // ReferenceError

 

  • 함수 호이스팅 (function hoisting)
/* 
    함수 선언문은 이전 JS 엔진에 의해 먼저 실행된다.
    따라서 함수 선언문 이전에 해당 함수를 참조하거나 호출할 수 있다.
    이를 JS 고유의 특징, 함수 호이스팅이라고 한다.

    단, 변수 할당문의 값은 런타임에 평가되므로
    함수 표현식으로 정의한 함수는 반드시 함수 표현식 이후에 참조 또는 호출해야 한다.
*/

console.log(returnHello);           // [Function: returnHello] :함수 참조
console.log(returnHello('맹구'));    // 맹구 HELLO ~! :함수 호출

console.log(returnHi);              // undefined
console.log(returnHi('maenggu'));   // TypeError: returnHi is not a function

function returnHello(name) {
    return `${name} HELLO ~!`;
}

var returnHi = function(name) {
    return `Yo! ${name} HI!!!`;
}

 

  • 매개변수(Parameter)와 인자(Arguments)
// function lunch(menu) {
function lunch(...menu) {   // 가변인자
    console.log('lunch가 몰래 갖고 있는 args:', arguments);
    // arguments: 함수 내부적으로 가지고 있는 인자값을 다 저장한 객체
    console.log('lunch가 받은 menu:', menu);
    return `오늘 점심은 ${menu}를 먹었어. 아주 맛있더라고~`;
}

console.log(lunch('김치찌개'));
console.log(lunch());       // 인자가 부족해서 할당되지 않으면 undefined
// console.log(menu);       // 파라미터 값은 함수 내부에서만 사용 가능
console.log(lunch('양꼬치', '양갈비', '소갈비', '채끝살'));     // 파라미터보다 많은 인자는 무시됨


function dance(danceName) {
    // 인자 검증
    // 1. 인자를 1개만 받아야 해
    // 2. 1개의 인자는 문자열이어야 해
    // 3. 빈 문자열이면 안돼
    if (arguments.length > 1 || typeof danceName !== 'string' || danceName.length === 0) {
        throw new TypeError('잘못된 인자 딱 걸렸음!!!');
    }
    return `${danceName} 춤추기 시작💃🏻🕺🏻🤸🏻‍♀️`;
}

result = dance('꼭짓점댄스');
console.log(result);

 

  • arrow function (화살표 함수)
/* 화살표 함수 (arrow function) */

var message;

message = function() {
    return "Hello World👋🏻"
};
console.log("function", message());

// 화살표 함수 기본 형식
message = () => {
    return "Hello World👋🏻"
};
console.log("arrow function", message());

// 함수 수행 내용이 return 뿐이라면
// return 키워드 생략 가능 + 함수블럭(중괄호) 생략 가능
// 단, 파라미터가 없더라도 화살표 앞 ()는 생략 불가
message = () => "Hello World👋🏻"
console.log("arrow function without {}", message());

message = (str1, str2) => str1 + "Hello World👋🏻" + str2;
console.log("arrow function with params", message("파라미터 ", " 전달 완료!"))

// 하지만 파라미터가 단 1개인 경우에는 화살표 앞 ()도 생략 가능
message = str1 => str1 + "Hello World👋🏻";
console.log("arrow function with param", message("파라미터 "));

// 반환값이 object인 경우에는 return 키워드와 코드블럭{}을 생략하려면 ()로 감싸줘야 함
var createUser = (name, hobby) => ({name, hobby});
console.log(createUser('맹구', 100));

// 화살표 함수는 코드 수행 내용이 한 줄인 함수에 유용하며
// 고차 함수의 인자(콜백 함수)로 많이 활용됨
console.log(
    [1, 2, 3, 4, 5].map(function(val) {return val * 10})
);

console.log(
    [1, 2, 3, 4, 5].map(val => val * 10)
);

 

  • 화살표 함수의 특징
// 1. 화살표 함수는 this를 가지지 않는다.
var theater = {
    store: '영등포점',
    titles: ['어쩔수가없다', '극장판 체인소 맨', '극장판 귀멸의 칼날', '얼굴', 'F1 더 무비'],
    showMovieList() {
        this.titles.forEach(
            title => console.log(this.store + ":" + title)
            // function(title) {
            //     console.log(this.store + ":" + title);
            // }
        );
    }
};

theater.showMovieList();

// 2. 화살표 함수는 new와 함께 호출할 수 없다.

// 3. 화살표 함수는 super를 가지지 않는다.

// 4. 화살표 함수는 arguments를 지원하지 않는다.
(function() {
    // var arrowFunc = function() {
    //     console.log(arguments);
    // }
    var arrowFunc = () => console.log(arguments);
    arrowFunc(3, 4);
}(1, 2));

 

  • 순수 함수 & 비순수 함수
/* 
    - 순수 함수: 외부 상태에 의존하지도 않고 외부 상태를 변경하지도 않는 함수
    - 비순수 함수: 외부 상태에 의존하거나 외부 상태를 변경하는 함수
*/
var cnt = 0;

// 순수 함수
function increase(n) {
    return ++n;
}

increase(cnt);
console.log(cnt);   // 0

cnt = increase(cnt);
console.log(cnt);   // 1

// 비순수 함수
function decrease() {
    return --cnt;
}

decrease();
console.log(cnt);   // 0

cnt = decrease();   // 재할당이 불필요 (함수 호출만 해도 같은 결과)
console.log(cnt);   // -1

 

  • 일급 객체
/* 
    1. 무명의 리터럴로 생성할 수 있다.
    2. 변수나 자료구조(객체, 배열 등)에 저장할 수 있다.
    3. 함수의 매개변수로 전달할 수 있다.
    4. 함수의 반환 값으로 사용할 수 있다.
*/

function repeat(func, count) {
    for(var i = 0; i < count; i++) {
        console.log(func());
    }
    return function() {
        console.log(`${count}번 반복 완료! 명심하셨으리라 생각합니다 ^_^!`)
    }
}

var cheerup = function() {
    return `JavaScript 열심히 공부해야 되겠죠 ^^?`;
};

var returnFunc = repeat(cheerup, 10);
returnFunc();

 

 

 

scope

 

  • 함수 레벨 스코프
/* 
    var 키워드로 선언된 변수는 오로지 함수의 코드 블록만을
    지역 스코프로 인정하는 함수 레벨 스코프를 가진다.
*/

var i = 0;

for(var i = 0; i < 10; i++) {
    console.log(`${i}번째 실행중~~~`);
}

console.log(i);     // 10

 

  • var
// 1. 변수 중복 선언 허용
var msg = "열심히 공부해 봅시다!";
console.log(msg);

var msg = "곧 최종 프로젝트니까요!";
console.log(msg);

// 초기화문이 없는 변수 선언문은 중복 선언하면 무시됨
var msg;
console.log(msg);   // 곧 최종 프로젝트니까요!

// 2. 함수 레벨 스코프 (함수 코드 블록만 지역 스코프 인정)

// 3. 변수 호이스팅
console.log(test);  // undefined
test = "테스트 출력";
console.log(test);  // 테스트 출력
var test;

 

  • let
// 1. 변수 중복 선언 금지
// let, const 키워드로 선언된 변수는 동일 스코프 내 중복 선언 불가
// 단, let 키워드를 사용하면 재할당은 가능
let msg = 'JavaScript 공부는 참 즐거워요 ^_^*';
console.log(msg);

// let msg = '어라 빨간 줄이 뜨죠...?';

msg = '열심히 공부할 맛이 납니다!';
console.log(msg);

// 2. 블럭 레벨 스코프
let i = 0;
for(let i = 0; i < 10; i++) {
    console.log(`${i}번째 실행중~~~`);
}
console.log(i); // 0

// 3. 변수 호이스팅
// let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작
// 선언은 되었지만 초기화가 되지 않아 참조 시 오류가 발생하는 것
console.log(test);
// let test;   // ReferenceError: Cannot access 'test' before initialization

 

  • const
/* 
    - const 키워드는 상수(constant) 선언을 위해 사용
    - let 키워드와 마찬가지로 블럭 레벨 스코프를 가짐
    - let 키워드와 마찬가지로 변수 호이스팅이 발생하지 않는 것처럼 동작
    (* 상수 == 재할당이 금지된 변수)
*/

// 1. 선언과 동시에 초기화해야 함
const TEST = 1;

// 2. const 키워드로 선언한 변수는 재할당 금지
// TEST = 100;     // TypeError: Assignment to constant variable.
console.log(TEST); // 1

// const 키워드로 선언한 변수에 '객체'를 할당한 경우
// property 값은 변경 가능 (단, 마찬가지로 재할당은 불가)
const STUDENT = {
    name: "SKN 17기",
    count: 28
}

STUDENT.name = "최종 프로젝트 때 최고의 결과를 보여줄 17기 ^^";
console.log(STUDENT);

STUDENT['studyTime'] = 960;
console.log(STUDENT);   // { name: '최종 프로젝트 때 최고의 결과를 보여줄 17기 ^^', count: 28, studyTime: 960 }

// STUDENT = {};        // TypeError: Assignment to constant variable.

 

 

Object constructor

 

  • Object 생성자 함수
const teacher = new Object();

teacher.name = '맹구';
teacher.height = 199;

console.log(teacher);   // { name: '맹구', height: 199 }

 

  • 생성자 함수 vs 일반 함수
function Student(name, hobby) {
    this.name = name;
    this.hobby = hobby;
    this.getInfo = function() {
        return `${this.name}의 취미는 ${this.hobby}입니다.`;
    };
}

// new 연산자와 함께 호출해야 함
// - new 연산자와 함꼐 호출해야 생성자 함수로써 동작
// - 그렇지 않으면 일반 함수로 동작
const student = Student('맹구', '야구보기');
console.log(student);
console.log(hobby);

const newStudent = new Student('짱구', '놀기');
console.log(newStudent);
console.log(hobby);


// new.target (ES6)
// - new 연산자와 함께 생성자 함수로써 호출 시, 함수 자기 자신
// - new 연산자 없이 일반 함수로써 호출 시, undefined
function Dog(name, skill) {

    console.log(new.target);
    if(!new.target) {
        return new Dog(name, skill);
    }

    this.name = name;
    this.skill = skill;
}

const dog = Dog('뽀삐', '손!');
console.log(dog);

const newDog = new Dog('삐뽀', '빵!');
console.log(newDog);


// 내적 생성자 함수는 new 연산자 없이 사용해도 빈 객체를 반환하는 방식으로
// 잘 동작하게 만들어짐
const obj = Object();
console.log(obj);

 

 

strict mode

 

  • 엄격 모드 (strict mode)
/* 
    전역 선두 또는 함수의 선두에 'use strict';를 작성해 사용
    (즉시 실행 함수 스크립트 단위로 적용하는 것이 바람직함)
*/

// 'use strict';

function test() {
    'use strict';
    x = 10;
}

test();

console.log(x);     // ReferenceError: x is not defined

 

  • strict mode 활용
// 1. 암묵적 전역 방지
(function(){
    // 'use strict';
    x = 1;
}());
console.log(x);


// 2. 매개변수 이름 중복 방지
(function(){
    // 'use strict';
    function test(x, x) {
        return x + x;
    }
    console.log(test(1, 2));
}());


// 3. 변수, 함수, 매개변수의 삭제 방지 (delete 키워드 사용 에러)
(function(){
    // 'use strict';
    var y = 1;
    delete y;
    console.log(y);
}());


// 4. with문 사용 방지
(function(){
    // 'use strict';
    with({ z : 1 }) {
        console.log(z);
    }
}());


// 5. 일반 함수에서의 this 사용 제한
// - 일반 함수로서 호출한 함수의 this == 전역 객체
// - strict mode에서 일반 함수로서 호출한 함수의 this == undefined
(function(){
    'use strict';
    function testThis() {
        console.log(this);
    }
    testThis();         // undefined
    new testThis();     // testThis {}
}());


// 6. arguments 객체
// strict mode에서는 매개변수에 전달된 인수를 재할당해 변경해도
// arguments 객체에는 반영되지 않음
(function(arg){
    'use strict';
    arg = 1;

    console.log(arg);           // 1
    console.log(arguments);     // [Arguments] { '0': 777 }
}(777));

 

 

array

 

  • 배열
// 1. 배열 리터럴
const arr  = ['다람쥐', '토끼', '호랑이'];
console.log(arr);


// 2. 배열 생성자 함수
const arr2 = new Array();
console.log(arr2);      // []

const arr2_1 = new Array(10);           // 10칸짜리 배열 (length 프로퍼티 지정)
const arr2_2 = new Array(10, 20, 30);   // 인자를 요소로 가지는 배열
const arr2_3 = new Array('javascript'); // 안저룰 요소로 가지는 배열

console.log(arr2_1);    // [ <10 empty items> ]
console.log(arr2_2);
console.log(arr2_3);


// 3. Array.of() 메서드
const arr3_1 = Array.of(10);
const arr3_2 = Array.of(10, 20, 30);
const arr3_3 = Array.of('javascript');

console.log(arr3_1);    // [ 10 ]
console.log(arr3_2);
console.log(arr3_3);

// 배열 == object
console.log(typeof arr);

/* 
    [ 프로퍼티 플래그 ]
    객체 프로퍼티는 값(value)과 함께 플래그(flag)라는 특별한 속성 세 가지를 가짐
    - writable: 수정 가능 여부 (true: 값 수정 가능 / false: 읽기만 가능)
    - enumerable: 반복문을 사용해 나열 가능한지 여부
    - configurable: 프로퍼티 삭제 및 플래그 수정 가능 여부
*/
console.log(Object.getOwnPropertyDescriptors(arr));

 

  • array property: length
const arr = [1, 2, 3, 4, 5];
console.log(arr.length);    // 5

arr.push(6);
console.log(arr.length);    // 6
arr.pop();
console.log(arr.length);    // 5

// length를 기존 길이보다 작게 조정하면 배열 요소도 조정됨
console.log(arr);           // [ 1, 2, 3, 4, 5 ]
arr.length = 3;
console.log(arr);           // [ 1, 2, 3 ]

// length를 기존 길이보다 늘리면 빈 공간이 추가됨
arr.length = 7;
console.log(arr);           // [ 1, 2, 3, <4 empty items> ]

// JS는 배열 요소 일부가 비어있는 배열을 문법적으로 인정함
// 문법적으로 인정 == length로 요소 개수를 셀 때도 카운트
// 단, 요소가 존재하는 것은 아니므로 property에는 없음
const arr2 = [1, , , , 100];;
console.log(arr2);          // [ 1, <3 empty items>, 100 ]
console.log(arr2.length);   // 5
console.log(Object.getOwnPropertyDescriptors(arr2));

 

  • JS 배열의 메서드
// indexOf, lastIndexOf, includes
const yongdonList = ['할머니', '할아버지', '큰삼촌', '할아버지', '작은숙모', '외삼촌', '할아버지'];
console.log('===== indexOf =====');
console.log(yongdonList.indexOf('할아버지'));       // 1
console.log(yongdonList.indexOf('할아버지', 2));    // 3
console.log(yongdonList.lastIndexOf('할아버지'));   // 6
console.log(yongdonList.includes('할머니'));        // true

// push, pop
console.log('===== push & pop =====');
console.log(yongdonList.push('엄마'), yongdonList);
console.log(yongdonList.push('아빠'), yongdonList);
console.log(yongdonList.pop(), yongdonList);
console.log(yongdonList.pop(), yongdonList);
console.log(yongdonList.pop(), yongdonList);
console.log(yongdonList.pop(), yongdonList);
console.log(yongdonList.pop(), yongdonList);        // 작은숙모 [ '할머니', '할아버지', '큰삼촌', '할아버지' ]

// shift, unshift
const chikenList = ['후라이드치킨', '양념치킨', '간장치킨'];
console.log('===== shift & unshift');
console.log('원래 치킨:', chikenList);
chikenList.unshift('파닭');
chikenList.unshift('마늘치킨');
chikenList.unshift('스노윙치킨', '불닭');
console.log('unshift 후:', chikenList);         // unshift 후: [ '스노윙치킨', '불닭', '마늘치킨', '파닭', '후라이드치킨', '양념치킨', '간장치킨' ]
console.log(chikenList.shift(), chikenList);
console.log(chikenList.shift(), chikenList);
console.log(chikenList.shift(), chikenList);
console.log(chikenList.shift(), chikenList);
console.log(chikenList.shift(), chikenList);    // 후라이드치킨 [ '양념치킨', '간장치킨' ]

// concat
// arr1.concat(arr2, ...);

// slice, splice
const frontEnd = ['HTML', 'CSS', 'JavaScript', 'ES6'];
console.log('===== slice & splice =====');
console.log(frontEnd.slice(1, 3));  // [ 'CSS', 'JavaScript' ]
console.log(frontEnd);              // [ 'HTML', 'CSS', 'JavaScript', 'ES6' ]
console.log(frontEnd.splice(3, 1, '자바스크립트'));     // [ 'ES6' ]
console.log(frontEnd);              // [ 'HTML', 'CSS', 'JavaScript', '자바스크립트' ]

// join
// 배열을 구분자로 결합하여 문자열로 반환
console.log(frontEnd.join('---'));       // HTML---CSS---JavaScript---자바스크립트

// reverse
// 배열의 순서를 뒤집음
console.log(frontEnd.reverse());        // [ '자바스크립트', 'JavaScript', 'CSS', 'HTML' ]

 

  • iterable & array-like
/*
    iterable: Symbol.iterator 메서드가 구현된 객체
    array-like(유사배열): 인덱스와 length 프로퍼티가 있어 배열처럼 보이는 객체

    - 이터러블이면서 유사배열일 수 있고
    - 이터러블 객체라고 해서 유사배열 객체는 아니며
    - 유사배열 객체라고 해서 이터러블 객체인 것도 아니다.
*/

// 유사배열 객체
let arrayLike = {
    0: '배열인듯',
    1: '배열아닌',
    2: '배열같은',
    3: '유사배열',
    length: 4
};
// console.log(arrayLike.pop());



// 이터러블 객체 (Symbol.iterator 구현)
let range = {
    from: 1,
    to: 5
};

range[Symbol.iterator] = function() {
    return {
        current: this.from,
        last: this.to,
        next() {
            if(this.current <= this.last) {
                return {done: false, value: this.current++};
            } else {
                return {done: true};
            }
        }
    };
};

console.log(range);

for(let item of range) {
    console.log(item);
}

// console.log(range.pop());



/*
    이터러블이나 유사배열을 Array.from()의 인자로 넘겨주면
    새로운 배열을 만들고 객체의 모든 요소를 새롭게 만든 배열에 복사한다.
*/
let arrLikeArr = Array.from(arrayLike);
console.log(arrLikeArr.pop());

let iterArr = Array.from(range);
console.log(iterArr.pop());

// 배열로 생성하기 전 적용할 함수 매핑 가능
iterArr = Array.from(range, num => num * num);
console.log(iterArr);