회고록 블로그

[공부 필기] 생활코딩 JavaScript 입문 강의 필기 (3) 본문

2. 프로그래밍 언어 공부/JavaScript

[공부 필기] 생활코딩 JavaScript 입문 강의 필기 (3)

김간장 2021. 4. 18. 10:58

cinnamonc.tistory.com/168

 

[공부 필기] 생활코딩 JavaScript 입문 강의 필기 (2)

cinnamonc.tistory.com/162 자바스크" data-og-host="cinnamonc.tistory.com" data-og-source-url="https://cinnamonc.tistory.com/162" data-og-url="https://cinnamonc.tistory.com/162" data-og-image="https:/..

cinnamonc.tistory.com

위의 글에서 필기 내용을 정리하고 있었는데

글이 너무 길어지면서 스크롤 압박이 심해져서 한번 끊어서 필기 내용을 정리하려고 한다..

 

객체 지향 프로그래밍 관련부터의 수업 내용은 아래에 이어서 적었다.

참고로 필기하고 공부를 할 때 참고한 강의의 생활코딩이다.

출처 : opentutorials.org/course/743/6584

 

객체지향 - 생활코딩

객체지향 프로그래밍은 크고 견고한 프로그램을 만들기 위한 노력의 산물이다. 객체지향이라는 큰 흐름은 현대적 프로그래밍 언어들을 지배하고 있는 가장 중요한 맥락이라고 할 수 있다. 하지

opentutorials.org


객체 지향 프로그래밍(Object-Oriented Programming, OOP)

객체 :

(1) 상태(state)와 행위(behavior)를 카테고라이징한 것이 객체

     * 객체와 객체를 합해서 하나의 완제품을 만들어감 (레고 블럭과 비슷)

(2) 우리는 코드의 규모가 커지고, 복잡해지면서 사이트에 있는 로직들을 구분하고 싶어졌음

     * 연관된 코드들끼리 구분해서 놓고, 연관되지 않은 코드는 다른 디렉터리에 놓거나 등등 (그룹핑)

(3) 객체를 그룹핑하고 카테고라이징하다보니 재활용성이 향상됨

     * 사이트 내에서 댓글 기능을 사용하는 여러 페이지가 있다면 객체를 재활용해서 사용할 수 있음

(4) 즉, 객체지향 프로그래밍은 객체를 만드는 것이고 객체 안에는 그 기능/취지(본문, 댓글 등)과 연관되어 있는 변수와 메소드가 있음

      * 서로 연관되어 있는 변수, 메소드를 객체라는 그릇에 담고, 서로 연견되지 않은 변수, 메소드는 별도의 객체에 담아서 분리를 시키는 것이 가장 기본적인 형태

      * 문법적으로는 위와 같지만 설계도 잘 만들어진 객체는 쉽게 만들 수 없음..

 

문법과 설계 :

(1) 객체 지향 프로그래밍 교육은 문법을 익히는 것, 설계를 배우는 것이 있음

(2) 설계를 잘하는 것은 정말 어려운 일임

      * 설계는 현실의 문제를 잘 파악 및 반영해서 소프트웨어로 옮기는 것인데,

         현실은 굉장히 복잡하기도 하고 모든 부분이 다 필요한 것이 아니기 때문에 필요한 부분만 잘 추출해서 간단하게 만들어야 함

         => 현실의 문제를 소프트웨어로 단순화시켜서 만드는 것을 추상화(abstract)라고 함

      * 설계는 꼭 언젠간 배우기를 권장..

(3) 잘 만들어진 부품의 조건이 몇가지 있음

      * 메소드, 변수를 단순히 기능에 따라 그룹핑 하는 것 뿐만 아니라

      * 그 기능의 내부가 어떻게 만들어졌는지 모르는 사람에게 그 기능을 주고

         사용 방법만 알려주면 바로 사용할 수 있도록 해준 부품이 잘 만들어진 부품

          => 그 부품이 어떻게 만들어졌는지 모르지만 사용방법만 알면 사용할 수 있도록 하는 것이 은닉화, 캡슐화

      *  또한, 부품과 부품을 교환할 수 있도록 표준화된 규격에 맞춰 연결점을 잘 만드는 것도 있음

          => 마치 모니터와 컴퓨터를 연결하는 케이블이 표준화된 규격을 가지고 있어서 어떤 모니터로 바꿔도 문제가 없는 것처럼..

          => 그 연결점을 표준에 맞춰 만들면 되는데, 이 연결점을 인터페이스라고 함

 

문법: 생성자와 new

(0) 생성자 함수는 반드시 대문자로 시작해야함 

(1) 자바스크립트 계열에 속하는 언어들은 보통 prototype-based programming이라고 부름

(2) 객체 지향 프로그래밍의 문법을 비슷하게 갖고 있으면서 + 전통적인 함수영 언어의 특성을 갖고 있는 자바스크립트

      * 그래서 알면 알수록 뭔가 미궁에 빠질 수 있음

      * 자바스크립트는 자유로움 (C언어 등은 규제를 엄격하게 해서 오류를 적게 만들지만 자바스크립트는 좀 다름)

