728x90

이 글은 [모던 JavaScript 튜토리얼]을 공부하며 정리한 글입니다.

 

 

👉 기본 환경

- Language: JavaScript

- IDE: VS code


1. 원시값의 메서드

* 원시값

    - 문자(string), 숫자(number), bigint, 불린(boolean), 심볼(symbol), null, undefined형 값

    - String / Number / Boolean: 인수를 원하는 형의 원시값(문자열, 숫자, 불린 값)으로 바꿔줌

* 객체

    - 프로퍼티에 다양한 종류의 값을 저장할 수 있음

    - 함수도 객체의 일종으로 프로퍼티로 저장할 수도 있음

 

1
2
3
4
5
let str = "Hello";
str.test = 5;
 
alert(str.test);
 
 

* use strict(엄격 모드)

- str의 프로퍼티에 접근할 때, 래퍼 객체가 만들어짐
- 래퍼 객체를 수정 시, 에러 발생
* 비 엄격 모드

- str의 프로퍼티에 접근할 때, 래퍼 객체가 만들어짐
- 래퍼 객체에 프로퍼티 test 추가

- 래퍼 객체 삭제

- 마지막 줄이 실행: 프로퍼티 test를 찾을 수 없음

 

 


2. 숫자형

 

* num.toString(base)

1
2
3
4
5
6
// base진법으로 num을 표현한 후, 이를 문자형으로 변환해 반환
let num = 255;
 
alert( num.toString(16) );  // ff
alert( num.toString(2) );   // 11111111
 
 

 

🚨 단, 숫자에서 직접 toString(base) 사용 시, 다음과 같이 작성해야 함

1
2
3
4
5
6
7
8
9
// 첫 번째 점 이후는 소수부로 인식되어 에러가 발생할 수 있음
123456.toString(36);
 
// 점 2개
123456..toString(36);
 
// 괄호
(123456).toString(36);
 
 

 

* 어림수 관련 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 버림
Math.floor(3.1); // 3
Math.floor(-1.6); // -2
 
// 올림
Math.ceil(3.1); // 4
Math.ceil(-1.6); // -1
 
// 반올림
Math.round(3.1); // 3
Math.round(-1.6); // -2
 
// 소수부 무시
Math.trunc(3.1); // 3
Math.trunc(-1.6); // -1
 
