언어 정리/JavaScript

[JavaScript] JS_advanced_syntax 기본 예제

알 수 없는 사용자 2024. 8. 30. 16:05

https://ko.javascript.info/

 

모던 JavaScript 튜토리얼

 

ko.javascript.info

 

 


주요 내용(개인적임)

/**
 * 참고로 getter와 setter는 User.prototype에 정의됩니다.
 * class 의 constructor 생성자를 이용해 멤버변수를 초기화 할때 setter 함수코드를 기반으로 초기화합니다.
 */
 
 
 
 
 
 /**
 * This 함수와 Object, Class 
 * 함수 표현식, 함수 선언식, 화살표 함수
 * 이해하기
 * ----------------------------------------------------------------------------------------------------------------------------------------------------
 * 함수 선언 : 코드가 실행되기 전에 메모리에 로드
 * 함수표현식 : 런타임 중에 로딩
 * 화살표 함수 : 런타임 중에 로딩 + 렉시컬 스코프 적용
   화살표 함수는 정적 바인딩으로 this의 참조를 고정적으로 가져가며, this 가 선언된 스코프( { } )의 상위객체를 참조 하게 된다.
   이를 렉시컬 환경(스코프)라고 부른다.
   렉시컬 환경 정의 : "코드가 작성된 위치에 따라 변수와 상위 스코프를 결정하는 JS의 스코프 체계이다."
   ex) 예를 들면
       \ 전역 스코프에서 정의된 경우: 전역 스코프에서 화살표 함수를 정의하면, 그 this는 전역 객체(window 또는 global)를 참조합니다.
       \ 객체의 메서드로 정의된 경우: 객체의 메서드로 화살표 함수를 정의하면, 그 this는 해당 객체가 아닌, 그 객체가 정의된 상위 스코프의 this를 참조합니다.
       \ 클래스 내부에서 정의된 경우: 클래스 내부에서 화살표 함수를 정의하면, 그 this는 클래스 인스턴스를 참조합니다. 
       \                             이는 클래스의 메서드가 인스턴스의 컨텍스트에서 호출되기 때문입니다.
 */
 
 
 
 
 
 // setTimeout() 함수에 callback 등록시 해당 함수부분만 전달되므로 전역 컨텍스트(window)에서 호출하게 됨
 
 
 
 
 
// /**
//  * 
// |               | 동작 대상                          | 반환값                                           |
// |---------------|-----------------------------------|--------------------------------------------------|
// | typeof        | 원시형                            | 문자열                                           |
// | {}.toString   | 원시형, 내장 객체,                 | Symbol.toStringTag가 있는 객체 문자열             |
// | instanceof    | 객체                              | true나 false                                     |

// tip > {}.toString === Object.prototype.toString 이다.
// ex  > { }.toString.call([]) === Object.prototype.toString.call([])

// 숫자형 – [object Number]
// 불린형 – [object Boolean]
// null – [object Null]
// undefined – [object Undefined]
// 배열 – [object Array]
// 그외 – 커스터마이징 가능
//  */
 




let user = {
    [Symbol.toStringTag]: "User"
};
alert(Object.prototype.toString.call(user)); // [object User]

class MyClass {
    get [Symbol.toStringTag]() {
        return "MyClass <<<-- 유지보수 용이하게 가능";
    }
}
let instance = new MyClass();
alert(Object.prototype.toString.call(instance)); // [object MyClass <<<-- 유지보수 용이하게 가능]
 
 
 
 
 
// /**
//  * 에러 핸들링 try{ } - catch(e){ } - finally{ }
//  */

 

 


index.html

<!DOCTYPE HTML>
<html>

<body>

  <p>스크립트 전</p>

  <!-- <script>
    alert( 'Hello, world!' );
  </script> -->
  <script src="main.js"></script>
  <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
  <script src="https://www.naver.com"></script> -->
  <p>스크립트 후asdasd</p>

</body>

</html>

 

 


main.js

'use strict';

// class SimpleDeclareClass {
//     constructor(name) { this.name = name; }
//     sayHi() { alert(this.name); }
// }

// let SimpleExpressClass = class {
//     sayHi() {
//         alert("안녕하세요.");
//     }
// };


// function makeClass(phrase) {
//     // 클래스를 선언하고 이를 반환함
//     return class {
//         sayHi() {
//             alert(phrase);
//         };
//     };
// }

// // 새로운 클래스를 만듦
// let User1 = makeClass("안녕하세요.");

// new User1().sayHi(); // 안녕하세요.