(3) 객체 생성 방법 (복습)

      * 중괄호 사용

// 객체 생성 첫번째 방법
var person = {} // 빈 객체
person.name = 'guest'; // 객체 안에 담긴 변수(여기에서는 name)는 속성(프로퍼티, property)
person.introduce = function(){ // 속성(property)에 담긴 함수 = 메소드
    return 'My name is '+this.name;
}
console.log(person.introduce());
// 객체 생성 두번째 방법
var person2 = {
    name : 'guest2',
    introduce : function(){ return 'My name is ' + this.name; }
}
...

(4) 객체 내의 중복되는 메소드가 있다면, 새로운 함수를 생성해서 해결할 수 있음

      * 자바스크립트는 객체 이전에 함수가 더 중요한 언어임 (함수 >> 객체)

         '함수'라는 것이 자바스크립트에서는 일꾼과 같은 존재와 비슷

var person1 = {
    name : 'guest',
    introduce : function(){ return 'My name is '+ this.name; }
}

var person2 = {
    name : 'student',
    introduce : function(){ return 'My name is '+ this.name; }
}

      * 아래와 같이 new를 이용해서 중복을 해결

      * 생성자 Person이 하는 일은 "객체를 초기화"(init, initialize)하는 것임

          => Person 생성자 함수가 어떠한 속성(property)을 가지고, 어떠한 메소드를 가지는지 적어서 세팅해줌 (=초기화)

function Person(name){ // 생성자 Person 정의
    this.name = name;
    this.introduce = function(){
        return 'My name is '+this.name;
    }
}
var person1 = new Person('guest'); // 생성자 Person 호출
console.log(person1.introduce());

var person2 = new Person('student'); // 생성자 Person 호출
console.log(person2.introduce());

      * 생성자 : 객체를 만드는 역할을 하는 함수

          => 문법적으로는 함수 앞에 new를 붙임

          => new [함수명]으로 함수를 호출하면 (아래 코드에서는 var p2 = new Person()이 여기에 해당)

                Person은 함수가 아니라 "생성자"가 됨

          => 여기에서 생성자는 "객체의 생성자"(객체를 생성함)를 말함

          => 아래의 코드는 비어있는 객체가 만들어진 것이라고 볼 수 있으며 코드에서 var p2 = new Person()는 var p = {}와 비슷하다고 할 수 있음

          => new를 붙인 것과 붙이지 않은 것의 차이는 아래 참고

                (new를 붙이지 않은 함수는 리턴값을 출력하지만, new를 붙인 함수(즉, 생성자)는 Person이라는 객체를 출력함)

첫번째 코드와 결과

 

함수에 new를 붙여서 객체를 만들었다고 할 수 있음(?)


전역객체(Global Object)

(1) 모든 객체는 '전역객체'의 속성(property)임

(2) e.g. window.func()는 무엇을 의미할까?

      * windows : 객체

      * func() : 메소드

      * = func()와 동일하게 동작함

(3) 원래 window를 붙여야 하지만, 생략해서 사용함

      * 암시적으로 window가 사용됨

(4) 코드 안에 있는 모든 함수, 변수는 전역변수로 선언했어도 사실 'window'라는 전역객체의 속성(property)

var o = { 'func' : function() { console.log('Hello?'); }}

o.func(); // 결과 Hello?
window.o.func(); // 결과 Hello?

(5) ECMAScript에서 표준으로 정의 해놓은 전역객체(window)의 API가 정의되어 있음

      * 전역 객체가 가지고 있는 API이며, decodeURI, encodeURI 등등 메소드가 있음

      * 개발자가 선언하고 정의하는 변수는 직접 전역 객체에 추가하는 것임

      * 참고로 Node.js에는 window가 아니라 global 이라고 하는 전역객체가 있음

 

this

(1) this : 함수 내에서 함수 호출 맥락을 의미

      * 맥락? 의미가 고정되지 않고 상황에 따라 의미가 가변적인 것이 맥락(context)

(2) 함수안에서 사용할 수 있는 일종의 변수

(3) 예제

      * 기본적으로 전역객체가 window 이므로 객체 만들지 않고 함수만 정의되어 호출하면 this는 window가 됨

      * 객체 obj를 정의한 후 메소드를 호출하면 this는 함수(메소드)가 속한 객체가 되므로 obj임

         => this는 "내가 속한 객체" (or 내가 생성할 인스턴스)를 가리키는 참조 변수

function CheckThis(){
    if(window === this){
        console.log("this === window");
    }
    else{
        console.log("this !== window");
    }
}

CheckThis(); // 결과 "this === window" 출력됨
var obj = {
    'CheckThis' : function(){
        if(obj === this){
            console.log("this === obj");
        }
        else{
            console.log("this !== obj");
        }
    }
}

obj.CheckThis(); // 결과 : "this === obj" 출력됨

 