// 소수 2째자리까지 표시
Math.floor(1.2345 * 100/ 100// 1.23
 
// 반올림 + ( number -> String )
(1.2345).toFixed(2); // 1.23
// 숫자로 반환하고자 할 경우,
+(1.2345).toFixed(2); // 1.23
Number((1.2345).toFixed(2)); // 1.23
 
 

 

* isNaN과 isFinite

* Infinity / -Infinity: 그 어떤 숫자보다 큰 혹은 작은 특수 숫자 값
* NaN: Not a Number

1
2
3
4
5
6
7
8
9
10
11
12
// isNaN(value): 인수를 숫자로 변환한 다음 NaN인지 테스트
isNaN(NaN); // true
isNaN("str"); // true
 
 
// isFinite(value): 인수를 숫자로 변환, 변환 숫자가 NaN/Infinity/-Infinity가 아닌 일반 숫자인 경우 true 반환
isFinite("15"); // true
isFinite("str"); // false
isFinite(Infinity); // false
isFinite(""); // true
isFinite("    "); // true
 
 

 

🚨 빈 문자열이나 공백만 있는 문자열은 isFinite를 포함한 모든 숫자 관련 내장 함수에서 0으로 취급되므로 유의

 

* parseInt, parseFloat

    - 불가능할 때까지 문자열에서 숫자를 읽고, 숫자를 읽는 도중 오류가 발생하면 이미 수집된 숫자를 반환

1
2
3
4
5
6
7
8
9
console.log( parseInt('100px') ); // 100
console.log( parseFloat('12.5em') ); // 12.5
 
console.log( parseInt('12.3') ); // 12
console.log( parseFloat('12.3.4') ); // 12.3
 
// cf. parseInt(str, radix)
console.log( parseInt('ff'16) ); // 255, 0x가 없어도 동작
 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Sol1
let readNumber = function(){
  let input = prompt("enter a number");
  if(!isFinite(input)){
    readNumber();
  }
  else if(input === null || input === ''){
    return null;
  }
 
  return +input;
 
}
 
alert(`result:${readNumber()}`);
 
 
// Sol2
function readNumber() {
  let num;
 
  do {
    num = prompt("Enter a number please?"0);
  } while ( !isFinite(num) );
 
  if (num === null || num === ''return null;
 
  return +num;
}
 
alert(`Read: ${readNumber()}`);
 

* 재귀 호출의 경우, 무한 재귀 호출에 대한 잠재적인 스택 오버플로우 오류가 발생할 수 있으므로 do...while 반복문 권장

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Create a function randomInteger(min, max) that generates a random integer number from min to max including both min and max as possible values.
// Any number from the interval min..max must appear with the same probability.
 
 
// wrong
function randomInteger(min, max) {
  let rand = min + Math.random() * (max - min);
  return Math.round(rand);
}
 
alert( randomInteger(13) );
 
/*
values from 1    ... to 1.4999999999  become 1
values from 1.5  ... to 2.4999999999  become 2
values from 2.5  ... to 2.9999999999  become 3
*/
 
 
// correct
function randomInteger(min, max) {
  // now rand is from  (min-0.5) to (max+0.5)
  let rand = min - 0.5 + Math.random() * (max - min + 1);
  return Math.round(rand);
}
 
alert( randomInteger(13) );
 
/*
values from 0.5  ... to 1.4999999999  become 1
values from 1.5  ... to 2.4999999999  become 2
values from 2.5  ... to 3.4999999999  become 3
*/
 

 

 


3. 문자열

 

 

 

 

 

 

 

728x90
728x90

이 글은 [모던 JavaScript 튜토리얼]을 공부하며 정리한 글입니다.

 

 

👉 기본 환경

- Language: JavaScript

- IDE: VS code


객체(Object)

- 중괄호 {…} 선언

    - 중괄호 안에 ‘키(key): 값(value)’ 쌍으로 구성된 프로퍼티(property)로 구성

    - 키(이름): 문자형, 값: 모든 자료형

        - 키 값에 예약어 사용 가능

        - 숫자 입력 시,

            - 자동으로 문자로 형변환

            - 자동으로 key가 오름차순 정렬(오름차순 정렬을 원하지 않을 경우에는 문자로 취급되도록 " " 등 추가)

 

* 객체 생성

1
2
3
let user = new Object(); // '객체 생성자' 
let user = {};  // '객체 리터럴' 
 
 

⭐ 객체 리터럴이 주로 사용됨

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
let user = {     
  name"John",  
  age: 30,
  "likes birds"true// 마지막 , 생략 가능
  // 띄어쓰기 시, "" 사용
};
 
 
// key값에 띄어쓰기가 없을 경우,
// set
user.isAdmin = true;
 
// get
user.isAdmin;
 
// delete
delete user.age;
 
 
// key값에 띄어쓰기가 있을 경우,
// set
user["likes birds"= true;
 
// get
alert(user["likes birds"]); // true
 
// delete
delete user["likes birds"];
 
 

 

⭐ Const 상수는 수정하지 못하지만, Const 객체의 property는 수정 가능

 

* 점 표기법과 대괄호 표기법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 점 표기법
user.likes birds = true // error
 
let user = {
  name"John",
  age: 30
};
 
let key = "name";
alert( user.key ) // undefined
 
 
// 대괄호 표기법
user["likes birds"= true;
 
let user = {
  name"John",
  age: 30
};
 
let key = prompt("사용자의 어떤 정보를 얻고 싶으신가요?""name");
 
// 변수로 접근
alert( user[key] ); // John
 
 

 

* 계산된 Property

- 객체를 만들 때 객체 리터럴 안의 프로퍼티 키가 대괄호로 둘러싸여 있는 경우

1
2
3
4
5
6
7
8
let fruit = prompt("어떤 과일을 구매하시겠습니까?""apple");
 
let bag = {
  [fruit]: 5// 변수 fruit에 프로퍼티 이름을 동적으로 할당
};
 
alert( bag.apple ); // fruit에 "apple" 할당 시, 5 출력
 
 

 

* 단축 Property

1
2
3
4
5
6
7
8
function makeUser(name, age) {
  return {
    name// name: name 과 같음
    age,  // age: age 와 같음
    // ...
  };
}
 
 

 

* 존재하는 Property인지 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// undefined
let user = {};
alert( user.noSuchProperty === undefined ); // '프로퍼티가 존재하지 않음'
 
let obj = {
  test: undefined
};
alert( obj.test === undefined ); // value: undefined와 존재하지 않는 undefined가 구분되지 않음
 
 
// in operator
let user = { name"John", age: 30 };
alert"age" in user ); // true
alert"blabla" in user ); // false
 
 

 

* for .. in 반복문

1
2
3
4
5
6
7
8
9
10
11
12
let user = {
  name"John",
  age: 30,
  isAdmin: true
};
 
for (let key in user) {
  alert( key );  // name, age, isAdmin
  // 키에 해당하는 값
  alert( user[key] ); // John, 30, true
}
 
 

 

 


2. 참조에 의한 객체 복사

 

* Object.assgin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Object.assign
let user = { name"John" };
 
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
 
Object.assign(user, permissions1, permissions2);
 
// user = { name: "John", canView: true, canEdit: true }
// permissions1과 permissions2의 프로퍼티를 user로 복사
 
 
// 목표 객체(user)에 동일한 이름을 가진 프로퍼티가 있는 경우
let user = { name"John" };
 
Object.assign(user, { name"Pete" });
 
alert(user.name);
// user = { name: "Pete" }
// 기존 값이 덮어씌워짐
 
 

🚨 Object.assign()은 중첩 객체 복사를 하지 못함

 

+ 중첩 객체 복사 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
let user = {
    name"John",
    sizes: {
        height: 182,
        width: 50
    }
};
 
 
// 1. lodash library
let deep = _.cloneDeep(user);
console.log(user.sizes === deep.sizes); // false
 
 
// 2. for .. in
let clone = {};
 
for (let key in user) {
    if(typeof user[key] == 'object'){
        clone[key] = Object.assign({}, user.sizes);
        continue;
    }
    clone[key] = user[key];
}
 
console.log(user.sizes === clone.sizes); // false
 
 

 

 


3. 가비지 컬렉션

- 객체는 도달 가능한 상태일 때 메모리에 남음

 

* 참조 복사

1
2
3
4
5
6
7
8
9
let user = {
    name"John"
};
 
let admin = user;
 
user = null;
console.log(admin.name);
 
 

🚨 user.name에 접근하여 직접 값을 변경하여 admin에도 반영이 되는 것과는 다른 개념

    - name: "John" 대신 null을입력한 것이 X

    - user의 화살표를 제거한 것

 

 


4. 메서드와 this

 

* 객체는 사용자(user), 주문(order) 등과 같이 실제 존재하는 개체(entity)를 표현하고자 할 때 생성

1
2
3
4
5
let user = {
  name"John",
  age: 30
};
 
 

 

* 객체의 프로퍼티에 함수를 할당(= 메서드)해 객체에게 행동할 수 있는 능력을 부여

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 표현식 1
user.sayHi = function() {
  alert("안녕하세요!");
};
 
user.sayHi(); // 안녕하세요!
 
 
// 표현식 2
// 함수 선언
function sayHi() {
  alert("안녕하세요!");
};
 
// 선언된 함수를 메서드로 등록
user.sayHi = sayHi;
 
user.sayHi(); // 안녕하세요!
 
 
// 표현식 3
user = {
  sayHi: function() {
    alert("Hello");
  }
};
 
user.sayHi(); // 안녕하세요!
 
 
// 표현식 4
user = {
  sayHi() { 
    alert("Hello");
  }
};
 
 

 

* this

- 현재 객체

- ‘점 앞의’ 객체가 무엇인가에 따라 ‘자유롭게’ 결정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let user = {
  name"John",
  age: 30,
 
  sayHi() {
    alert( user.name ); // Error: Cannot read property 'name' of null
  }
 
};
 
 
let admin = user;
user = null;
 
admin.sayHi();
// sayHi()가 user의 name을 참조하면서 오류 발생
 
 

🚨 this.name으로 선언 시, admin의 name을 참조하게 되므로 오류가 발생하지 않음

 

* 화살표 함수

- 자신만의 this를 가지지 않음

- 화살표 함수 안에서 this를 사용하면, 외부에서 this 값을 가져옴

1
2
3
4
5
6
7
8
9
10
let user = {
  firstName: "보라",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};
 
user.sayHi(); // 보라
 
 

 

* 객체 리터럴에서 'this' 사용하기

1
2
3
4
5
6
7
8
9
10
11
12
// Case1
function makeUser() {
  return {
    name"John",
    ref: this
  };
};


let user = makeUser();
 
alert( user.ref.name ); // Error: Cannot read property 'name' of undefined
 
 

⭐ Undefined Error 발생 이유

- 객체의 메서드로써 호출된 게 아니라 함수로써 호출되었기 때문

    - ref 속성은 함수 내에서 this로 설정되지 않고, 전역 객체를 참조

        - 일반적으로 브라우저 환경에서, 전역 객체는 window 객체
        - window.name은 브라우저 창의 이름을 나타내는 속성으로, 일반적으로 초기에는 빈 문자열로 설정

 

1
2
3
4
5
6
7
8
9
10
11
12
13
function makeUser() {
  return {
    name"John",
    ref() {
      return this;
    }
  };
};
 
let user = makeUser();
 
alert( user.ref().name ); // John
 
 

- ref 속성은 메서드로 정의되었으며, 함수 내부에서 this는 해당 메서드를 호출한 객체

 

⭐ 1번에서는 메서드로 정의되지 않은 ref이므로 this가 해당 메서드를 호출한 객체를 가르키게 되는게 아니라, 전역 객체를 가리키게 되고

⭐ 2번에서는 메서드로 정의된 ref에서 this는 해당 메서드를 호출한 객체를 가르키게 되므로 user 객체가 가르키는 객체가 됨

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let calculator = {
  sum() {
    return this.a + this.b;
  },
 
  mul() {
    return this.a * this.b;
  },
 
  read() {
    this.a = +prompt('첫 번째 값:'0);
    this.b = +prompt('두 번째 값:'0);
  }
};
 
calculator.read();
alert( calculator.sum() );
alert( calculator.mul() );
 
 

* this를 활용하여 변수를 선언하지 않고도 계산 수행 가능

* prompt에 + 선언

    - 사용자 입력 값을 숫자로 변환

 

⭐ 객체 리터럴 내에서 속성을 선언하고 값을 할당하지 않으면 JavaScript는 자동으로 해당 속성을 undefined로 초기화

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// * 일반
let ladder = {
  step: 0,
  up() {
    this.step++;
  },
  down() {
    this.step--;
  },
  showStep: function() { // 사다리에서 몇 번째 단에 올라와 있는지 보여줌
    alertthis.step );
  }
};
 
ladder.up();
ladder.up();
ladder.down();
ladder.showStep(); // 1
 
 
// * Chaining
let ladder = {
  step: 0,
  up() {
    this.step++;
    return this;
  },
  down() {
    this.step--;
    return this;
  },
  showStep() {
    alertthis.step );
    return this;
  }
}
 