// /**
//  * 참고로 getter와 setter는 User.prototype에 정의됩니다.
//  * class 의 constructor 생성자를 이용해 멤버변수를 초기화 할때 setter 함수코드를 기반으로 초기화합니다.
//  */
// class User {

//     constructor(name) {
//         // setter를 활성화합니다.
//         this.name = name;
//     }
//     get name() {
//         return this._name;
//     }

//     set name(value) {
//         if (value.length < 1) {
//             alert("이름이 너무 짧습니다.");
//             return;
//         }
//         this._name = value;
//     }
// }

// let user = new User("보라");
// alert(user.name); // 보라
// user.name = "디노";
// alert(user.name);

// let testConst = new User(""); // 이름이 너무 짧습니다.


/**
 * This 함수와 Object, Class 
 * 함수 표현식, 함수 선언식, 화살표 함수
 * 이해하기
 * ----------------------------------------------------------------------------------------------------------------------------------------------------
 * 함수 선언 : 코드가 실행되기 전에 메모리에 로드
 * 함수표현식 : 런타임 중에 로딩
 * 화살표 함수 : 런타임 중에 로딩 + 렉시컬 스코프 적용
   화살표 함수는 정적 바인딩으로 this의 참조를 고정적으로 가져가며, this 가 선언된 스코프( { } )의 상위객체를 참조 하게 된다.
   이를 렉시컬 환경(스코프)라고 부른다.
   렉시컬 환경 정의 : "코드가 작성된 위치에 따라 변수와 상위 스코프를 결정하는 JS의 스코프 체계이다."
   ex) 예를 들면
       \ 전역 스코프에서 정의된 경우: 전역 스코프에서 화살표 함수를 정의하면, 그 this는 전역 객체(window 또는 global)를 참조합니다.
       \ 객체의 메서드로 정의된 경우: 객체의 메서드로 화살표 함수를 정의하면, 그 this는 해당 객체가 아닌, 그 객체가 정의된 상위 스코프의 this를 참조합니다.
       \ 클래스 내부에서 정의된 경우: 클래스 내부에서 화살표 함수를 정의하면, 그 this는 클래스 인스턴스를 참조합니다. 
       \                             이는 클래스의 메서드가 인스턴스의 컨텍스트에서 호출되기 때문입니다.
 */

// let buttonFuncObj = {
//     value1 : "buttonFuncObj !",

//     // 함수 선언 : 코드가 실행되기 전에 메모리에 로드
//     clickDeclare() {
//         alert(this.value1);  // buttonFuncObj !
//     },

//     // 함수표현식 : 런타임 중에 로딩
//     clickExpress : function () {
//         alert(this.value1);  // buttonFuncObj !
//     },

//     // 화살표 함수 : 런타임 중에 로딩 + 렉시컬 스코프 적용
//     // 렉시컬 환경 : 현재 {} 스코프 상위의 this 를 정적 바인딩한다. -> 어느 상황이든 동일한 this를 지칭 하게 해줌
//     clickArrow : () => {
//         alert(this.value1);  // undefined
//     }
// }

// // 함수 선언식: 정의된 지점의 this (동적 바인딩)
// buttonFuncObj.clickDeclare();  // buttonFuncObj !
// // 함수 표현식: 정의된 지점의 this (동적 바인딩 )
// buttonFuncObj.clickExpress();  // buttonFuncObj !

// // 화살표 함수: 작성된 위치의 상위객체를 고정적으로 참조 this  (정적 바인딩 )
// buttonFuncObj.clickArrow();    // undefined
// // 아래의 코드랑 동일한거임
// alert(this.value1);            // undefined
// /**
//  * 화살표 함수만 제대로 안나오는 이유 :
//  * 함수에서 사용되는 중괄호"{ }"는 독립적인 스코프를 제공하지만, 
//  * 객체 리터럴의 중괄호"{ }"는 단순히 키-값 쌍의 집합이기 때문에 "buttonFuncObj.clickArrow()"함수의 상위 함수는 전역객체가 된다.
//  * 따라서 전역객체에 value1은 없으므로 "undefined"가 출력된다.
//  * 
//  * 객체 리터럴 정의 : 중괄호 {}를 사용하여 키-값 쌍의 집합을 정의하는 자바스크립트의 문법
//  */


// // --------------------------------------------------------------------------------------------------------------------

// class Button {
//     constructor(value) {
//         this.value = value;
//     }

//     // 함수 선언식
//     clickDeclare() {
//         alert(this.value);
//     }

//     // 함수표현식
//     clickExpress = function () {
//         alert(this.value);
//     }