(4) 생성자 안에서 this의 의미는?

    * 아래 코드에서 생성될(예정인 것) 객체인 o2를 가리킴

       => 생성자 new Func() 코드가 호출되어 초기화를 할 때에는 아직 o2 객체가 생성된 것이 아님

       => 초기화를 마친 후 그 결과가 o2 객체에 대입되는 것이므로

       => 따라서 Func() 생성자에 if(o2 === this) 라는 코드를 추가하면 "o2"가 이때에는 undefined 이므로 if문의 결과가 false가 됨

var funcThis = null;

function Func(){
  funcThis = this;
}

var o1 = Func(); // 생성자 아닌 함수
if(funcThis === window){
  console.log('window');
}
// 함수를 o1 변수에 대입한 것과는 별개로
// 함수는 전역객체(window)의 메소드이므로 window 출력

var o2 = new Func() // 생성자
if(funcThis === o2){
  console.log('o2');
}
// 생성자는 객체를 만드는 역할을 함
// 이때 this는 생성될 객체인 o2를 가리킴

 

(5) 함수의 메소드 apply, call

      * 함수(Function)는 객체(object)이기 때문에 메소드가 있음

      * 함수의 메소드인 apply 사용은 아래 예시 참고

         => apply와 call 메소드를 이용해서 this 값을 제어 가능

var o = {};
var p = {};

function func(){
    switch(this){
        case o:
            console.log('o 객체');
            break;
        case p:
            console.log('p 객체');
            break;
        case window:
            console.log('window 전역객체');
            break;
    }
}

func();
func.apply(o); // apply의 첫번째 인자는 thisarg(this의 인자)
func.apply(p);

 

상속(inheritance)

(1) 부모 객체의 로직/기능을 자식 객체가 물려 받는 것(동일하게 가지는 것) = 상속

(2) 상속 준비작업 - 예제

      * 지금까지 배운 내용으로는

         객체를 생성할 때 속성(property)을 세팅하는 방법으로 생성자를 사용했었음 (아래 코드에서 Person 생성자 함수 참고)

function Person(name){
    this.name = name;
    this.introduce = function(){
        return 'My name is ' + this.name;
    }
}

var p1 = new Person('guest');
console.log(p1.introduce());

      * 아래와 같은 방법으로 속성(property)을 세팅할수도 있음

         => 설명: Person이라는 생성자에 prototype 이라는 속성(property)이 있음

         => 이 prototype이라는 속성 안에는 객체가 값으로 들어있고,

         => 그 객체 안에 name이라는 속성(property)을 대입하는 코드가 Person.prototype.name = null 등등임

function Person(name){
    this.name = name;
    }
}

Person.prototype.name = null;
Person.prototype.introduce = function() {
    return 'My name is ' + this.name;
}

var p1 = new Person('guest');
console.log(p1.introduce());

      * 참고로 Person.prototype.name과 Person.prototype.introduce 는 상속을 위한 사전 작업(기본적인 준비작업)이기도 함

 

(3) 상속 코드 예제

      * Programmer라는 생성자는 Person이라는 생성자의 introduce 속성(property)을 상속 받았음

      * Programmer라는 생성자는 prototype라는 속성이 있고, 이곳에 new Person()을 해줌

         => Person 생성자에 의해서 객체가 생성됨

         => 이 객체를 생성할 때 1. 자바스크립트가 prototype이라는 속성을 생성자 함수 Person이 가지고 있는지 확인하고 2. 그 생성자 함수 안에 들어있는 객체(name, introduce 속성과 그 값/메소드들)와 똑같은 객체를 만들어서 3. 리턴해주고 4. "Programmer.prototype"에 대입됨

function Person(name){ // 2번. Person 생성자 함수의 prototype 있는지 확인
    this.name = name;
    }

Person.prototype.name = null; // 3번. prototype을 가져와서 리턴
Person.prototype.introduce = function() { // 3번. prototype을 가져와서 리턴
    return 'My name is ' + this.name;
}

function Programmer(name){
    this.name = name;
}

Programmer.prototype = new Person(); // 1번. 생성자 호출
									 // 4번. Programmer.prototype에 대입

var p1 = new Programmer('guest'); // 5번. 역시 1번~4번 작동 순서와 동일. Programmer의 prototype과 동일한 객체를 만들어서 p1에 대입
console.log(p1.introduce());

 

(4) 상속 코드 예제 - 물려받은 후 자식 객체에 새로운 값 추가하기

      * 부모 객체에는 필요없고, 자식 객체에만 추가가 필요한 속성이 있을 때 사용

...
Programmer.prototype = new Person(); // Person 생성자 함수의 prototype을 상속받음
Programmer.prototype.coding = function(){ // Programmer에만 있는 coding 속성 추가함
    return "Hello world";
}

function Designer(name){
    this.name = name;
}

Designer.prototype = new Person(); // Person 생성자 함수의 prototype을 상속받음
Designer.prototype.design = function(){ // Designer에만 있는 design 속성 추가함
    return "Design beautiful";
}