ladder
  .up()
  .up()
  .down()
  .up()
  .down()
  .showStep(); // 1
 
 

⭐ 객체 반환을 통해서 메서드 체이닝

 

 


 

5. new 연산자와 생성자 함수

유사한 객체를 여러 개 만들 때 유용

 

*함수 이름의 첫 글자는 대문자로 시작

    - 일반 함수와 동일한 형태이지만, 구분을 위해 첫 글자를 대문자로 작성

*반드시 new 연산자를 붙여 실행

    - new와 함께 호출하면 내부에서 this가 암시적으로 만들어지고, 마지막엔 this가 반환

1
2
3
4
5
6
7
8
9
10
function User(name) {
  this.name = name;
  this.isAdmin = false;
}
 
let user = new User("보라");
 
alert(user.name); // 보라
alert(user.isAdmin); // false
 
 

- 빈 객체를 만들어 this에 할당

- 함수 본문

- this에 새로운 프로퍼티를 추가해 this를 수정
- this를 반환

1
2
3
4
5
let user = {
  name"보라",
  isAdmin: false
};
 
 

 

* 반환해야 할 것들은 모두 this에 저장되고, this는 자동으로 반환되기 때문에 반환문을 명시적으로 써 줄 필요가 없음

    - return 객체: this 대신 객체 반환

    - return 원시형: return문 무시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function BigUser() {
  this.name = "원숭이";
  return { name"고릴라" };  // <-- this가 아닌 새로운 객체 반환
}
 