//     // 화살표 함수
//     clickArrow = () => {
//         alert(this.value);
//     }
// }

// let button = new Button("안녕하세요.");

// // 함수 선언식: 정의된 지점의 this (동적 바인딩)
// button.clickDeclare();  // "안녕하세요."
// // 함수 선언식: 정의된 지점의 this (동적 바인딩)
// button.clickExpress();  // "안녕하세요."
// // 화살표 함수: 작성된 위치의 상위객체를 고정적으로 참조 this  (정적 바인딩 )
// button.clickArrow();    // "안녕하세요."

// // setTimeout() 함수 설명 : 
// // setTimeout(callback, 1000)의 콜백 함수는 기본적으로 전역 컨텍스트에서 호출

// // 함수 선언식: 정의된 지점의 this (동적 바인딩)
// setTimeout(button.clickDeclare, 500);  // undefined
// // 함수 선언식: 정의된 지점의 this (동적 바인딩)
// setTimeout(button.clickExpress, 1000); // undefined

// // 화살표 함수: 작성된 위치의 상위객체를 고정적으로 참조 this  (정적 바인딩 )
// setTimeout(button.clickArrow, 1500);   // "안녕하세요."
// /**
//  * 화살표 함수만 제대로 나오는 이유 : 
//  * setTimeout() 함수에 callback 등록시 해당 함수부분만 전달되므로 전역 컨텍스트에서 호출하게 된다. 그래서 함수 선언식,표현식 둘다 button을 this로 가리키지 못함
//  * 그러나 화살표 함수의 경우 정적 바인딩이므로 고정적으로 this 의 타겟을 참조 할 수 있음
//  * 또한 렉시컬 환경이 적용되므로 상위 객체를 this로 참조 하게 된다. [ 상위 객체를 this로 참조란 >> { }스코프 상위의 존재를 의미함 ]
//  * 따라서 화살표 함수에서의 this 는 상위객체인 button 인스턴스를 가리키게 되므로 어디서든 button인스턴스를 this로 가리킬 수 있다.
//  */


// class Animal {

//     constructor(name) {
//         this.speed = 0;
//         this.name = name;
//     }

//     run(speed) {
//         this.speed = speed;
//         alert(`${this.name}가 속도 ${this.speed}로 달립니다.`);
//     }

//     stop() {
//         this.speed = 0;
//         alert(`${this.name}가 멈췄습니다.`);
//     }

// }

// class Rabbit extends Animal {
//     constructor(name) {
//         this.speed = 0;
//         this.name = name;
//     }
//     hide() {
//         alert(`${this.name}가 숨었습니다!`);
//     }

//     stop() {
//         super.stop(); // 부모 클래스의 stop을 호출해 멈추고,
//         this.hide(); // 숨습니다.
//     }
// }

// let rabbit = new Rabbit("흰 토끼");

// rabbit.run(5); // 흰 토끼가 속도 5로 달립니다.
// rabbit.stop(); // 흰 토끼가 멈췄습니다. 흰 토끼가 숨었습니다!



// class Animal {

//     constructor(name) {
//         this.speed = 0;
//         this.name = name;
//     }
//     yesMother(){
//         alert("I am your mother");
//     }

//     // ...
// }

// class Rabbit extends Animal {

//     constructor(name, earLength) {
//         super(name);
//         this.earLength = earLength;
//     }
//     showMother(){
//         this.__proto__.yesMother();
//     }

//     // ...
// }

// // 이제 에러 없이 동작합니다.
// let rabbit = new Rabbit("흰 토끼", 10);
// alert(rabbit.name); // 흰 토끼
// alert(rabbit.earLength); // 10
// rabbit.showMother();



// let animal = {
//     name: "동물",
//     eat() {
//         alert(`${this.name} 이/가 먹이를 먹습니다.`);
//     }
// };

// let rabbit = {
//     __proto__: animal,
//     name: "토끼",
//     eat() {
//         // 예상대로라면 super.eat()이 동작해야 합니다.
//         this.__proto__.eat.call(this); // (*)
//         this.__proto__.eat();
//     }
// };

// rabbit.eat(); // 토끼 이/가 먹이를 먹습니다.



// class Article {
//     static varStatic = "클래스 변수";
//     constructor(title, date) {
//         this.title = title;
//         this.date = date;
//     }

//     static createTodays() {
//         // this는 Article입니다.
//         return new this("Today's digest", new Date());
//     }
//     static testStatic(){
//         return 1;
//     }
// }

// let article = Article.createTodays();

