JavaScript fundamental (자바스크립트 기본)

2021. 12. 31. 11:44Frontend/Web programming

    목차
반응형

JavaScript?

JavaScript는 web browser에서 기존 HTML의 한계를 극복하고 다양한 기능을 프로그래밍 할 수 있도록 지원하고자 Netscape에서 개발한 언어 입니다. 최초 Mocha라는 이름으로 개발되었으며, 저변 확대를 위해 당시 가장 유명했던 언어인 Java의 이 름과 유사한 JavaScript라는 이름으로 변경 되었습니다. 지난 20년 동안 JavaScript의 문법은 파편화 되어 통일 성이 떨어졌으나, ECMAScript 표준 사양이 완성된 후 대부분의 browser에서 표준에 맞게 동작하게 되었습니다. 이로 인해 현재는 개발자들이 다양한 파편화된 지식들을 습득하면서 개발해야 하는 어려움이 사라졌습니다.

Hello JavaScript!

Browser에서 동작하는 JavaScript 코드는 아래와 같이 HTML code 내에 embedding 됩니다.

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
    <title>JavaScript code</title>
    <script type="text/javascript">
            document.write("<p>Hello World!</p>");
    </script>
  </head>
  <body>
    <noscript>
      <p>JavaScript is not supported by the browswer.</p>
    </noscript>
  </body>
</html>

자료형

hoisting

hoisting은 함수 실행 전 함수 내 필요 변수값들을 모아 유효 범위의 최상단에 선언하는 것 입니다.
즉, 아래쪽에 존재하는 내용 중 필요한 값들을 끌어올립니다.

hoisting 대상

  • var 변수 선언
  • 함수 선언문
  • let, const의 경우 hoisting 대상에서 제외

var

function scope 입니다.
다음과 같은 경우 문제가 발생합니다.

var a = 1
var a = 2

연달아 선언 시 override 됩니다.

a = 1
var a

위 코드는 hoisting으로 reference error가 발생하지 않습니다.

hoisting 우선순위

  1. var
  2. 함수 선언

function hoisting 시 주의점

다음은 정상 case 입니다.

function print(val) {
    var inner = function() {
        return "a"
    }

    var res = inner()
    console.log(res)
}

print()    // parameter에 값을 주지 않아도 에러가 나지 않음

함수 표현식의 hoisting은 var 이후에 위치하게 됩니다. 즉, 아래의 경우 에러가 발생합니다.

function print(val) {
    var res = inner()            ← TypeError: inner is not a function

    var inner = function() {
        return "a"
    }

    console.log(res)
}

print()

let, const

block scope 입니다.
변수의 재선언이 불가능합니다. var 처럼 연달아 선언하면 에러가 발생합니다.
let, const의 차이는 immutable의 차이 입니다.

기본연산자

C++, JAVA와 거의 동일합니다.

삼항연산자

condition ? true_expr : false_expr

var alist = []
if (alist)    // true: C++/Java처럼 empty list를 객체로 판단합니다. 

기본 제어문

for 문

s = "abcde"
for (var i = 0; i < s.length; i++) {
   console.log(s[i])
}

toLowerCase

s = "The"
console.log(s.toLowerCase());     # the

labeling
for문에 label을 지정하여 break 시 해당 label의 for 문으로 빠져나갈 수 있음

lev1: for (var i = 0; i < mat.length; i++) {
    lev2: for (var j = 0; j < mat[0].length; j++) {
        break lev1
    }
}

foreach

list(array)에 대해서만 foreach를 사용할 수 있음 (ES6 부터 map, set도 지원)

var items = ['item1', 'item2', 'item3'];

items.forEach(function(item) {
    console.log(item);
});

// 출력 결과: item, item2, item3

for in

dictionary에 대해서만 사용할 수 있음 (ES6에 추가)

var obj = {
    a: 1, 
    b: 2, 
    c: 3
};

for (var prop in obj) {
    console.log(prop, obj[prop]); // a 1, b 2, c 3
}

for of

list(array)의 반복문 (ES6에 추가)
객체가 Symbol.iterator 속성을 가지고 있어야 사용 가능

var iterable = [10, 20, 30];

for (var value of iterable) {
  console.log(value);         // 10, 20, 30
}

Type

casting

var num = 777;
var n = num.toString()        // "777"