alertnew BigUser().name ); // 고릴라
 
 
function SmallUser() {
  this.name = "원숭이";
  return 2// <-- this 반환
}
 
alertnew SmallUser().name ); // 원숭이
 
 

 

⭐ return문이 있는 생성자 함수는 거의 없음

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Calculator(){
    this.read = function(){
        this.a = +prompt('Enter a number.''0');
        this.b = +prompt('Enter a number.''0');
    },
    this.sum = function (){
        return this.a + this.b;
    },
    this.mul = function (){                
        return this.a * this.b;
    }
}
 
let calculator = new Calculator();
calculator.read();
 
alert("Sum=" + calculator.sum());
alert("Mul=" + calculator.mul());
 
 

* 생성자 함수 내부에서 this를 사용 시, 그 this는 새로 생성되는 객체 인스턴스를 가리킴

* 생성자 함수에서 메서드를 정의할 때 this.read = function() {}와 같이 작성하는 이유

- 생성된 각 객체 인스턴스가 자신만의 'read' 메서드를 가지게 됨

- 즉, 'read' 메서드는 해당 객체에 속한 프로퍼티가 되어 해당 객체의 컨텍스트(this)에서 접근하고 호출할 수 있습니다.

- 반대로, 만약 this 없이 그냥 function read() {}와 같이 작성한다면, 'read' 함수는 생성자 함수의 로컬 함수가 되어 외부에서 접근할 수 없음


