Web/Frontend

[Javascript] 변수형과 자료형

콧등치기국수 2021. 7. 22. 03:11

1. 지역변수, 전역변수

  • 전역변수 : window 내에서 어디에서든 사용가능
  • 지역변수 : 해당 함수에서만 사용 가능

(1) 함수 밖에서 var 변수명; 으로 선언한 변수는 전역변수이다.

(2) 함수 안에서 변수명; 으로 선언한 변수는 지역변수이다.

(3) 지역변수가 우선권을 가지기 때문에 지역변수가 출력된다.

(4) 전역변수 사용시 window.변수명 또는 this.변수명 으로 표현

<script>
	str = "전역변수";
        var str2 = "var 전역변수";   //(1)
    
        window.onload = function(){
    	
          var str = "지역변수"
          var str3 = "새로운 지역변수"
          str4 = "전역변수"           //(2)

          console.log("-----------------str호출------------------")
          console.log(str);          //지역  (3)
          console.log(this.str);     //전역  (4)
          console.log(window.str);   //전역

          console.log("-----------------str2호출------------------")
          console.log(str2);          //전역  --> 지역변수 안에 없어서 자동으로 전역변수에서 찾는다.
          console.log(this.str2);     //전역
          console.log(window.str2);   //전역

          console.log("-----------------str3호출------------------")
          console.log(str);          //지역변수
          console.log(this.str);     //undefined
          console.log(window.str);   //undefined

          console.log("-----------------str4호출------------------")
          console.log(str);          //전역
          console.log(this.str);     //전역
          console.log(window.str);   //전역
        } 
    
</script>

 

2. var, let, const

  • var : 선언된 위치 내부의 블록 전체에서 사용할 수 있는 범위의 변수 선언
           블록 내부에서 재선언하여도 새로운 변수가 생성되지 않는다.
  • let : 선언된 블록 영역만 사용할 수 있는 제한 범위 변수
          값 재할당 가능 
          재선언X
  • const : let과 같이 블록 범위에서만 사용할 수 있음
             상수의 값은 재할당을 통해 변경 X 
             재선언 X
    <script>
        function constTest(){
            var x = 1;
            const y = 3;

            if(true){
                var x = 2;      
                const y = 2;
                //y = 2;  --> 에러난다. -->위에 y랑 다른 블록이라서 적용 안됨!
                console.log("[var]내부에서 선언된 x : "+ x);    //2
                console.log("[const]내부에서 선언된 y : "+ y);  //2
            }

            //y=4;  --> const는 재할당 안됨! 
            //const y = 4; --> 재선언 안된다.
            console.log("[var]외부에서 선언된 x : "+ x);    //2  -->if문에서 새로 var 생성했는데도 1이 아닌 2가 나온다. 즉 var는 전체 영역에서 쓰는 변수
            console.log("[const]외부에서 선언된 y : "+ y);  //3
        }

<script>태그 내부의 const y와 if문 내부의  const y는 다른 변수이다.

따라서 if문에서 y를 const y = 2;로 바꿔줘도 if문 밖에서 출력해보면 "1"이 출력된다.

그리고 상수의 개념으로 받기 때문에 새로운 값으로 할당도 안된다.

 

let은 var와 달리 const의 제한범위와 같지만 x=3으로 값을 재할당할 수 있다.

 

 

3. 자료형

  • prototype
  • __proto__

자바스크립트는 느슨한 타입 (loosely typed) 언어, 혹은 동적 (dynamic) 언어이다. 그 말은, 변수의 타입을 미리 선언할 필요가 없다는 뜻이다. 타입은 프로그램이 처리되는 과정에서 자동으로 파악될 것이다. 또한 그 말은 같은 변수에 여러 타입의 값을 넣을 수 있다는 뜻이다.

즉 자바스크립트에서 자료형별로 변수타입이 지정하는 것이 아니라, 리터럴에 의해서 자료형이 결정된다.

 

1) 기본자료형(Primitive type) : immutable value

  • Boolean
  • Null 
    • Null타입은 딱 한가지 값, null을 가질 수 있다.
  • Undefined
    • 값을 할당하지 않은 변수는 undefined값을 가진다.
  • Number
    • 정수만을 표현하기 위한 특별한 자료형은 없다.
    • +Infinity, -Infinity, NaN(Not a Number)와 같은 값도 표현할 수 있다.
      • 42 / 0  --> Infinity    (0으로 나눴을 때) 
      • 42 / -0  --> Infinity   (-0으로 나눴을 때)
  • String
  • Symbol

 

2) 객체(Objects) : 주소를 참조하는 참조변수

  • Arrays
    • 배열은 Array.prototype을 상속받으므로 배열을 다룰 때 편한 indexOf와 push같은 함수를 사용할 수 있다.
  • Dates

 