var val = "2"
alert(typeof val);        // string
val = Number(val)
alert(typeof val);        // number
val = parseInt("1")
alert(typeof val);        // number
val = parseFloat("1")        
alert(typeof val);        // number
parseInt("100mile");        // 100
parseInt("0x35");              // 53
parseInt("1101", 2);        // 13
parseInt("10", 8)              // 8
parseInt("05")            // 5
parseFloat("123.456")        // 123.456
parseFloat("w25")              // NaN
parseFloat("0x35")        // 0

char to ASCII

console.log('A'.charCodeAt(0))
ASCII to char
console.log(String.fromCharCode(65))

string

var s = "abcde"
s.charAt(0)        # a

split

var str = "123-456-789"
str.split('-')          //  [ '123', '456', '789' ]
var str = "123 456 789"
str.split(' ')          // [ '123', '456', '789' ]
var str = "abc"
str.split('')        // ['a','b','c']
str.substring(0, 3)    // 123  start index, end index (end index 포함)
str.substr(0, 3)        // 123  start index, length

to uppercase/lowercase

str.toUpperCase()
str.toLowerCase()
charAt(0).toUpperCase()

join

var words = ["aaa", "bbb", "ccc"]
words.join("")        // "aaabbbccc"
words.join(" ")        // "aaa bbb ccc"

> arr = [1, 2, 3]
> let num = arr.join('')
'123'

Math

min/max

var arr = [1, 2, 3]
Math.max(...arr)               // 3
Math.min(...arr)            // 1

pow

Math.pow(2, 7)            // 2^7

sqrt

Math.sqrt(4)            // 2

list

var arr = new Array();  // or var arr = [];
var arr2 = new Array("a", "b");
var arr3 = [1, 2, 3];
var arr4 = [];

for (var i = 0; i < arr3.length; i++) {
    document.write(arr3[i]);
}
var alist = [1, 2, 3]
alist[100] = 4                # length는 101임

deep copy

var vals = [1, 2, 3]
var nums = vals                            // shallow copy임 즉, reference

var seq = [...vals]                        // deep copy 임
var numbers = Object.assign([], vals)    // deep copy

some

Python의 any에 해당하나, 더 유연하게 사용 가능함
check 하는 함수를 지정해서 사용 가능

res = [false, true, false, false]
console.log(res.some((param) => {return param == true})) // true

every

Python의 all에 해당 역시 function을 지정할 수 있음

const isBelowThreshold = (currentValue) => currentValue < 40;

const array1 = [1, 30, 39, 29, 10, 13];
console.log(array1.every(isBelowThreshold));  // true

splice

splice로 삭제 혹은 추가 가능

splice(start_index, delete_count, item1, item2, ...)

start_index가 -1은 마지막 index

const nums = [1, 2, 4, 5]

특정 위치 값 추가

nums.splice(1, 0, 2)        // 1번 index에서 0개를 2로 교체→[1, 2, 2, 4, 5] 

nums.splice(2, 1, 3)        // 2번 index에서 1개를 3로 교체→[1, 2, 3, 4, 5]

특정 위치 값 삭제

nums.splice(4, 1)          // 4번 index에서 1개 제거 → [1,2,3,4]

nums.splice(2)        // 2번 index부터 모두 제거 → [1, 2]

값 제거

arr2.pop();            // 끝의 원소 제거하여 반환
arr2.shift();        // 첫 번째 원소 제거 하여 반환

2차원 배열

const arr = [[], []];
arr[0][0] = 1;
arr[0][1] = 2;
arr[1][0] = 3;
arr[1][1] = 4;
console.log(arr);    // [[1, 2], [3, 4]]

const arr = Array(Array(), Array());
...
const arr = Array(Array(), Array());
arr[0].push(1);
arr[0].push(2);
arr[1].push(3);
arr[1].push(4);

const arr = Array(Array(1, 2), Array(3, 4));

var arr = Array.from(Array(2), () => new Array(4));

Array.map

const arr = Array(4).fill(null).map(() => Array());
// [[], [], [], []]

arrow function

ES6에서 정의한 문법

기존 함수 정의

function func() {
}

함수를 변수에 저장
const func = () => {}

arguments
function func() {
    console.log(arguments)
}

위와 같이 argument가 없어도,
func(1, 2, 3);        // 1, 2, 3으로 호출 가능함