* 따라서, 각각의 Calculator 인스턴스가 자신만의 'read', 'sum', 'mul' 메서드를 가지고 이들 메서드가 해당 인스턴스에 속한 데이터('a', 'b')에 접근할 수 있도록 하기 위해, 메서드 정의에 this.methodName = function() {} 형태로 작성해야 함

 

1
2
3
4
5
6
7
8
function Accumulator(startNum){
    this.number = startNum;
    this.read = function(){
        this.number += +prompt('Enter a number');
    }
    this.value = this.number;
}
 
 

* value가 startNum으로 고정된 이유

    - this.value = number는 객체 생성 시, 1번만 실행되므로 이미 설정된 value의 값은 변경되지 않음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Accumulator(startNum) {
    this.number = startNum;
    this.read = function () {
        this.number += +prompt('Enter a number');
    };
    this.value = function () {
        return this.number;
    };
}
 
let accumulator = new Accumulator(1); // 최초값: 1
 
accumulator.read(); // 사용자가 입력한 값을 더해줌
accumulator.read(); // 사용자가 입력한 값을 더해줌
 
alert(accumulator.value()); // 최초값과 사용자가 입력한 모든 값을 더해 출력함
 
 

* value를 메서드로 만들어 현재까지 누적된 값을 반환하게 할 수 있도록 함

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Accumulator(startNum) {
    this.value = startNum;
    this.read = function () {
        this.value += +prompt('Enter a number');
    };
}
 
let accumulator = new Accumulator(1); // 최초값: 1
 
accumulator.read(); // 사용자가 입력한 값을 더해줌
accumulator.read(); // 사용자가 입력한 값을 더해줌
 
alert(accumulator.value); // 최초값과 사용자가 입력한 모든 값을 더해 출력함
 
 

* value 변수를 직접 변경

 

 


6. 옵셔널 체이닝 '?.'

 

* Optional Chaining

- ?. ?.의 왼쪽 평가 대상이 undefined null이면 평가를 멈추고 undefined를 반환

⭐ ?.는 존재하지 않아도 괜찮은 대상에만 사용

    - 논리상 user는 반드시 있어야 하는데 address는 필수값이 아닐 경우, user.address?.street를 사용하는 것이 바람직

    - 꼭 있어야 하는 값인데 없는 경우에 ?.을 사용하면 프로그래밍 에러를 쉽게 찾을 수 없으므로 유의

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// key: 값
let user = null;
alert( user?.address ); // undefined
 
 
// key: 함수
let user1 = {
  admin() {
    alert("관리자 계정입니다.");
  }
}
 
let user2 = {};
 
user1.admin?.(); // 관리자 계정입니다.
user2.admin?.(); // 평가가 멈춤
 
 

 

🚨 ?.은 읽기나 삭제하기에는 사용할 수 있지만 쓰기에는 사용할 수 없음