3) typeof( ) : 피연산자의 자료형을 나타내는 문자열 반환

  • typeof(배열객체) -> object 반환되는데 이는 아래에서 확인 가능하다.
  • typeof(null) : object
    typeof(undefined) : undefined
  • null과 undefined 모두 값이 없는데 typeof를 하면 undefined는 내 예상대로 나오지만 null은 object가 나온다. 이는 typeof에서 null을 체크하지 못해 생기는 버그이다.
    그렇다면 좀더 정확하게  null을 얻을 수는 없을까? 그러기 위해서는 아래 4번을 참고해보자.
  • 객체변수인 user의 name속성을 가져오고 싶다면 어떻게 해야할까?
    area1.innerHTML += "객체 속성접근 : "user.name + "<br>" 이렇게 "변수명.속성명" 으로 알 수 있다.
    <button onclick="testType();">자료형 테스트</button>
    <br><br>
    <div id="area1" class="area"></div>
    <script>
        function testType(){
            //문자열 변수
            var name = "유재석";
            console.log(name);
            console.log(typeof(name));  //string

            //숫자 변수
            var age = 20;
            console.log(age);
            console.log(typeof(age));   //number

            //논리 변수
            var check = true;
            console.log(check);
            console.log(typeof(check)); //boolean

            //배열변수
            var hobby = ["축구","농구","야구"]
            console.log(hobby);          //__proto__ : array __proto__: object
            console.log(typeof(hobby));  //object 객체!

            //객체 변수
            var user ={
                name : "유재석",
                age : 20,
                id: "user01"
            }
            console.log(user);
            console.log(typeof(user));


            //함수변수
            var testFunction = function testFunction(num1, num2){
                var sum = num1+num2;
            }
            console.log(testFunction)
            console.log(typeof(testFunction));  //function


            function Person(name,first,second){
                this.name=name;
                this.first=first;
                this.second=second;
            }   
            console.log("Person"+Person);      //function
            console.log(typeof(Person));


            //정의되지 않은 변수
            var apple;
            console.log(apple);
            console.log(typeof(apple));         //undefined

            var area1 = document.getElementById("area1");
            area1.innerHTML += "문자열 변수 : "+ name + "<br>";
            area1.innerHTML += "숫자 변수 : "+ age + "<br>";
            area1.innerHTML += "논리 변수 : "+ check + "<br>";
            area1.innerHTML += "배열 : "+ hobby + "<br>";
            area1.innerHTML += "배열인덱스[0] : "+ hobby[0] + "<br>";
            area1.innerHTML += "객체 : "+ user + "<br>";
            area1.innerHTML += "객체 속성접근 : "+ user.name + "<br>";
            area1.innerHTML += "함수 : "+ testFunction + "<br>";
            area1.innerHTML += "정의되지않은 변수  : "+ apple + "<br>";
        }

위 콘솔 출력결과는 아래 코드의 결과이다. 

hobby의 __proto___와 user의 __proto__값을 볼 수 있다.

hobby는 array의 prtotype객체에서 공유프로퍼티를 받고있는데(__proto__ : Array(0)) 또 세모표시를 눌러서 안에를 확인해보면 array의 prtotype객체에서 공유프로퍼티는 __proto__:Object를 가지고 있다. 즉 프로토타입의 최상위에서 Object의 프로토타입객체에게 상속받는 것이다.

            //배열변수
            var hobby = ["축구","농구","야구"]
            console.log(hobby);          //__proto__ : array __proto__: object
            console.log(typeof(hobby));  //object 객체!

            //객체 변수
            var user ={
                name : "유재석",
                age : 20,
                id: "user01"
            }
            console.log(user);
            console.log(typeof(user));

 

4. Obejct.prototype.toString :  object 의 타입을 반환해주는 메소드

아래와 같이 Function.prototype.call이나 Function.prototype.apply을 같이 사용해줘야 한다.

toString만 써주면 될 것 같은데 왜 call을 사용해줘야 할까?

call은 전달받은 인자를 호출한 메소드내에서 참조하게끔 만들어 주는 역할을 하기 때문이다.

 