var p1 = new Programmer('guest');
console.log(p1.introduce());
console.log(p1.coding());

var p2 = new Designer('prof.');
console.log(p2.introduce());
console.log(p2.design());

 


※ 그렇다면 여기에서 등장하는 prototype이 무엇이길래 자꾸 쓰일까

     * prototype을 통해서 자바스크립트는 상속이라는 개념을 제공하고 있음

     * 아래 예제 참고

        => 상속 관계는 현재 "Sub ---(상속받음)----> Super -----(상속받음)-----> Ultra" (Ultra에는 ultraProp 있음)

        => o.ultraProp가 Sub라는 객체 안에 없음에도 불구하고 Sub 객체의 o를 통해 호출되도록 코드가 작성됨

              이때 Sub의 상속받은 부모 객체를 하나씩 확인하면서 ultraProp 값을 찾아서 출력해줌

function Ultra(){}
Ultra.prototype.ultraProp = true;

function Super(){}
Super.prototype = new Ultra();

function Sub(){}
Sub.prototype = new Super();

var o = new Sub();
console.log(o.ultraProp);

     * prototype은 이러한 코드가 가능토록 만듦

 

이를 이해하려면 생성자에 대해서 좀 더 알아야 할 것 같음

강의를 듣고 이해한 내용은 이러함..

 

생성자인 new [함수명]은 사실 "new 뒤에 있는 함수가 가지고 있는 메소드, 속성 등(원형)의 데이터를 그대로 가져와서 객체를 만드는 것"이었던 것임

그리고 그 메소드, 속성 등의 데이터는 하나하나 가져오는게 아니라 그걸 담고 있는 객체(이것이 prototype)를 하나 가져오는 것임

이 객체가 prototype 인 것 같음..

    * 참고1. 함수도 객체이기 때문에 prototype과 같은 속성(property)을 가질 수 있음

    * 참고2. 이 함수의 속성인 prototype은 객체이기 때문에 또 그 안에 속성과 값, 메소드 등을 담을 수 있음

       => 그렇기 때문에 위의 예제에서 Person.prototype.name 과 같이 코드를 작성할 수 있었던 것

       => 보통 prototype 객체는 비어있지만 Person.prototype.name과 같이 속성과 값(또는 메소드)을 추가해줄 수 있음

 

다음 강의에서 prototype에 대해서 설명을 해주니까 그 내용 필기 후 다시 정리해야할 것 같음..


(5) 상속의 장점은 부모 객체의 prototype을 수정하면 그 prototype을 상속받은 자식 객체들의 데이터도 일괄 수정 가능함

      * 아까 위의 코드에서 부모 객체인 Person의 prototype을 수정하면 자식 객체가 모두 영향을 받음

function Person(name){
    this.name = name;
    }

Person.prototype.name = null;
Person.prototype.introduce = function() {
    return 'My nickname is ' + this.name; // "My name is ~"를 "My nickname is ~"로 변경함
}
...

 

prototype

(1) 프로토타입(prototype)에 대하여 자세하게 알아보자

(2) 자바스크립트 객체 지향을 지탱하고 있는 '핵심적인 개념'이며, 자바스크립트와 다른 언어들과 '구분되는 개념'이기도 함

(3) prototype = 원형

(4) 아래와 같은 코드가 있음

      * Sub를 통해서 만들어진 객체 o1은 ultraProp를 가지고 있지 않지만,

         Sub의 부모인 Super -> Super의 부모인 Ultra 로 부터 상속받아서 ultraProp 값을 가져온 것

      * 이렇게 가능한 것은 prototype 덕분

function Ultra(){}
Ultra.prototype.ultraProp = true;

function Super(){}
Super.prototype = new Ultra();

function Sub(){}
Sub.prototype = new Super();

var o = new Sub();
console.log(o.ultraProp);

(5) 생성자인 "new [함수명]"은 사실 "new 뒤에 있는 함수명이 가지고 있는 메소드, 속성 등(원형)의 데이터와 동일하게 생긴 객체를 만드는 것"이었던 것으로 추측됨

       * 그리고 그 "메소드, 속성 등의 데이터"(빈 객체라면 메소드, 속성이 비어있을것)라고 하는 '원형'은 어딘가에 들어있는데 그게 바로 prototype 속성(property)

(6) prototype 속성에는 객체가 정의되어 있음

(7) 아래의 두 코드는 동일한 실행결과를 나타냄

function func() {}
func.prototype // 결과 func {} : 빈객체

new Object(); // 결과 Object {} : 빈객체

(8) 위와 같이 서로가 서로 연결되어 있는 관계를 prototype chain 이라고 함