// alert(article.title); // Today's digest
// alert(Article.testStatic()); // 1
// alert(Article.varStatic);   // 클래스 변수
// alert(article.varStatic);   // undefined
// Article.varStatic = "클래스 변수 변경";
// alert(Article.varStatic);   // 클래스 변경

// // hasOwnProperty : 해당 클래스가 ( ) 안에 들어가 있는 프로퍼티가 있는지 확인
// alert(article.hasOwnProperty("title"));  // true



// class Animal {}
// class Rabbit extends Animal {}

// // 정적 메서드
// alert(Rabbit.__proto__ === Animal); // true

// // 일반 메서드
// alert(Rabbit.prototype.__proto__ === Animal.prototype); // true




// // private, protected 프로퍼티와 메서드 , 읽기 전용
// class CoffeeMachine {
//     _waterProtected = 0;    // '_' 가 붙으면 protected  // 상속시 사용가능      // 인스턴스 직접 접근 허용
//     #deviceSize = 0;        // '#' 가 붙으면 private    // 상속해도 사용 불가능 // 인스턴스 직접 접근 불가
//     _test = 0;

//     set waterAmount(value) {
//         if (value < 0) throw new Error("물의 양은 음수가 될 수 없습니다.");
//         this._waterAmount = value;
//     }

//     get waterAmount() {
//         return this._waterAmount;
//     }

//     constructor(power) {
//         this._power = power;
//     }
//     set setDeviceSize(ds) {
//         this.#deviceSize = ds;
//     }
//     get getDeviceSize(){
//         return this.#deviceSize
//     }
// }

// let coffeeMachine = new CoffeeMachine(220);
// coffeeMachine._waterAmount = 100;

// // 인스턴스 직접 접근 허용
// coffeeMachine._test = 999;
// alert(coffeeMachine._test);

// // // private -> 인스턴스 직접 접근 불가
// // coffeeMachine.#deviceSize = 10001;

// // private -> get set 접근은 허용
// coffeeMachine.setDeviceSize = 1000;
// alert(coffeeMachine.getDeviceSize);



// // 메서드 하나를 추가합니다(더 많이 추가하는 것도 가능).
// class CustomArray extends Array {
//     isLengthTwoOrFive() {
//         return (this.length === 5) || (this.length === 2);
//     }
// }

// let arr = new CustomArray(1, 2, 5, 10, 50);
// alert(arr.isLengthTwoOrFive()); // true

// let filteredArr = arr.filter(item => item >= 10);
// alert(filteredArr); // 10, 50
// alert(filteredArr.isLengthTwoOrFive()); // true
// alert("----------------------------------");
// // obj instanceof Class << 의미 : obj가 Class에 속하거나 Class를 상속받는 클래스에 속하면 true가 반환됩니다.
// // 따라서 객체(obj)가 특정 클래스(Class)의 인스턴스인지 확인하는 데 사용된다.
// alert( Array instanceof Object);        // true
// alert( CustomArray instanceof Array);   // false  // CustomArray는 클래스 자체이지, 인스턴스가 아니므로 instanceof 를 사용 할 수 없다.
// alert( Array instanceof CustomArray);   // false  // CustomArray는 클래스 자체이지, 인스턴스가 아니므로 instanceof 를 사용 할 수 없다.
// alert( CustomArray.__proto__ === Array);  // true  // CustomArray 의 부모인지 확인하려면
// alert("----------------------------------");
// alert( arr.__proto__ === CustomArray.prototype )  // true
// alert( arr.__proto__.__proto__ === Array.prototype ) // true
// alert(Object.getPrototypeOf(arr) === CustomArray.prototype); // true
// alert(Object.getPrototypeOf(CustomArray.prototype) === Array.prototype); // true
// alert( arr instanceof CustomArray);     // true
// alert( arr instanceof Array);           // true
// alert( arr instanceof Object);          // true


// /**
//  * 
// |               | 동작 대상                          | 반환값                                           |
// |---------------|-----------------------------------|--------------------------------------------------|
// | typeof        | 원시형                            | 문자열                                           |
// | {}.toString   | 원시형, 내장 객체,                 | Symbol.toStringTag가 있는 객체 문자열             |
// | instanceof    | 객체                              | true나 false                                     |

// tip > {}.toString === Object.prototype.toString 이다.
// ex  > { }.toString.call([]) === Object.prototype.toString.call([])

// 숫자형 – [object Number]
// 불린형 – [object Boolean]
// null – [object Null]
// undefined – [object Undefined]
// 배열 – [object Array]
// 그외 – 커스터마이징 가능
//  */
// class Test { }
// let s = Object.prototype.toString;