arrow function

const func = () => {
    console.log(arg);
}

func(1, 2, 3);        // 에러 (arg is not defined)

const func = (...arg) => {        // rest parameter
    console.log(arg);
}
() => { /* body */ }  // 인자가 없는 경우
a => {}             // 인자가 한 개인 경우
(a) => {}             // 인자가 한 개인 경우
(a, b) => {}         // 인자가 2개인 경우
a => { return a; }
a => a;                // 함수가 한 구문인 경우 (자동으로 a를 리턴)
a => ({key:a});        // dictionary 객체 리턴 시 ()가 필요
(a = defaultValue) => {}
([a, b] = [1, 2]) => a + b;
({key:val} = {key:3}) => val;
([a, b] = [1, 2], {key:val} = {key:a}) => { console.log(a, b, val); };      // 1 2 1

this

const obj = {
    a : () => { console.log(this) },        // Window
    b : function() { console.log(this) }     // {a:f, b:f}
}

arrow function scope에서의 'this'는 Window (전역 context)임
function scope에서의 'this'는 자신을 정의한 객체임

prototype

const obj = {a: 1};
Object.prototype.func = function() {
    console.log(this)
}

obj.func();    // {a:1}
const obj = {a: 1};
Object.prototype.func = () => {
    console.log(this)
}

obj.func();    // Window

empty check

if (arr.length == 0) {
    ...
}

sort

arr = [1, 30, 4, 21, 10000]
arr.sort() // [1, 10000, 21, 30, 4] ← ASCII 값으로 정렬됨

ascending order

arr.sort(function(a, b){return a - b}) // [1, 4, 21, 30, 10000]
arr.sort((a, b) => a - b)

descending order

arr.sort(function(a, b){return b - a}) // [10000, 30, 21, 4, 1]
arr.sort((a, b) => b - a)

속성 기준 정렬

값 기준 정렬

const items = [
    {name: 'Edward', value: 21},
    {name: 'Sharpe', value: 37},
    {name: 'And', value: 45},
    {name: 'The', value: -12},
    {name: 'Magnetic', value: 13},
    {name: 'Zeros', value:37}
]

items.sort(function(a, b) {
    if (a.value > b.value) {
        return 1
    }
    if (a.value < b.value) {
        return -1
    }
    return 0     // Equal
})

name 기준 정렬

items.sort(function(a, b) {
    if (a.name > b.name) {
        return 1
    }
    if (a.name < b.name) {
        return -1
    }
    return 0     // Equal
})

배열 내 list의 값의 차이로 정렬하

arr = [[259,770],[448,54],[926,667],[184,139],[840,118],[577,469]]
arr.sort((a, b) => {return (a[0] - a[1]) - (b[0] - b[1])})

배열 연산

concat

var arr1 = new Array("a", "b");
var arr2 = new Array("c", "d");

arr1 = arr1.concat(arr2);

reverse

reverse
arr.reverse();         // 뒤집음

slice

splice는 (시작, 갯수, 값) 이었으나, slice는 잘라내기 입니다.
slice의 인자는 [start index, stop index) 입니다.

var alist = [1, 2, 3, 4, 5]
console.log(alist.slice(1, 4))  // [ 2, 3, 4 ]

var astr = '12345'
console.log(astr.slice(1, 4))   // 234

dictionary

dictionary 초기화 및 조회

var dict = {};
dict['banana'] = '바나나';
dict['apple'] = '사과';

for (var key in dict) {
    console.log("key = " + key + " value = " + dict[key]);
}

delete dict["apple"];

Object.keys(dict);        // 모든 키만 list로 ["banana"]
Object.keys(dict).length;

key 유/무 확인

"apple" in dict        // false
"banana" in dict        // true

not in은 지원하지 않습니다.

dictionary의 sort

바로 지원하지 않아 일단 list로 convert 하고 sort 해야 함

key 값으로 정렬

var dict = {'a':1, 'b':2};

var kvs = []

for (var key in dict) {
    kvs.push([key, dict[key]]);
}

kvs.sort(function compare(item1, item2) {
    return item1[1] - item2[1];
});

dictionary의 empty 여부는 dictionary 객체의 length method로 파악 가능

Python tuple?