(9) 이런 경우도 알아두어야 함

      * 첫번째 코드와 실행결과 : o1 객체의 ultraProp를 호출할 때 o1이 ultraProp를 가지고 있는지 먼저 확인하는데,

         o1.ultraProp = 1이라는 코드가 있기 때문에 결과가 1로 출력됨

      * 두번째 코드와 실행결과 : o1.ultraProp를 호출할 때 o1 객체에 ultraProp가 있는지 확인하는데,

         없기 때문에 o1 객체의 생성자를 알아내어서 그 생성자의 prototype을 확인하고 그 안에 있는 ultraProp를 출력함

      * 세번째 코드와 실행결과 : 두번째와 동일한 작동 방법

      * 네번째 코드와 실행결과 : 사진 참고..

      * 개인적으로 정리를 해보면, 처음에 호출할 때 ultraProp가 해당 객체 안에 있으면 거기에서 ultraProp 출력

         -> 없으면 상속받은 부모 객체(생성자)를 알아내어, 그 부모 객체의 prototype을 확인하고, 거기에 ultraProp가 있는지 확인 후 있으면 출력

         -> 또 없으면 그 위에 또 상속받은 부모 객체(생성자)를 알아내어서, 부모 객체의 prototype을 확인하고, 그 곳에 ultraProp가 있는지 확인 후 있으면 출력

        -> 없으면 또 반복

첫번재 코드와 실행결과
두번째 코드와 실행결과
세번째 코드와 실행결과
네번째 코드와 실행결과

※ 코드를 유의깊게 확인해야 함

      - ultraProp에는 숫자가 대입될 수 있지만

...
function Super(){}
Super.prototype = new Ultra();
Super.prototype.ultraProp = 2;

...

var o1 = new Sub();
o1.ultraProp = 1;

console.log(o1.ultraProp);

      - prototype에는 숫자가 대입되지 않았음

        * 아래 코드 중 "var s = new Super();"나 "Sub.prototype = s;" 코드가 없으면 제대로 결과가 출력되지 않음

        * 코드를 보면 알겠지만 prototype에는 "숫자"가 대입된 것이 없음

          "객체 s"처럼 객체가 들어가거나, "new Super()"처럼 부모 생성자가 들어갔었음

...

function Sub(){}		// Sub 생성자 정의함
var s = new Super();	// Super 생성자 함수로 만든 객체 s (Super와 그 위의 Ultra 생성자의 prototype을 객체 s가 상속받음)
s.ultraProp = 3;		// 그 객체 s는 ultraProp에 "3"을 대입하였음 
Sub.prototype = s;		// Sub 생성자의 prototype은 객체 s의 prototype을 가져와서 대입하였음

...

 

※ 잘못된 코드

      - 이런 경우 Sub의 prototype에 어떠한 값을 추가하면, Super의 prototype에 영향을 끼칠 수 있기 때문에 사용해서는 안됨

         * 자식에게 일어나는 일이 부모에도 반영될 수 있음

      - 부모 객체의 prototype을 그대로 대입하는 것이 아니라 부모 생성자(객체)와 똑같은 객체를 만들어서 (= new Super()에 해당)

         자식 객체에 대입하여야 함

      - Sub의 prototype에만 subProp를 추가하고 싶은데, 위에서 언급한 잘못된 코드를 사용하면?

추가하기 전 기본 코드
잘못된 코드로 인한 결과 (Sub prototype 뿐만 아니라 Super prototype에도 추가됨)
올바른 코드로 인한 실행 결과 (Sub prototype에만 추가가 됨)

 

※ 동작 원리를 알기 위해서 코드를 위와 같이 작성하였지만 사실 실용적인 예제는 아니라고 함..

 

※ 프로토타입(prototype)에 대하여 공부한 후에 아래 글을 읽으면 prototype에 대하여 더 잘 알 수 있음

medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67

 

[Javascript ] 프로토타입 이해하기

자바스크립트는 프로토타입 기반 언어라고 불립니다. 자바스크립트 개발을 하면 빠질 수 없는 것이 프로토타입인데요. 프로토타입이 거의 자바스크립트 그 자체이기때문에 이해하는 것이 어렵

medium.com

 

 

표준 내장 객체(Standard Built-in Object)의 확장

(1) 자바스크립트가 우리에게 기본적으로 제공하는 객체 = 표준 내장 객체

(2) 내장 객체는 이러함 (이것밖에 없음)

      - Object, Function, Array, String, Boolean, Number, Math, Date, RegExp

      * 언어 자체가 제공하는 내장 객체는 이게 전부이지만, 호스트환경(웹브라우저, Node.js 등)이 제공하는 API가 정말 많음

         => 그렇기 때문에 둘 다 알아야 함

(3) 표준 내장 객체 <-> 사용자 정의 객체

      * JavaScript의 표준 내장 객체를 이용해서 사용자가 객체를 정의해서 쓰는 경우가 사용자 정의 객체

(4) 필요에 따라 표준 내장 객체를 추가하고, 만들어서 쓸 수도 있음

      * 내장 객체에 기능을 추가해서 쓰는 방법

         => 여기에서도 prototype을 사용함

         => 아래 코드는 배열에 저장된 값 랜덤하게 출력하는 코드