즉, Object.prototype.toString.call(hobby) 에서 call의 인자로 hobby를 넘기면 toString의 this는 hobby가 된다. 그래서 [object Array]를 반환할 수 있다.

    <h3>동적타입테스트</h3>
    <button onclick="testDynamicType();">실행확인</button>
    <script>
        function testDynamicType(){
            var testVariable =123;
            console.log(typeof(testVariable)+" : "+testVariable)

            testVariable ="안녕하세요";
            console.log(typeof(testVariable)+" : "+testVariable)

            console.log(getTypeTest(testVariable))  //string
            console.log(getTypeTest(null))          //***null***
            console.log(getTypeTest(undefined))     //undefined
            console.log(typeof(null));              //***object***  --> 둘다 값 없는데 object로 나옴
            console.log(typeof(undefined));         //undefined


            var hobby = ["축구", "농구", "야구"];
            console.log(hobby)           //배열           
            console.log(typeof(hobby));  //object
            console.log(getTypeTest(hobby));  //array

            console.log(hobby.__proto__) //array
            console.log(hobby.toString(hobby))                 //축구 농구 야구
            console.log(hobby.__proto__.toString.call(hobby))  //축구 농구 야구
            console.log(Array.prototype.toString.call(hobby))  //축구 농구 야구 
            console.log(hobby.__proto__.__proto__.toString.call(hobby))  //[object Array]
            console.log(Object.prototype.toString.call(hobby))           //[object Array]
            console.log(Object.prototype.toString.call(testVariable))    //[object String]

            //프로토타입 체인
        }

        //null명확히 체크하려면
        function getTypeTest(val){
            return Object.prototype.toString.call(val).slice(8,-1)
            //return Object.prototype.toString.call(val) -->이렇게하면 --> [Object String]
            //배열 - toString 재정의 되어있다.(자바에서처럼)
            //배열 - object에서 있던 tostring이 배열에서 재정의가 되어있어서 문자열로 받을 수 있다.
            //prototype : 상위에서 내려줘서 사용가능한 메소드 
            //_proto_: 상위메소드 보여줌
            //배열 최상위 -> object이라서 object 메소드 정의하지 않아도 사용할 수 있다.
        }
    </script>
  • hobby.__proto__.Array.prototype 모두 Array의 prototype객체를 가리킨다.
  • hobby에는 toString 메소드가 없지만 결과가 나오는 동일하게 나오는 이유는 자바스크립트 내부에서 해당 객체에 메소드가 없는 경우 prototype객체에 가서 찾기 때문이다. 
console.log(hobby.toString(hobby))                 //축구 농구 야구
console.log(hobby.__proto__.toString.call(hobby))  //축구 농구 야구
console.log(Array.prototype.toString.call(hobby))  //축구 농구 야구
    • 위와 비슷한데 아래는 해당객체의 내부 값이 아닌 자료형을 문자열로 반환해준다.
    •  toString() 메서드는 Object에서 비롯된 모든 객체에 상속된다. 그래서 이 메서드가 사용자 지정 개체에서 재정의되지 않으면 toString()은 "[object type]"을 반환한다. 여기서 type은 object type이다.
    • Object.prototype.toString.call( 에 null을 인자로 주면 [object null]이,
      Object.prototype.toString.call( 에 undefiend을 인자로 주면 [object undefined]가 출력된다.
      즉 위 3번에서 typeof의 한계를 해결했다고 볼 수 있다.
console.log(hobby.__proto__.__proto__.toString.call(hobby))  //[object Array]
console.log(Object.prototype.toString.call(hobby))           //[object Array]
console.log(Object.prototype.toString.call(testVariable))    //[object String]

console.log(getTypeTest(null))          //***null***
console.log(getTypeTest(undefined))     //undefined

배열인 hobby.toString()은 배열의 값을 가져오고, Object.prototype.toString은 왜 [object Array]를 반환하는가?

이는 자바에서처럼 toString이 hobby.__proto__가 가리키는 배열의 prototype객체가 obejct의 toString()메소드를 재정의했기때문이다. 재정의되지 않았다면 Object로 부터 상속받은 기본값을 반환하여 [object type]을 반환하게 된다.

 

 

5.  prototype 와 __proto__ 의 차이

  • 자바스크립트는 프로토타입 기반 언어이다. 그렇다면 prototype이란 무엇인가?
    • prototype : 다른 객체에 공유 프로퍼티(메서드 포함)을 제공하는 객체이다.
  • 자바스크립트의 모든 객체는 자신의 부모를 가리키는 참조링크인 prototype이 존재한다.
    • 객체는 자신의 prototype에 접근할 때 "__proto__"를 이용한다.
  • 자바스크립트의 모든 객체는 Object 객체의 프로토타입을 기반으로 확장되었기 때문에 연결의 끝은 항상 Object이다.

 

1) prototype 과 __proto__예시

자바스크립트에서 "함수"는 아주 독특하다. 자바스크립트에서 "함수"는 객체이다!

Person 함수 생성

함수는 객체이기 때문에 property(속성)을 가질 수 있다.

 

  • Person함수가 생성될때 Person의 prototype도 생성된다. 
    person의 prototype도 객체이므로 __proto__를 갖고있다!!
  • Person객체에 내부적으로 prototype이라는 property가 생기고 그 속성은 person의 prototype을 가리킨다.(Person.prototype)
  • person의 prototype도 constructor라는 property를 생성해서 Person객체를 가리킨다.
  • 서로간에 상호참조를 하고 있다.
  • var kim = new Person("kim",10,20) -> kim객체 생성하면 kim객체에는 "__proto__"라는 속성이 생성된다.
    "__proto__"속성은 kim이라는 객체를 생성한 person의 prototype을 가리킨다.
  • 즉 Person의 prototype에 접근하기
    1. Person.prototype 
    2. kim.__proto__

 

 

 

참고 및 출처

1. https://opentutorials.org/module/4047/24629

2. https://mygumi.tistory.com/312

3. https://medium.com/%EC%98%A4%EB%8A%98%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-object-object-%EA%B0%80-%EB%8C%80%EC%B2%B4-%EB%AD%98%EA%B9%8C-fe55b754e709

4. https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/toString