// alert(s.call(123));   // [object Number]
// alert(Object.prototype.toString(123));   // [object object]
// alert(s.call(null));  // [object Null]
// alert(s.call(alert)); // [object Function]
// alert(Object.prototype.toString.call([]))   // [object Array]
// alert(Object.prototype.toString.call({}))   // [object Object]
// alert(Object.prototype.toString.call(Test)) // [object Function]


// alert("----------------------------------");
// // 특정 호스트 환경의 객체와 클래스에 구현된 toStringTag
// alert(window[Symbol.toStringTag]); // Window
// alert(XMLHttpRequest.prototype[Symbol.toStringTag]); // XMLHttpRequest

// alert(Object.prototype.toString.call(window)); // [object Window]
// alert(Object.prototype.toString.call(new XMLHttpRequest())); // [object XMLHttpRequest]
// // alert({}.toString.call(window)); // [object Window] 이렇게도 됨
// // alert({}.toString.call(new XMLHttpRequest())); // [object XMLHttpRequest] 이렇게도 됨

// alert("----------------------------------");
// let user = {
//     [Symbol.toStringTag]: "User"
// };
// alert(Object.prototype.toString.call(user)); // [object User]

// class MyClass {
//     get [Symbol.toStringTag]() {
//         return "MyClass <<<-- 유지보수 용이하게 가능";
//     }
// }
// let instance = new MyClass();
// alert(Object.prototype.toString.call(instance)); // [object MyClass <<<-- 유지보수 용이하게 가능]


// /**
//  * 에러 핸들링 try{ } - catch(e){ } - finally{ }
//  */
// let json = "{ bad json }";

// try {

//     let user = JSON.parse(json); // <-- 여기서 에러가 발생하므로
//     alert(user.name); // 이 코드는 동작하지 않습니다.

// } catch (e) {
//     // 에러가 발생하면 제어 흐름이 catch 문으로 넘어옵니다.
//     alert("데이터에 에러가 있어 재요청을 시도합니다.");
//     alert(e.name);
//     alert(e.message);
//     alert(e.stack); // ReferenceError: lalala is not defined at ... (호출 스택)

//     // 에러 전체를 보여줄 수도 있습니다.
//     // 이때, 에러 객체는 "name: message" 형태의 문자열로 변환됩니다.
//     alert(e); // ReferenceError: lalala is not defined
// }


// let json2 = '{ "age": 30 }'; // 불완전한 데이터

// try {
//     let user = JSON.parse(json2); // <-- 에러 없음
//     if (!user.name) {
//         throw new SyntaxError("불완전한 데이터: name 키 없음"); // (*)
//     }
//     alert(user.name);
// } catch (e) {
//     alert("JSON Error: " + e.message); // JSON Error: 불완전한 데이터: name 키 없음
// } finally {
//     let tmp = JSON.parse(json2);
//     tmp["name"] = null;
//     json2 = JSON.stringify(tmp);
// }
// alert(json2)


class ReadError extends Error {
    constructor(message, cause) {
        super(message);
        this.cause = cause;
        this.name = 'ReadError';
    }
}

class ValidationError extends Error { /*...*/ }
class PropertyRequiredError extends ValidationError { /* ... */ }

function validateUser(user) {
    if (!user.age) {
        throw new PropertyRequiredError("age");
    }

    if (!user.name) {
        throw new PropertyRequiredError("name");
    }
}

function readUser(json) {
    let user;

    try {
        user = JSON.parse(json);
    } catch (err) {
        if (err instanceof SyntaxError) {
            throw new ReadError("Syntax Error", err);
        } else {
            throw err;
        }
    }

    try {
        validateUser(user);
    } catch (err) {
        if (err instanceof ValidationError) {
            throw new ReadError("Validation Error", err);
        } else {
            throw err;
        }
    }

}

try {
    // let jsonMsg = '{"age": 18, "name" : "john"}'  // 오케이
    // let jsonMsg = '{"age": 18, "name" : ""}'  // Original error: Error: name
    let jsonMsg = '{"age": 18}'  // Original error: Error: name
    // let jsonMsg = '{형식이 아예 틀리면}'  // Original error: SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
    readUser(jsonMsg);
} catch (e) {
    if (e instanceof ReadError) {
        alert(e);
        // Original error: SyntaxError: Unexpected token b in JSON at position 1
        alert("Original error: " + e.cause);
    } else {
        throw e;
    }
}