var arr = new Array('seoul', 'new york', 'ladarkh', 'Tsukuba');
function getRandomValueFromArray(arr){
    var index = Math.floor(arr.length * Math.random());
    // Math.random : 0 ~ 1(0 이상 1 미만)까지의 랜덤한 숫자를 리턴함
    // Math.floor : 소수점 첫번째 자리에서 내림
    return arr[index];
}

console.log(getRandomValueFromArray(arr));

         => 이 코드를 Array 객체 안으로 추가

               ※ 함수로 작성할 때의 함수명은 어떤 기능을 하는 함수인지 명확하게 알리기 위해 복잡하게 적었지만, 객체에 들어가는 메소드명은 간결하게 기재

 

※ 코드 설명

     - 어떤 배열(arr)을 Array라는 생성자를 이용해 만들었을 때,

       Array라는 생성자는 '자신이 갖고 있는 속성 중 prototype(객체임)의 안에 있는 데이터'를 확인해서 객체를 만들기 때문에

       이 prototype에 random이라는 메소드를 추가한 것

     - 아래 코드에서 this는 Array를 가리킴

       => Array 라는 배열 생성자를 통해서 만들어진 객체가 random을 가지고 있으니 this는 Array가 갖고 있는 값(seoul, new york 등)을 가리킴       

// Array에 있는 prototype 속성(property)에 random 이라는 메소드를 추가함
Array.prototype.random = function(){
    var index = Math.floor(this.length * Math.random());
    return this[index];
}

var arr = new Array('seoul', 'new york', 'ladarkh', 'Tsukuba');
console.log(arr.random());

     - 이렇게 작성하면 random 이라는 메소드가 Array에 소속된 느낌이라, 코드를 보는 입장에서도 random이 배열과 관련됨을 알 수 있어서 가독성이 높아짐

     - 또한, 인자를 받지 않기 때문에 사용자가 신경 써야할 부분이 적다는 장점이 있음

 

Object

(1) Object : 아무것도 상속받지 않은 순수한 객체

      * 모든 객체가 가지고 있는 부모 객체임 (Object라는 내장 객체는 모든 객체들의 최초의 조상임)

      * Object의 prototype은 "모든 객체들"의 원형이 됨

         => 그렇기 때문에 Object의 prototype을 수정할 때 조심하기

(2) 또한 모든 객체가 공통적으로 가지고 있어야 하는 기능이 있다면 Object의 prototype에 기능을 추가하면 된다는 얘기이기도 함

(3) Object 라는 객체의 매뉴얼 보는법: MDN 홈페이지 

developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object

 

Object - JavaScript | MDN

ObjectNearly all objects in JavaScript are instances of Object; a typical object inherits properties (including methods) from Object.prototype, although these properties may be shadowed (a.k.a. overridden). However, an Object may be deliberately created fo

developer.mozilla.org

      * Object. 이 있고 Object.prototype. ~~~이 있음

      * Object.keys 와 Object.prototype.toString 을 예시로 사용

         => prototype.toString :

               Object라는 생성자 함수를 이용해서 객체를 만든 후 -> "객체명.toString()"과 같이 호출해서 사용

         => keys :

               prototype이 없는 Object.keys는 "Object.keys(인자)"의 형태로 호출해서 사용함

      * Object.keys()

          => 여기에서 Object는 생성자 함수.

          => 그리고 그 생성자 함수 Object는 아래와 같이 정의되어 있을 것임

                Object.keys = function(인자) { ~~~~~ };

var arr = ["a", "b", "c"];
console.log("Object.keys(arr) 실행 결과 : ", Object.keys(arr));

      * Object.prototype.toString()

          => 여기에서 Object도 생성자 함수.

          => 생성자 함수 안의 내용은 모르겠지만 그 생성자의 prototype은 아래와 같이 되어 있을 것임

                Object.prototype.toString = function() { ~~~~~~ };

          => prototype의 소속이라는 것은 "new Object();"를 실행시키는 순간에 어떠한 객체를 만들고,

                그 객체는 prototype이라는 속성에 저장된 객체를 원형으로 하는 객체를 만든다는 것

          => 그렇기 때문에 객체를 생성하고 그 객체에 대한 메소드로 사용함

var o = new Object();
console.log("o.toString 실행 결과 : ", o.toString());

      * Object는 모든 객체(배열, 함수 등등)의 부모 객체임

          => 그렇기 때문에 아래와 같이 Array로 만들어진 객체도 부모 객체의 prototype(객체임)에 있는 toString을 상속받아서 사용할 수 있음

var a = new Array(1, 2, 3);
console.log("a.toString 실행 결과 : ", a.toString());
var f = new Function();
console.log("f.toString 실행 결과 : ", f.toString());

(4) Object 객체를 확장해보기

      * 모든 객체에서 공통적으로 가져와서 쓸 기능 구현 위함