1
2
3
4
5
6
7
8
9
10
11
12
// 검색
let user = {}; // 주소 정보가 없는 사용자
alert( user?.address?.street ); // undefined,
 
 
// 삭제
delete user?.name// user가 존재하면 user.name을 삭제
 
 
// 삽입
user?.name = "Violet"// undefined = "Violet"으로 SyntaxError 발생
 
 

 

 


7. 심볼형

* 유일한 식별자(unique identifier)를 만들고 싶을 때 사용

- 심볼형 값은 다른 자료형으로 암시적 형 변환(자동 형 변환)되지 않음

 

* 심볼 설명

1
2
3
// 심볼 id에는 "id"라는 설명 추가
let id = Symbol("id");
 
 

 

* 숨김 프로퍼티

    - 외부 코드에서 접근이 불가능하고 값도 덮어쓸 수 없는 프로퍼티

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 서드파티 코드에서 가져온 객체
let user = { name"John" };
 
user.id = "스크립트 id 값";
// 문자열 "id"를 사용해 식별자 생성
user.id = "제3 스크립트 id 값"
// 덮어쓰기되어 id 식별자가 무의미해짐
 
 
let id1 = Symbol("id");
// 심볼을 사용하면 서드파티 코드가 모르게 user에 식별자를 부여할 수 있음
user[id1] = "스크립트 id 값";
// 심볼은 유일성이 보장되므로 우리가 만든 식별자와 제3의 스크립트에서 만든 식별자의 이름이 동일하더라도 충돌하지 않음
let id2 = Symbol("id");
user[id2] = "제3 스크립트 id 값";
 
alert(id1 == id2); // false
 
 

 

*  심볼 배제와 포함

- for...in 반복문

- Object.keys()

- Object.assign()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let id = Symbol("id");
let user = {
  name"John",
  age: 30,
  [id]: 123
};
 
 
// for...in 반복문
for (let key in user) alert(key);
// name과 age만 출력되고, 심볼은 출력되지 않음
alert"직접 접근한 값: " + user[id] );
// 심볼로 직접 접근하면 작동
 
 
// Object.keys(user)
alert(Object.keys(user));
// name과 age만 출력되고, 심볼은 출력되지 않음
 
 
// Object.assign(user)
let clone = Object.assign({}, user);
 
alert( clone[id] ); // 123
// 키가 심볼인 프로퍼티를 배제하지 않고 객체 내 모든 프로퍼티를 복사
 
 

 

* 전역 심볼

- 전역 심볼 레지스트리 안에 있는 심볼은 전역 심볼

- 전역 심볼 레지스트리 안에 심볼을 만들고 해당 심볼에 접근하면, 이름이 같은 경우 항상 동일한 심볼을 반환

    - 조건에 맞는 심볼이 레지스트리 안에 없으면 새로운 심볼 Symbol(key)을 만들고 레지스트리 안에 저장

- 이름이 같은 심볼이 같은 개체를 가리키길 원하는 경우 사용

- 애플리케이션에서 광범위하게 사용해야 하는 심볼이라면 전역 심볼을 사용

1
2
3
4
5
6
7
8
9
// 전역 레지스트리에서 심볼을 검색
let id = Symbol.for("id");
// 심볼이 존재하지 않으면 새로운 심볼 생성
 
let idAgain = Symbol.for("id");
// 동일한 이름을 이용해 심볼을 다시 검색
 
alert( id === idAgain ); // true
 
 

 

* Symbol.for() && Symbol.keyFor()

1
2
3
4
5
6
7
8
// 이름을 이용해 심볼을 찾음
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
 
// 심볼을 이용해 이름을 얻음
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
 
 

🚨 검색 범위가 전역 심볼 레지스트리이기 때문에 전역 심볼이 아닌 심볼에는 사용할 수 없음

전역 심볼이 아닌 인자가 넘어오면 Symbol.keyFor undefined를 반환

 

전역 심볼이 아닌 모든 심볼은 description 프로퍼티 사용

일반 심볼에서 이름을 얻고 싶으면 description 프로퍼티 사용

1
2
3
4
5
6
7
8
9
let globalSymbol = Symbol.for("gloName");
let localSymbol = Symbol("locName");
 
alert(Symbol.keyFor(globalSymbol)); // gloName, 전역 심볼
alert(Symbol.keyFor(localSymbol)); // undefined, 전역 심볼이 아님
 
alert(localSymbol.description); // locName
alert(globalSymbol.description); // gloName
 
 

 