JavaScript에서는 python의 tuple을 지원하지 않습니다.
즉, (1, 2)을 dict의 key로 사용할 수 없습니다.
대신 list가 상수로서 dict의 key가 될 수 있습니다.

var adict = {}
adict[['e', 'a', 't']] = 7

adict        // { 'e,a,t': 7 }

class

JavaScript는 class와 class의 상속을 지원하지 않았습니다.
이에 ECMAScript 1~5까지 class와 유사한 기능을 지원하는 utility를 만들어 제공 했습니다.

이런 많은 class 지원 library는 class를 ECMAScript6에 포함되게 되었습니다.

prototype

ECMAScript5 이전 ver의 JavaScript에는 class가 없었습니다.
class에 가장 유사한 방법이 prototype에 method를 할당하는 것 이었습니다.
이는 "사용자 정의 type 생성" 이라고 불리는 접근법 입니다.

function PersonType(name) {   // 여기서는 name property만 생성
    this.name = name;
}

PersonType.prototype.sayName = function() {
    console.log(this.name);
}

// prototype은 모든 PersonType 객체가 공유함

let person = new PersonType("R")
person.sayName();

console.log(person instanceof PersonType)    // True
console.log(person instanceof Object)        // True

이 pattern이 JavaScript에서 class에 대한 모방의 기본입니다.
ECMAScript6에서는 정식으로 class keyword를 지원 합니다.

class 선언

class PersonClass {
    constructor(name) {
        this.name = name
    }

    sayName() {
        console.log(this.name);
    }
}

let person = new PersonClass("R")
person.sayName();

console.log(person instanceof PersonType)    // True
console.log(person instanceof Object)        // True

console.log(typeof PersonClass);                  // "function"
console.log(typeof PersonClass.prototype.sayName) // "function"

class 선언은 기존 타입 선언의 Syntactic sugar입니다.
PsersonClass 선언은 constructor method를 갖는 함수를 생성 합니다.

class 구문 사용 이유

  • class 선언은 함수 선언과 달리 hoisting을 지원하지 않음
    • let 선언과 같이 동작
  • class 선언의 모든 코드는 strict 모드로 자동 실행됨
  • 모든 method는 non-enumerable
  • class method의 name을 overwrite 할 수 없음
  • 모든 method 내부 [[construct]] method가 없고 new 호출 불가

위 class 코드는 아래 타입 선언 코드와 동일

let PersonType = (function() {
    "use strict";

    const PersonType = function(name) {
        if (typeof new.target == "undefined") {
            throw new Error("Constructor must be called with new");
        }
        this.name = name;
    }

    Object.defineProperty(PersonType.prototype, "sayName", {
        value: function() {
            if (typeof new.target !== "undefined") {
                throw new Error("Method cannot be called with new");
            }
        },
        enumerable: false,
        writable: true,
        configurable: true
    });

    return PersonType;
});

class 구문이 훨씬 단순하다는 것을 알 수 있습니다.
class 내부에서는 class name을 overwrite 할 수 없습니다.

기본 class 표현식

let Person = class {
    constructor(name) {
        this.name = name
    }

    sayName() {
        console.log(this.name)
    }
};

let person = new Person("A");
person.sayName();

console.log(person instanceof Person)        // True
console.log(person instanceof Object)        // True

console.log(typeof Person);                  // "function"
console.log(typeof Person.prototype.sayName) // "function"

named class 표현식

let Person = class PersonClass {
    constructor(name) {
        this.name = name
    }

    sayName() {
        console.log(this.name)
    }
};

let person = new Person("A");
person.sayName();

console.log(typeof Person);          // "function"
console.log(typeof PersonClass);     // "undefined"

위에서 PersonClass는 내부적으로만 사용됨

let PersonClass = (function() {
   "use strict";
   const PersonClass2 = function(name) {
   ...
   return PersonClass2;
}());

console

console.log
console.info
console.warn
console.error
console.assert(val === 1, 'val is not 1');
console.table(dog) ← object를 table로 출력함
console.dir(dog, {colors: false, depth: 1}) ← object의 nested를 1개 출력
console.time('measure1');
...
console.timeEnd('measure1');
var cnt = 1;
console.log('%{cnt} times');

console.count('...') ← 출력 될 때마다 출력 횟수를 표시

console.countReset('...');
console.trace();        // call frame을 출력함
반응형