// contain 이라는 메소드 추가함
// 임의의 객체에 찾고자 하는 값(needle)이 있는지 확인하는 메소드
Object.prototype.contain = function(needle){
    for(var name in this){
        if(this[name] === needle){
            return true;
        }
    }
    return false;

}
var o = {'name': 'student', 'city':'seoul'};
console.log(o.contain('student1'));

var a = ['student', 'prof', 'guest'];
console.log(a.contain('student'));

      * 하지만 이러한 확장 방법이 권장되는 방법은 아님

         => 모든 객체에 영향을 주기 때문 (어떠한 위험이 있는지 알고 있는 상태로 굉장히 신중하게 해야함)

         => 아래의 코드 참고

               (만약 landmark라는 객체에 contain 메소드를 추가할 의도가 없었다고 가정한다면, 아래 코드는 위험한 코드가 됨)

...
var o = {'name': 'student', 'city':'seoul'};
// console.log(o.contain('student1'));

var a = ['student', 'prof', 'guest'];
// console.log(a.contain('student'));


var landmark = ['SeoulTowel', 'Deoksugung', 'Gyeongbokgung'];

for(var name in landmark) {
    console.log(landmark[name]);
}

실행결과

         => 그래도 이 코드를 사용할 수 있는 방법이 있기는 함

               (hasOwnProperty 사용)

         => hasOwnProperty는 Object의 메소드인데, 

               landmark라는 객체가 인자로 전달받은 값을 자신(landmark 객체)의 속성(property)으로 직접 갖고 있는지 체크함

         => contain 메소드는 부모로부터 상속받은 메소드이므로 hasOwnProperty 메소드 사용 시 출력되지 않음

...
var landmark = ['SeoulTowel', 'Deoksugung', 'Gyeongbokgung'];

for(var name in landmark) {
    if(landmark.hasOwnProperty(name))	// 코드 추가
    {
        console.log(landmark[name]);
    }
    
}

※ 프로그램을 만들 때, 만들고자 하는 기능의 대략적인 인상(?)을 가지고 코딩을 하면

      => 사용하기 불편한 API를 만들 수 있음

※ 따라서, 만들고자 하는 기능이 어떻게 사용될 것인가를 명확하게 설계하고 구현하는 것을 권고

※ 처음 배우는 기능은 기능의 원리를 먼저 공부하는 것도 좋지만, 기능이 어떻게(어디에) 사용되고, 어떤 취지의 기능인지 먼저 보고 그 다음에 기능의 원리를 공부하는 것도 좋을 수 있음 (이라고 조언을 주심)


데이터 타입

(1) 원시(기본) 데이터 타입 vs  객체(참조) 데이터 타입

      * 원시 데이터 타입(primitive type) : 숫자, 문자열, 불리언, null, undefined 

      * 객체 데이터 타입 : 위의 원시 데이터 타입에 해당하지 않는 모든 데이터

(2) 래퍼 객체(wrapper object)

      * 아래 예제

          => 문자열은 '원시 데이터 타입'이라고 하였으나, 객체처럼 메소드도 호출해옴..

          => 원시 데이터라고 했으면서 왜 객체처럼 쓰일까..

                - 그 이유는 온점(.)이 Object Access Operator 이기 때문

                - 온점(.) 앞에 있는 str은 객체임 (즉, 문자열은 객체가 됨)

var str = 'coding';
console.log(str.length); // 결과 6
console.log(str.charAt(0)); // 결과 "C"

※ 문자열은 '원시 데이터'라고 했는데 '객체'? 근데 객체는 아니라니?

      * 자바스크립트에서 문자열은 "원시 데이터 타입"이 맞기는 하지만

        그 문자열을 제어(문자열 길이 체크, 문자열 특정 값 추출 등)하기 위해서는, 문자열이 마치 객체 인 것처럼 동작해야함

      * 객체 지향 프로그래밍이기 때문에(?)

         => 객체 지향 프로그램인데 문자열 같은 것들은 예외로 해서 객체를 사용하지 않으면 객체 지향 프로그램이라고 할 수 있을까..

      * 자세한 내용은 아래 동작 원리 참고


      * 문자열이 객체가 아닌 원시 데이터 타입이라고 하는 이유는 아래 동작 원리를 이해해야 함

var str = 'coding';
console.log(str.length); // 결과 6
console.log(str.charAt(0)); // 결과 "C"

          => 첫째줄과 두번째줄 사이에      str = new String('coding');      이라는 코드가 있다고 생각해야 함

                (개인적으로 추측하기에 str.length를 하는 순간 String 객체가 만들어지는 것 같음)

          => 자바스크립트 내부적으로 String 객체를 만듦

          => 그리고 String 객체가 가지고 있는 메소드 중에는 length, charAt 등이 있음

          => 이 메소드를 호출해서 결과를 도출한 후

          => String 객체는 소리, 소문없이(?) 떠남(?)