cf. 심볼 조회 메서드

내장 메서드 Object.getOwnPropertySymbols(obj)를 사용하면 모든 심볼을 볼 수 있고, 

메서드 Reflect.ownKeys(obj)는 심볼형 키를 포함한 객체의 모든 키를 반환

* 그러나, 대부분의 라이브러리, 내장 함수 등은 이런 메서드를 사용하지 않음

728x90
728x90

👉 기본 환경

- Language: JavaScript

- IDE: VS Code

 

 

⌨️ 코드

1
2
alert123456.toString(36) );
 
 

 

 

🖨️오류

1
2
An identifier or keyword cannot immediately follow a numeric literal.
 
 

 

 

📡 원인

JavaScript에서 숫자 리터럴 다음에는 식별자나 키워드가 나타나서는 안됨

    - JavaScript 파서가 숫자 리터럴과 그 다음에 오는 괄호 (를 잘못 해석

    -  JavaScript가 숫자 리터럴 다음에 바로 메소드를 호출하는 것을 이해하지 못하기 때문에 발생

 

 

📰 해결 방법

1
2
alert((123456).toString(36));
 
 

* 숫자 리터럴을 괄호로 감싸기

 

1
2
3
var num = 123456;
alert(num.toString(36));
 
 

* 변수에 할당한 후에 메소드를 호출

 

 

 

📚 참고 자료

 

숫자형

 

ko.javascript.info

 

728x90
728x90

👉 기본 환경

- Language: JavaScript

- IDE: VS Code

 

 

⌨️ 코드

1
2
3
4
5
let str = "Hello";
str.test = 5;
 
alert(str.test);
 
 

 

 

🖨️오류

1
2
TypeError: Cannot create property 'test' on string 'Hello'
 
 

 

 

📡 원인

* 엄격 모드

    - 래퍼 객체를 수정 시, 에러가 발생

* 비엄격 모드

    - 래퍼 객체에 properties 'test' 추가 → 래퍼 객체 삭제 → properties 'test'를 찾을 수 없음

 

🚨 원시값은 추가 데이터를 저장할 수 없음

 

 

📰 해결 방법

1
2
3
4
5
let str = new String("Hello"); // 문자열을 객체로 변환
str.test = 5;
 
alert(str.test);
 
 

String을 객체로 변환

 

⭐ 생성자를 통해 원하는 타입의 "래퍼 객체"를 직접 만들 수 있으나, 문자열은 일반적으로 기본 데이터 타입 권장

 

cf. String("Hello")

    - 인 수를 원하는 형의 원시값(문자열, 숫자, 불린 값)으로 바꿔주는 역할

 

 

 

📚 참고 자료

 

원시값의 메서드

 

ko.javascript.info

 

728x90
728x90

이 글은 [모던 JavaScript 튜토리얼]을 공부하며 정리한 글입니다.

 

 

👉 기본 환경

- Language: JavaScript

- IDE: VS code

 

 


1. Chrome으로 디버깅하기

 

- 왼쪽

    - 파일 탐색 영역

- 중앙

    - 코드 에디터 영역: 소스 코드 보기 및 편집

- 오른쪽

    - 디버깅 영역

 

* 중단점 설정

    - Breakpoints를 활용해 중단점 위치로 이동, 비활성화, 삭제 등 가능

    - + 버튼(또는 소스 코드 줄 번호에서 오른쪽 마우스 클릭 후, Add conditional breakpoint)을 통해 조건부 디버깅 가능

        - 표현식이 참인 경우에만 실행 중지

 

cf. 소스 코드 줄 번호에서 오른쪽 마우스 클릭 후, Continue to here

    - 중단점을 설정하기는 귀찮은데 해당 줄에서 실행을 재개하고 싶을 때 사용

1
2
3
4
5
6
7
8
function hello(name) {
  let phrase = `Hello, ${name}!`;
 
  debugger;  // <-- 여기서 실행이 멈춥니다.
 
  say(phrase);
}
 
 

* debugger 작성을 통한, breakpoint 설정

 

* F5를 통해 중단점 실행

 

* watch: +를 클릭해 원하는 표현식을 입력한 후 Enter를 누르면 중단 시점의 값 반환

 

* Call Stack: 실행 경로를 역순으로 표시

* Scope: 현재 정의된 모든 변수 출력

    - Local: 지역 변수

    - Global: 전역 변수

 

1. Resume: 스크립트 실행을 다시 시작 (F8)

2. Step over: 다음 명령어를 실행하되, 함수 안으로 들어가진 않음 (F10)

3. Step Into: 비동기 동작을 담당하는 코드로 진입하고, 필요하다면 비동기 동작이 완료될 때까지 대기 (F11)

4. Step Out: 실행 중인 함수의 실행이 끝날 때 까지 실행을 계속 (Shift + F11)

    - 내부 동작을 알고 싶지 않은 중첩 함수로 진입했거나 가능한 한 빨리 함수 실행을 끝내고 싶은 경우

5.  모든 중단점을 활성화/비활성화

 

 


5. 테스트 자동화와 Mocha

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
 
    <!-- Mocha CSS: mocha 결과 출력용 CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css">
    <!-- Mocha Code -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
    <script>
        mocha.setup('bdd'); // 기본 셋업
    </script>
 
    <!-- Chai Code -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
    <script>
        // assert 전역 설정
        let assert = chai.assert;
    </script>
</head>
 
<body>
 
    <script>
        function pow(x, n) {
            /* 코드를 여기에 작성합니다. */
 
            if (n < 0return NaN;
            if (Math.round(n) != n) return NaN;
 
            let result = 1;
            for (let i = 0; i < n; i++) {
                result *= x;
            }
            return result;
        }
    </script>
 
    <!-- 테스트(describe, it...)가 있는 스크립트 삽입 -->
    <script src="test.js"></script>
 
    <!-- 테스트 결과를 id가 "mocha"인 요소에 출력-->
    <div id="mocha"></div>
 
    <!-- 테스트를 실행 -->
    <script>
        mocha.run();
    </script>
 
</body>
 
</html>
 
 

Mocha를 활용한 Test 준비

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe("pow"function () {
  describe("x를 세 번 곱합니다."function () {
    function makeTest(x) {
      let expected = x * x * x;
      it(`${x}을/를 세 번 곱하면 ${expected}입니다.`, function () {
        assert.equal(pow(x, 3), expected);
      });
    }
 
    for (let x = 1; x <= 5; x++) {
      makeTest(x);
    }
  });
});
 
 

* 중첩 describe를 통한 테스트 그룹화

* describe("title", function() { ... })
    - title: 구현하고자 하는 기능에 대한 설명

    - function: title과 관련된 it 블럭 그룹화

* it("유스 케이스 설명", function() { ... })
    - it의 첫 번째 인수: 특정 유스 케이스에 대한 설명

    - it의 두 번째 인수: 유스 케이스 테스트 함수
* assert.equal(value1, value2)
    - value1(테스트 함수 결과값)과 value2(기대값)가 동일한지 비교

 

1
2
3
4
5
6
7
8
9
10
11
12
13
describe("test"function() {
 
  before(() => alert("테스트를 시작합니다 - 테스트가 시작되기 전"));
  after(() => alert("테스트를 종료합니다 - 테스트가 종료된 후"));
 
  beforeEach(() => alert("단일 테스트를 시작합니다 - 각 테스트 시작 전"));
  afterEach(() => alert("단일 테스트를 종료합니다 - 각 테스트 종료 후"));
 
  it('test 1', () => alert(1));
  it('test 2', () => alert(2));
 
});
 
 

* 초기화 용도로 사용

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe("pow"function() {
 
  // ...
 
  it("n이 음수일 때 결과는 NaN입니다."function() {
    assert.isNaN(pow(2-1));
  });
 
  it("n이 정수가 아닐 때 결과는 NaN입니다."function() {
    assert.isNaN(pow(21.5));
  });
 
});
 
 

* 예외적 상황에 대한 test code 추가

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe("주어진 숫자의 n 제곱"function() {
  it("5를 1 제곱하면 5"function() {
    assert.equal(pow(51), 5);
  });
 
  // Mocha는 only 블록만 실행
  it.only("5를 2 제곱하면 25"function() {
    assert.equal(pow(52), 25);
  });
 
  it("5를 3 제곱하면 125"function() {
    assert.equal(pow(53), 125);
  });
});
 
 

* test case는 it블럭으로 모두 구분하여 확인

* it.only

    - 해당 it 블럭만 수행

 

 

 

 

 

 

 

728x90