var str = 'coding';
str.prop = 'everybody';
console.log(str.prop);

      * 위의 코드도 마찬가지

          => str.prop를 호출하는 순간 자바스크립트가 내부적으로 String 객체를 만듦

          => String 객체의 메소드인 prop 속성(property)에 'everybody'가 저장됨

          => 그리고 String 객체가 사라짐(제거됨)

          => 그 후 console.log(str.prop)를 호출하여도 prop라는 속성이 저장되었던 String 객체는 사라졌으니

                undefined 결과 도출됨

      * 이때 내부적으로 생성되는 String 객체를 래퍼객체(wrapper object)라고 함

          => 원시 데이터 타입을 감싸(wrap)주는 객체...

          => 래퍼객체는 아래와 같은 종류가 있음

                (숫자 - Number / 문자열 - String / 불리언 - Boolean / null과 undefined - 래퍼객체없음)

 

(3) 정리

      * 문자열, 숫자, Boolean은 객체처럼 사용할 수 있지만 사실은 원시 데이터 타입임

      * 객체처럼 사용할 수 있는 이유는 래퍼객체(wrapper object)로 감싸져 있기 때문

 

복제와 참조

(1) 복제

      * b는 a의 값을 복제해왔음

      * 이때 b의 값을 변경하여도 a에는 영향을 주지 않음

var a = 1;
var b = a; // b는 a를 복제해옴
b = 2;
console.log(a); // 결과 1
console.log(b); // 결과 2

      * 반대로 a의 값을 변경하여도 b는 영향을 받지 않음

var a = 1;
var b = a; // b는 a를 복제해옴
a = 3;
console.log(a); // 결과 3
console.log(b); // 결과 1

      * 이유

         => 변수 a는 '1'이라는 값을 담고 있음

         => 변수 b는 'a'가 가리키는 값 '1'을 카피해서 b에 담음 (a와 b는 서로 독립적임)

         => 이것을 '복제'라고 함

(2) 참조(reference)

      * b는 a의 값을 참조해왔음

      * 이때 b의 id 값을 변경하면 a의 id 값도 영향을 받음 

var a = {
  'id' : 1
} // 객체 데이터 타입
var b = a; 
b.id = 2;
console.log(a.id); // 결과 2
console.log(b.id); // 결과 2

      * 이유

         => 변수 a는 '1'이라는 값을 가리키고 있음

         => 변수 b도 변수 a가 참조하고 있는 '1'이라는 값을 가리키고 있음

         => 이것을 '참조'라고 함

(3) 아래와 같은 경우에는 "변수 b에게 {id:2} 라는 새로운 객체를 생성해서 할당해주었기 때문에" a와 별개로 움직임

var a = {'id' : 1};
var b = a;
b = {'id' : 2};
b.id = 3;
console.log(a.id); // 결과 1
console.log(b.id); // 결과 3

(4) 정리

      * 변수에 담겨있는 값이 원시 데이터 타입인 경우, 그 값을 다른 변수에 대입하면 '복제'를 하고

      * 변수에 담겨있는 값이 객체 데이터 타입인 경우, 그 값을 다른 변수에 대입하면 '참조'를 함

(5) 함수와 참조

       * 아래와 같은 코드는 매개변수 b에게 a의 값을 "복제"해서 전달하기 때문에 b의 값은 변하지만, a의 값은 변하지 않음

var a = 1; // 원시 데이터 타입
function func(b) {
    b = 2;
}

func(a);
console.log(a); // 결과 1

     * 아래의 코드를 보자

        => 매개변수 b에게 a의 값을 "참조"해서 전달했지만, 함수 내에서 b에게 새로운 객체를 생성하여 대입해주었기 때문에 a와 b 사이에 링크가 끊김

var a = {'id' : 1};
function func(b){
    b = {'id' : 2};
}

func(a);
console.log(a.id); // 결과 1

        => 이 코드는 사실 이렇게 동작함

        => 변수 a의 값은 {id: 1}임

        => 변수 b에 a의 값을 대입하였으므로 변수 a, b는 동일한 데이터를 가리키고 있음 (= 참조)

        => b의 값에 {id: 2}를 대입하면서 b는 {id:2}라는 새로운 객체를 할당받게 됨

        => 따라서, 변수 a와 b 사이의 링크가 끊기게 되고 a의 id 값은 변하지 않음

 

     * 아래의 코드를 보자

       => 이 코드는매개변수 b에게 a의 값을 "참조"해서 전달하고 b의 id 값을 2로 변경하고 있음

       => 이때 a의 값도 함께 변경이 됨

var a = {'id' : 1};
function func(b){
    b.id = 2;
}

func(a);
console.log(a.id); // 결과 2

        => 이 코드는 이렇게 동작함

        => 변수 a의 값은 {id: 1}임

        => 변수 b에 a의 값을 대입하였으므로 변수 a, b는 동일한 데이터를 가리키고 있음 (= 참조)

        => b의 id 값을 변경하였음 (a와 b는 같은 값을 참조하고 있기 때문에 a의 값도 변경됨)

        => 따라서, 변수 a와 b의 id 값이 변경됨

Comments